| ... | @@ -17,9 +17,13 @@ const refToIndex = Zir.refToIndex; | ... | @@ -17,9 +17,13 @@ const refToIndex = Zir.refToIndex; |
| 17 | const indexToRef = Zir.indexToRef; | 17 | const indexToRef = Zir.indexToRef; |
| 18 | const trace = @import("tracy.zig").trace; | 18 | const trace = @import("tracy.zig").trace; |
| 19 | const BuiltinFn = @import("BuiltinFn.zig"); | 19 | const BuiltinFn = @import("BuiltinFn.zig"); |
| | 20 | const AstRlAnnotate = @import("AstRlAnnotate.zig"); |
| 20 | | 21 | |
| 21 | gpa: Allocator, | 22 | gpa: Allocator, |
| 22 | tree: *const Ast, | 23 | tree: *const Ast, |
| | 24 | /// The set of nodes which, given the choice, must expose a result pointer to |
| | 25 | /// sub-expressions. See `AstRlAnnotate` for details. |
| | 26 | nodes_need_rl: *const AstRlAnnotate.RlNeededSet, |
| 23 | instructions: std.MultiArrayList(Zir.Inst) = .{}, | 27 | instructions: std.MultiArrayList(Zir.Inst) = .{}, |
| 24 | extra: ArrayListUnmanaged(u32) = .{}, | 28 | extra: ArrayListUnmanaged(u32) = .{}, |
| 25 | string_bytes: ArrayListUnmanaged(u8) = .{}, | 29 | string_bytes: ArrayListUnmanaged(u8) = .{}, |
| ... | @@ -113,10 +117,14 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { | ... | @@ -113,10 +117,14 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { |
| 113 | var arena = std.heap.ArenaAllocator.init(gpa); | 117 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 114 | defer arena.deinit(); | 118 | defer arena.deinit(); |
| 115 | | 119 | |
| | 120 | var nodes_need_rl = try AstRlAnnotate.annotate(gpa, arena.allocator(), tree); |
| | 121 | defer nodes_need_rl.deinit(gpa); |
| | 122 | |
| 116 | var astgen: AstGen = .{ | 123 | var astgen: AstGen = .{ |
| 117 | .gpa = gpa, | 124 | .gpa = gpa, |
| 118 | .arena = arena.allocator(), | 125 | .arena = arena.allocator(), |
| 119 | .tree = &tree, | 126 | .tree = &tree, |
| | 127 | .nodes_need_rl = &nodes_need_rl, |
| 120 | }; | 128 | }; |
| 121 | defer astgen.deinit(gpa); | 129 | defer astgen.deinit(gpa); |
| 122 | | 130 | |
| ... | @@ -272,69 +280,12 @@ const ResultInfo = struct { | ... | @@ -272,69 +280,12 @@ const ResultInfo = struct { |
| 272 | /// The result instruction from the expression must be ignored. | 280 | /// The result instruction from the expression must be ignored. |
| 273 | /// Always an instruction with tag `alloc_inferred`. | 281 | /// Always an instruction with tag `alloc_inferred`. |
| 274 | inferred_ptr: Zir.Inst.Ref, | 282 | inferred_ptr: Zir.Inst.Ref, |
| 275 | /// There is a pointer for the expression to store its result into, however, its type | | |
| 276 | /// is inferred based on peer type resolution for a `Zir.Inst.Block`. | | |
| 277 | /// The result instruction from the expression must be ignored. | | |
| 278 | block_ptr: *GenZir, | | |
| 279 | | 283 | |
| 280 | const PtrResultLoc = struct { | 284 | const PtrResultLoc = struct { |
| 281 | inst: Zir.Inst.Ref, | 285 | inst: Zir.Inst.Ref, |
| 282 | src_node: ?Ast.Node.Index = null, | 286 | src_node: ?Ast.Node.Index = null, |
| 283 | }; | 287 | }; |
| 284 | | 288 | |
| 285 | const Strategy = struct { | | |
| 286 | elide_store_to_block_ptr_instructions: bool, | | |
| 287 | tag: Tag, | | |
| 288 | | | |
| 289 | const Tag = enum { | | |
| 290 | /// Both branches will use break_void; result location is used to communicate the | | |
| 291 | /// result instruction. | | |
| 292 | break_void, | | |
| 293 | /// Use break statements to pass the block result value, and call rvalue() at | | |
| 294 | /// the end depending on rl. Also elide the store_to_block_ptr instructions | | |
| 295 | /// depending on rl. | | |
| 296 | break_operand, | | |
| 297 | }; | | |
| 298 | }; | | |
| 299 | | | |
| 300 | fn strategy(rl: Loc, block_scope: *GenZir) Strategy { | | |
| 301 | switch (rl) { | | |
| 302 | // In this branch there will not be any store_to_block_ptr instructions. | | |
| 303 | .none, .ty, .coerced_ty, .ref => return .{ | | |
| 304 | .tag = .break_operand, | | |
| 305 | .elide_store_to_block_ptr_instructions = false, | | |
| 306 | }, | | |
| 307 | .discard => return .{ | | |
| 308 | .tag = .break_void, | | |
| 309 | .elide_store_to_block_ptr_instructions = false, | | |
| 310 | }, | | |
| 311 | // The pointer got passed through to the sub-expressions, so we will use | | |
| 312 | // break_void here. | | |
| 313 | // In this branch there will not be any store_to_block_ptr instructions. | | |
| 314 | .ptr => return .{ | | |
| 315 | .tag = .break_void, | | |
| 316 | .elide_store_to_block_ptr_instructions = false, | | |
| 317 | }, | | |
| 318 | .inferred_ptr, .block_ptr => { | | |
| 319 | if (block_scope.rvalue_rl_count == block_scope.break_count) { | | |
| 320 | // Neither prong of the if consumed the result location, so we can | | |
| 321 | // use break instructions to create an rvalue. | | |
| 322 | return .{ | | |
| 323 | .tag = .break_operand, | | |
| 324 | .elide_store_to_block_ptr_instructions = true, | | |
| 325 | }; | | |
| 326 | } else { | | |
| 327 | // Allow the store_to_block_ptr instructions to remain so that | | |
| 328 | // semantic analysis can turn them into bitcasts. | | |
| 329 | return .{ | | |
| 330 | .tag = .break_void, | | |
| 331 | .elide_store_to_block_ptr_instructions = false, | | |
| 332 | }; | | |
| 333 | } | | |
| 334 | }, | | |
| 335 | } | | |
| 336 | } | | |
| 337 | | | |
| 338 | /// Find the result type for a cast builtin given the result location. | 289 | /// Find the result type for a cast builtin given the result location. |
| 339 | /// If the location does not have a known result type, emits an error on | 290 | /// If the location does not have a known result type, emits an error on |
| 340 | /// the given node. | 291 | /// the given node. |
| ... | @@ -347,13 +298,6 @@ const ResultInfo = struct { | ... | @@ -347,13 +298,6 @@ const ResultInfo = struct { |
| 347 | const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node); | 298 | const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node); |
| 348 | return gz.addUnNode(.elem_type, ptr_ty, node); | 299 | return gz.addUnNode(.elem_type, ptr_ty, node); |
| 349 | }, | 300 | }, |
| 350 | .block_ptr => |block_scope| { | | |
| 351 | if (block_scope.rl_ty_inst != .none) return block_scope.rl_ty_inst; | | |
| 352 | if (block_scope.break_result_info.rl == .ptr) { | | |
| 353 | const ptr_ty = try gz.addUnNode(.typeof, block_scope.break_result_info.rl.ptr.inst, node); | | |
| 354 | return gz.addUnNode(.elem_type, ptr_ty, node); | | |
| 355 | } | | |
| 356 | }, | | |
| 357 | } | 301 | } |
| 358 | | 302 | |
| 359 | return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ | 303 | return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ |
| ... | @@ -1516,7 +1460,7 @@ fn arrayInitExpr( | ... | @@ -1516,7 +1460,7 @@ fn arrayInitExpr( |
| 1516 | return rvalue(gz, ri, result, node); | 1460 | return rvalue(gz, ri, result, node); |
| 1517 | }, | 1461 | }, |
| 1518 | .ptr => |ptr_res| { | 1462 | .ptr => |ptr_res| { |
| 1519 | return arrayInitExprRlPtr(gz, scope, ri, node, ptr_res.inst, array_init.ast.elements, types.array); | 1463 | return arrayInitExprRlPtr(gz, scope, node, ptr_res.inst, array_init.ast.elements, types.array); |
| 1520 | }, | 1464 | }, |
| 1521 | .inferred_ptr => |ptr_inst| { | 1465 | .inferred_ptr => |ptr_inst| { |
| 1522 | if (types.array == .none) { | 1466 | if (types.array == .none) { |
| ... | @@ -1526,17 +1470,8 @@ fn arrayInitExpr( | ... | @@ -1526,17 +1470,8 @@ fn arrayInitExpr( |
| 1526 | const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); | 1470 | const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); |
| 1527 | return rvalue(gz, ri, result, node); | 1471 | return rvalue(gz, ri, result, node); |
| 1528 | } else { | 1472 | } else { |
| 1529 | return arrayInitExprRlPtr(gz, scope, ri, node, ptr_inst, array_init.ast.elements, types.array); | 1473 | return arrayInitExprRlPtr(gz, scope, node, ptr_inst, array_init.ast.elements, types.array); |
| 1530 | } | | |
| 1531 | }, | | |
| 1532 | .block_ptr => |block_gz| { | | |
| 1533 | // This condition is here for the same reason as the above condition in `inferred_ptr`. | | |
| 1534 | // See corresponding logic in structInitExpr. | | |
| 1535 | if (types.array == .none and astgen.isInferred(block_gz.rl_ptr)) { | | |
| 1536 | const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); | | |
| 1537 | return rvalue(gz, ri, result, node); | | |
| 1538 | } | 1474 | } |
| 1539 | return arrayInitExprRlPtr(gz, scope, ri, node, block_gz.rl_ptr, array_init.ast.elements, types.array); | | |
| 1540 | }, | 1475 | }, |
| 1541 | } | 1476 | } |
| 1542 | } | 1477 | } |
| ... | @@ -1609,7 +1544,6 @@ fn arrayInitExprInner( | ... | @@ -1609,7 +1544,6 @@ fn arrayInitExprInner( |
| 1609 | fn arrayInitExprRlPtr( | 1544 | fn arrayInitExprRlPtr( |
| 1610 | gz: *GenZir, | 1545 | gz: *GenZir, |
| 1611 | scope: *Scope, | 1546 | scope: *Scope, |
| 1612 | ri: ResultInfo, | | |
| 1613 | node: Ast.Node.Index, | 1547 | node: Ast.Node.Index, |
| 1614 | result_ptr: Zir.Inst.Ref, | 1548 | result_ptr: Zir.Inst.Ref, |
| 1615 | elements: []const Ast.Node.Index, | 1549 | elements: []const Ast.Node.Index, |
| ... | @@ -1620,11 +1554,8 @@ fn arrayInitExprRlPtr( | ... | @@ -1620,11 +1554,8 @@ fn arrayInitExprRlPtr( |
| 1620 | return arrayInitExprRlPtrInner(gz, scope, node, base_ptr, elements); | 1554 | return arrayInitExprRlPtrInner(gz, scope, node, base_ptr, elements); |
| 1621 | } | 1555 | } |
| 1622 | | 1556 | |
| 1623 | var as_scope = try gz.makeCoercionScope(scope, array_ty, result_ptr, node); | 1557 | const casted_ptr = try gz.addPlNode(.coerce_result_ptr, node, Zir.Inst.Bin{ .lhs = array_ty, .rhs = result_ptr }); |
| 1624 | defer as_scope.unstack(); | 1558 | return arrayInitExprRlPtrInner(gz, scope, node, casted_ptr, elements); |
| 1625 | | | |
| 1626 | const result = try arrayInitExprRlPtrInner(&as_scope, scope, node, as_scope.rl_ptr, elements); | | |
| 1627 | return as_scope.finishCoercion(gz, ri, node, result, array_ty); | | |
| 1628 | } | 1559 | } |
| 1629 | | 1560 | |
| 1630 | fn arrayInitExprRlPtrInner( | 1561 | fn arrayInitExprRlPtrInner( |
| ... | @@ -1759,7 +1690,7 @@ fn structInitExpr( | ... | @@ -1759,7 +1690,7 @@ fn structInitExpr( |
| 1759 | const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init); | 1690 | const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init); |
| 1760 | return rvalue(gz, ri, result, node); | 1691 | return rvalue(gz, ri, result, node); |
| 1761 | }, | 1692 | }, |
| 1762 | .ptr => |ptr_res| return structInitExprRlPtr(gz, scope, ri, node, struct_init, ptr_res.inst), | 1693 | .ptr => |ptr_res| return structInitExprRlPtr(gz, scope, node, struct_init, ptr_res.inst), |
| 1763 | .inferred_ptr => |ptr_inst| { | 1694 | .inferred_ptr => |ptr_inst| { |
| 1764 | if (struct_init.ast.type_expr == 0) { | 1695 | if (struct_init.ast.type_expr == 0) { |
| 1765 | // We treat this case differently so that we don't get a crash when | 1696 | // We treat this case differently so that we don't get a crash when |
| ... | @@ -1768,19 +1699,9 @@ fn structInitExpr( | ... | @@ -1768,19 +1699,9 @@ fn structInitExpr( |
| 1768 | const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); | 1699 | const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); |
| 1769 | return rvalue(gz, ri, result, node); | 1700 | return rvalue(gz, ri, result, node); |
| 1770 | } else { | 1701 | } else { |
| 1771 | return structInitExprRlPtr(gz, scope, ri, node, struct_init, ptr_inst); | 1702 | return structInitExprRlPtr(gz, scope, node, struct_init, ptr_inst); |
| 1772 | } | 1703 | } |
| 1773 | }, | 1704 | }, |
| 1774 | .block_ptr => |block_gz| { | | |
| 1775 | // This condition is here for the same reason as the above condition in `inferred_ptr`. | | |
| 1776 | // See corresponding logic in arrayInitExpr. | | |
| 1777 | if (struct_init.ast.type_expr == 0 and astgen.isInferred(block_gz.rl_ptr)) { | | |
| 1778 | const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); | | |
| 1779 | return rvalue(gz, ri, result, node); | | |
| 1780 | } | | |
| 1781 | | | |
| 1782 | return structInitExprRlPtr(gz, scope, ri, node, struct_init, block_gz.rl_ptr); | | |
| 1783 | }, | | |
| 1784 | } | 1705 | } |
| 1785 | } | 1706 | } |
| 1786 | | 1707 | |
| ... | @@ -1824,7 +1745,6 @@ fn structInitExprRlNone( | ... | @@ -1824,7 +1745,6 @@ fn structInitExprRlNone( |
| 1824 | fn structInitExprRlPtr( | 1745 | fn structInitExprRlPtr( |
| 1825 | gz: *GenZir, | 1746 | gz: *GenZir, |
| 1826 | scope: *Scope, | 1747 | scope: *Scope, |
| 1827 | ri: ResultInfo, | | |
| 1828 | node: Ast.Node.Index, | 1748 | node: Ast.Node.Index, |
| 1829 | struct_init: Ast.full.StructInit, | 1749 | struct_init: Ast.full.StructInit, |
| 1830 | result_ptr: Zir.Inst.Ref, | 1750 | result_ptr: Zir.Inst.Ref, |
| ... | @@ -1836,11 +1756,8 @@ fn structInitExprRlPtr( | ... | @@ -1836,11 +1756,8 @@ fn structInitExprRlPtr( |
| 1836 | const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); | 1756 | const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); |
| 1837 | _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); | 1757 | _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node); |
| 1838 | | 1758 | |
| 1839 | var as_scope = try gz.makeCoercionScope(scope, ty_inst, result_ptr, node); | 1759 | const casted_ptr = try gz.addPlNode(.coerce_result_ptr, node, Zir.Inst.Bin{ .lhs = ty_inst, .rhs = result_ptr }); |
| 1840 | defer as_scope.unstack(); | 1760 | return structInitExprRlPtrInner(gz, scope, node, struct_init, casted_ptr); |
| 1841 | | | |
| 1842 | const result = try structInitExprRlPtrInner(&as_scope, scope, node, struct_init, as_scope.rl_ptr); | | |
| 1843 | return as_scope.finishCoercion(gz, ri, node, result, ty_inst); | | |
| 1844 | } | 1761 | } |
| 1845 | | 1762 | |
| 1846 | fn structInitExprRlPtrInner( | 1763 | fn structInitExprRlPtrInner( |
| ... | @@ -2039,22 +1956,13 @@ fn restoreErrRetIndex( | ... | @@ -2039,22 +1956,13 @@ fn restoreErrRetIndex( |
| 2039 | .maybe => switch (ri.ctx) { | 1956 | .maybe => switch (ri.ctx) { |
| 2040 | .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) { | 1957 | .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) { |
| 2041 | .ptr => |ptr_res| try gz.addUnNode(.load, ptr_res.inst, node), | 1958 | .ptr => |ptr_res| try gz.addUnNode(.load, ptr_res.inst, node), |
| 2042 | .inferred_ptr => |ptr| try gz.addUnNode(.load, ptr, node), | 1959 | .inferred_ptr => blk: { |
| 2043 | .block_ptr => |block_scope| if (block_scope.rvalue_rl_count != block_scope.break_count) b: { | 1960 | // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr |
| 2044 | // The result location may have been used by this expression, in which case | 1961 | // before its type has been resolved. There is no valid operand to use here, so error |
| 2045 | // the operand is not the result and we need to load the rl ptr. | 1962 | // traces will be popped prematurely. |
| 2046 | switch (gz.astgen.instructions.items(.tag)[Zir.refToIndex(block_scope.rl_ptr).?]) { | 1963 | // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it. |
| 2047 | .alloc_inferred, .alloc_inferred_mut => { | 1964 | break :blk .none; |
| 2048 | // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr | 1965 | }, |
| 2049 | // before its type has been resolved. The operand we use here instead is not guaranteed | | |
| 2050 | // to be valid, and when it's not, we will pop error traces prematurely. | | |
| 2051 | // | | |
| 2052 | // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it. | | |
| 2053 | break :b result; | | |
| 2054 | }, | | |
| 2055 | else => break :b try gz.addUnNode(.load, block_scope.rl_ptr, node), | | |
| 2056 | } | | |
| 2057 | } else result, | | |
| 2058 | else => result, | 1966 | else => result, |
| 2059 | }, | 1967 | }, |
| 2060 | else => .none, // always restore/pop | 1968 | else => .none, // always restore/pop |
| ... | @@ -2110,7 +2018,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn | ... | @@ -2110,7 +2018,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 2110 | else | 2018 | else |
| 2111 | .@"break"; | 2019 | .@"break"; |
| 2112 | | 2020 | |
| 2113 | block_gz.break_count += 1; | | |
| 2114 | if (rhs == 0) { | 2021 | if (rhs == 0) { |
| 2115 | _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node); | 2022 | _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node); |
| 2116 | | 2023 | |
| ... | @@ -2125,7 +2032,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn | ... | @@ -2125,7 +2032,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 2125 | } | 2032 | } |
| 2126 | | 2033 | |
| 2127 | const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node); | 2034 | const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node); |
| 2128 | const search_index: Zir.Inst.Index = @intCast(astgen.instructions.len); | | |
| 2129 | | 2035 | |
| 2130 | try genDefers(parent_gz, scope, parent_scope, .normal_only); | 2036 | try genDefers(parent_gz, scope, parent_scope, .normal_only); |
| 2131 | | 2037 | |
| ... | @@ -2134,10 +2040,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn | ... | @@ -2134,10 +2040,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 2134 | try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand); | 2040 | try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand); |
| 2135 | | 2041 | |
| 2136 | switch (block_gz.break_result_info.rl) { | 2042 | switch (block_gz.break_result_info.rl) { |
| 2137 | .block_ptr => { | | |
| 2138 | const br = try parent_gz.addBreakWithSrcNode(break_tag, block_inst, operand, rhs); | | |
| 2139 | try block_gz.labeled_breaks.append(astgen.gpa, .{ .br = br, .search = search_index }); | | |
| 2140 | }, | | |
| 2141 | .ptr => { | 2043 | .ptr => { |
| 2142 | // In this case we don't have any mechanism to intercept it; | 2044 | // In this case we don't have any mechanism to intercept it; |
| 2143 | // we assume the result location is written, and we break with void. | 2045 | // we assume the result location is written, and we break with void. |
| ... | @@ -2346,6 +2248,20 @@ fn labeledBlockExpr( | ... | @@ -2346,6 +2248,20 @@ fn labeledBlockExpr( |
| 2346 | | 2248 | |
| 2347 | try astgen.checkLabelRedefinition(parent_scope, label_token); | 2249 | try astgen.checkLabelRedefinition(parent_scope, label_token); |
| 2348 | | 2250 | |
| | 2251 | const need_rl = astgen.nodes_need_rl.contains(block_node); |
| | 2252 | const block_ri: ResultInfo = if (need_rl) ri else .{ |
| | 2253 | .rl = switch (ri.rl) { |
| | 2254 | .ptr => .{ .ty = try ri.rl.resultType(gz, block_node, undefined) }, |
| | 2255 | .inferred_ptr => .none, |
| | 2256 | else => ri.rl, |
| | 2257 | }, |
| | 2258 | .ctx = ri.ctx, |
| | 2259 | }; |
| | 2260 | // We need to call `rvalue` to write through to the pointer only if we had a |
| | 2261 | // result pointer and aren't forwarding it. |
| | 2262 | const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?; |
| | 2263 | const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); |
| | 2264 | |
| 2349 | // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct | 2265 | // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct |
| 2350 | // so that break statements can reference it. | 2266 | // so that break statements can reference it. |
| 2351 | const block_tag: Zir.Inst.Tag = if (force_comptime) .block_comptime else .block; | 2267 | const block_tag: Zir.Inst.Tag = if (force_comptime) .block_comptime else .block; |
| ... | @@ -2356,10 +2272,9 @@ fn labeledBlockExpr( | ... | @@ -2356,10 +2272,9 @@ fn labeledBlockExpr( |
| 2356 | .token = label_token, | 2272 | .token = label_token, |
| 2357 | .block_inst = block_inst, | 2273 | .block_inst = block_inst, |
| 2358 | }; | 2274 | }; |
| 2359 | block_scope.setBreakResultInfo(ri); | 2275 | block_scope.setBreakResultInfo(block_ri); |
| 2360 | if (force_comptime) block_scope.is_comptime = true; | 2276 | if (force_comptime) block_scope.is_comptime = true; |
| 2361 | defer block_scope.unstack(); | 2277 | defer block_scope.unstack(); |
| 2362 | defer block_scope.labeled_breaks.deinit(astgen.gpa); | | |
| 2363 | | 2278 | |
| 2364 | try blockExprStmts(&block_scope, &block_scope.base, statements); | 2279 | try blockExprStmts(&block_scope, &block_scope.base, statements); |
| 2365 | if (!block_scope.endsWithNoReturn()) { | 2280 | if (!block_scope.endsWithNoReturn()) { |
| ... | @@ -2372,75 +2287,11 @@ fn labeledBlockExpr( | ... | @@ -2372,75 +2287,11 @@ fn labeledBlockExpr( |
| 2372 | try astgen.appendErrorTok(label_token, "unused block label", .{}); | 2287 | try astgen.appendErrorTok(label_token, "unused block label", .{}); |
| 2373 | } | 2288 | } |
| 2374 | | 2289 | |
| 2375 | const zir_datas = astgen.instructions.items(.data); | 2290 | try block_scope.setBlockBody(block_inst); |
| 2376 | const zir_tags = astgen.instructions.items(.tag); | 2291 | if (need_result_rvalue) { |
| 2377 | const strat = ri.rl.strategy(&block_scope); | 2292 | return rvalue(gz, ri, indexToRef(block_inst), block_node); |
| 2378 | switch (strat.tag) { | 2293 | } else { |
| 2379 | .break_void => { | 2294 | return indexToRef(block_inst); |
| 2380 | // The code took advantage of the result location as a pointer. | | |
| 2381 | // Turn the break instruction operands into void. | | |
| 2382 | for (block_scope.labeled_breaks.items) |br| { | | |
| 2383 | zir_datas[br.br].@"break".operand = .void_value; | | |
| 2384 | } | | |
| 2385 | try block_scope.setBlockBody(block_inst); | | |
| 2386 | | | |
| 2387 | return indexToRef(block_inst); | | |
| 2388 | }, | | |
| 2389 | .break_operand => { | | |
| 2390 | // All break operands are values that did not use the result location pointer | | |
| 2391 | // (except for a single .store_to_block_ptr inst which we re-write here). | | |
| 2392 | // The break instructions need to have their operands coerced if the | | |
| 2393 | // block's result location is a `ty`. In this case we overwrite the | | |
| 2394 | // `store_to_block_ptr` instruction with an `as` instruction and repurpose | | |
| 2395 | // it as the break operand. | | |
| 2396 | // This corresponds to similar code in `setCondBrPayloadElideBlockStorePtr`. | | |
| 2397 | if (block_scope.rl_ty_inst != .none) { | | |
| 2398 | try astgen.extra.ensureUnusedCapacity( | | |
| 2399 | astgen.gpa, | | |
| 2400 | @typeInfo(Zir.Inst.As).Struct.fields.len * block_scope.labeled_breaks.items.len, | | |
| 2401 | ); | | |
| 2402 | for (block_scope.labeled_breaks.items) |br| { | | |
| 2403 | // We expect the `store_to_block_ptr` to be created between 1-3 instructions | | |
| 2404 | // prior to the break. | | |
| 2405 | var search_index = br.search -| 3; | | |
| 2406 | while (search_index < br.search) : (search_index += 1) { | | |
| 2407 | if (zir_tags[search_index] == .store_to_block_ptr and | | |
| 2408 | zir_datas[search_index].bin.lhs == block_scope.rl_ptr) | | |
| 2409 | { | | |
| 2410 | const break_data = &zir_datas[br.br].@"break"; | | |
| 2411 | const break_src: i32 = @bitCast(astgen.extra.items[ | | |
| 2412 | break_data.payload_index + | | |
| 2413 | std.meta.fieldIndex(Zir.Inst.Break, "operand_src_node").? | | |
| 2414 | ]); | | |
| 2415 | if (break_src == Zir.Inst.Break.no_src_node) { | | |
| 2416 | zir_tags[search_index] = .as; | | |
| 2417 | zir_datas[search_index].bin = .{ | | |
| 2418 | .lhs = block_scope.rl_ty_inst, | | |
| 2419 | .rhs = break_data.operand, | | |
| 2420 | }; | | |
| 2421 | } else { | | |
| 2422 | zir_tags[search_index] = .as_node; | | |
| 2423 | zir_datas[search_index] = .{ .pl_node = .{ | | |
| 2424 | .src_node = break_src, | | |
| 2425 | .payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.As{ | | |
| 2426 | .dest_type = block_scope.rl_ty_inst, | | |
| 2427 | .operand = break_data.operand, | | |
| 2428 | }), | | |
| 2429 | } }; | | |
| 2430 | } | | |
| 2431 | break_data.operand = indexToRef(search_index); | | |
| 2432 | break; | | |
| 2433 | } | | |
| 2434 | } else unreachable; | | |
| 2435 | } | | |
| 2436 | } | | |
| 2437 | try block_scope.setBlockBody(block_inst); | | |
| 2438 | const block_ref = indexToRef(block_inst); | | |
| 2439 | switch (ri.rl) { | | |
| 2440 | .ref => return block_ref, | | |
| 2441 | else => return rvalue(gz, ri, block_ref, block_node), | | |
| 2442 | } | | |
| 2443 | }, | | |
| 2444 | } | 2295 | } |
| 2445 | } | 2296 | } |
| 2446 | | 2297 | |
| ... | @@ -2818,7 +2669,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2818,7 +2669,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2818 | .atomic_store, | 2669 | .atomic_store, |
| 2819 | .store, | 2670 | .store, |
| 2820 | .store_node, | 2671 | .store_node, |
| 2821 | .store_to_block_ptr, | | |
| 2822 | .store_to_inferred_ptr, | 2672 | .store_to_inferred_ptr, |
| 2823 | .resolve_inferred_alloc, | 2673 | .resolve_inferred_alloc, |
| 2824 | .validate_struct_init, | 2674 | .validate_struct_init, |
| ... | @@ -3137,7 +2987,7 @@ fn varDecl( | ... | @@ -3137,7 +2987,7 @@ fn varDecl( |
| 3137 | // the variable, no memory location needed. | 2987 | // the variable, no memory location needed. |
| 3138 | const type_node = var_decl.ast.type_node; | 2988 | const type_node = var_decl.ast.type_node; |
| 3139 | if (align_inst == .none and | 2989 | if (align_inst == .none and |
| 3140 | !nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node, type_node != 0)) | 2990 | !astgen.nodes_need_rl.contains(node)) |
| 3141 | { | 2991 | { |
| 3142 | const result_info: ResultInfo = if (type_node != 0) .{ | 2992 | const result_info: ResultInfo = if (type_node != 0) .{ |
| 3143 | .rl = .{ .ty = try typeExpr(gz, scope, type_node) }, | 2993 | .rl = .{ .ty = try typeExpr(gz, scope, type_node) }, |
| ... | @@ -3170,132 +3020,62 @@ fn varDecl( | ... | @@ -3170,132 +3020,62 @@ fn varDecl( |
| 3170 | const is_comptime = gz.is_comptime or | 3020 | const is_comptime = gz.is_comptime or |
| 3171 | tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime"; | 3021 | tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime"; |
| 3172 | | 3022 | |
| 3173 | // Detect whether the initialization expression actually uses the | | |
| 3174 | // result location pointer. | | |
| 3175 | var init_scope = gz.makeSubBlock(scope); | | |
| 3176 | // we may add more instructions to gz before stacking init_scope | | |
| 3177 | init_scope.instructions_top = GenZir.unstacked_top; | | |
| 3178 | init_scope.anon_name_strategy = .dbg_var; | | |
| 3179 | defer init_scope.unstack(); | | |
| 3180 | | | |
| 3181 | var resolve_inferred_alloc: Zir.Inst.Ref = .none; | 3023 | var resolve_inferred_alloc: Zir.Inst.Ref = .none; |
| 3182 | var opt_type_inst: Zir.Inst.Ref = .none; | 3024 | var opt_type_inst: Zir.Inst.Ref = .none; |
| 3183 | if (type_node != 0) { | 3025 | const init_rl: ResultInfo.Loc = if (type_node != 0) init_rl: { |
| 3184 | const type_inst = try typeExpr(gz, &init_scope.base, type_node); | 3026 | const type_inst = try typeExpr(gz, scope, type_node); |
| 3185 | opt_type_inst = type_inst; | 3027 | opt_type_inst = type_inst; |
| 3186 | if (align_inst == .none) { | 3028 | if (align_inst == .none) { |
| 3187 | init_scope.instructions_top = gz.instructions.items.len; | 3029 | break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } }; |
| 3188 | init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node); | | |
| 3189 | } else { | 3030 | } else { |
| 3190 | init_scope.rl_ptr = try gz.addAllocExtended(.{ | 3031 | break :init_rl .{ .ptr = .{ .inst = try gz.addAllocExtended(.{ |
| 3191 | .node = node, | 3032 | .node = node, |
| 3192 | .type_inst = type_inst, | 3033 | .type_inst = type_inst, |
| 3193 | .align_inst = align_inst, | 3034 | .align_inst = align_inst, |
| 3194 | .is_const = true, | 3035 | .is_const = true, |
| 3195 | .is_comptime = is_comptime, | 3036 | .is_comptime = is_comptime, |
| 3196 | }); | 3037 | }) } }; |
| 3197 | init_scope.instructions_top = gz.instructions.items.len; | | |
| 3198 | } | 3038 | } |
| 3199 | init_scope.rl_ty_inst = type_inst; | 3039 | } else init_rl: { |
| 3200 | } else { | 3040 | const alloc_inst = if (align_inst == .none) ptr: { |
| 3201 | const alloc = if (align_inst == .none) alloc: { | | |
| 3202 | init_scope.instructions_top = gz.instructions.items.len; | | |
| 3203 | const tag: Zir.Inst.Tag = if (is_comptime) | 3041 | const tag: Zir.Inst.Tag = if (is_comptime) |
| 3204 | .alloc_inferred_comptime | 3042 | .alloc_inferred_comptime |
| 3205 | else | 3043 | else |
| 3206 | .alloc_inferred; | 3044 | .alloc_inferred; |
| 3207 | break :alloc try init_scope.addNode(tag, node); | 3045 | break :ptr try gz.addNode(tag, node); |
| 3208 | } else alloc: { | 3046 | } else ptr: { |
| 3209 | const ref = try gz.addAllocExtended(.{ | 3047 | break :ptr try gz.addAllocExtended(.{ |
| 3210 | .node = node, | 3048 | .node = node, |
| 3211 | .type_inst = .none, | 3049 | .type_inst = .none, |
| 3212 | .align_inst = align_inst, | 3050 | .align_inst = align_inst, |
| 3213 | .is_const = true, | 3051 | .is_const = true, |
| 3214 | .is_comptime = is_comptime, | 3052 | .is_comptime = is_comptime, |
| 3215 | }); | 3053 | }); |
| 3216 | init_scope.instructions_top = gz.instructions.items.len; | | |
| 3217 | break :alloc ref; | | |
| 3218 | }; | 3054 | }; |
| 3219 | resolve_inferred_alloc = alloc; | 3055 | resolve_inferred_alloc = alloc_inst; |
| 3220 | init_scope.rl_ptr = alloc; | 3056 | break :init_rl .{ .inferred_ptr = alloc_inst }; |
| 3221 | init_scope.rl_ty_inst = .none; | 3057 | }; |
| 3222 | } | 3058 | const var_ptr = switch (init_rl) { |
| 3223 | const init_result_info: ResultInfo = .{ .rl = .{ .block_ptr = &init_scope }, .ctx = .const_init }; | 3059 | .ptr => |ptr| ptr.inst, |
| 3224 | const init_inst = try reachableExpr(&init_scope, &init_scope.base, init_result_info, var_decl.ast.init_node, node); | 3060 | .inferred_ptr => |inst| inst, |
| | 3061 | else => unreachable, |
| | 3062 | }; |
| | 3063 | const init_result_info: ResultInfo = .{ .rl = init_rl, .ctx = .const_init }; |
| | 3064 | |
| | 3065 | const prev_anon_name_strategy = gz.anon_name_strategy; |
| | 3066 | gz.anon_name_strategy = .dbg_var; |
| | 3067 | defer gz.anon_name_strategy = prev_anon_name_strategy; |
| | 3068 | const init_inst = try reachableExpr(gz, scope, init_result_info, var_decl.ast.init_node, node); |
| 3225 | | 3069 | |
| 3226 | // The const init expression may have modified the error return trace, so signal | 3070 | // The const init expression may have modified the error return trace, so signal |
| 3227 | // to Sema that it should save the new index for restoring later. | 3071 | // to Sema that it should save the new index for restoring later. |
| 3228 | if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node)) | 3072 | if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node)) |
| 3229 | _ = try init_scope.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }); | 3073 | _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }); |
| 3230 | | | |
| 3231 | const zir_tags = astgen.instructions.items(.tag); | | |
| 3232 | const zir_datas = astgen.instructions.items(.data); | | |
| 3233 | | | |
| 3234 | if (align_inst == .none and init_scope.rvalue_rl_count == 1) { | | |
| 3235 | // Result location pointer not used. We don't need an alloc for this | | |
| 3236 | // const local, and type inference becomes trivial. | | |
| 3237 | // Implicitly move the init_scope instructions into the parent scope, | | |
| 3238 | // then elide the alloc instruction and the store_to_block_ptr instruction. | | |
| 3239 | var src = init_scope.instructions_top; | | |
| 3240 | var dst = src; | | |
| 3241 | init_scope.instructions_top = GenZir.unstacked_top; | | |
| 3242 | while (src < gz.instructions.items.len) : (src += 1) { | | |
| 3243 | const src_inst = gz.instructions.items[src]; | | |
| 3244 | if (indexToRef(src_inst) == init_scope.rl_ptr) continue; | | |
| 3245 | if (zir_tags[src_inst] == .store_to_block_ptr) { | | |
| 3246 | if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue; | | |
| 3247 | } | | |
| 3248 | gz.instructions.items[dst] = src_inst; | | |
| 3249 | dst += 1; | | |
| 3250 | } | | |
| 3251 | gz.instructions.items.len = dst; | | |
| 3252 | | | |
| 3253 | // In case the result location did not do the coercion | | |
| 3254 | // for us so we must do it here. | | |
| 3255 | const coerced_init = if (opt_type_inst != .none) | | |
| 3256 | try gz.addPlNode(.as_node, var_decl.ast.init_node, Zir.Inst.As{ | | |
| 3257 | .dest_type = opt_type_inst, | | |
| 3258 | .operand = init_inst, | | |
| 3259 | }) | | |
| 3260 | else | | |
| 3261 | init_inst; | | |
| 3262 | | | |
| 3263 | try gz.addDbgVar(.dbg_var_val, ident_name, coerced_init); | | |
| 3264 | | 3074 | |
| 3265 | const sub_scope = try block_arena.create(Scope.LocalVal); | | |
| 3266 | sub_scope.* = .{ | | |
| 3267 | .parent = scope, | | |
| 3268 | .gen_zir = gz, | | |
| 3269 | .name = ident_name, | | |
| 3270 | .inst = coerced_init, | | |
| 3271 | .token_src = name_token, | | |
| 3272 | .id_cat = .@"local constant", | | |
| 3273 | }; | | |
| 3274 | return &sub_scope.base; | | |
| 3275 | } | | |
| 3276 | // The initialization expression took advantage of the result location | | |
| 3277 | // of the const local. In this case we will create an alloc and a LocalPtr for it. | | |
| 3278 | // Implicitly move the init_scope instructions into the parent scope, then swap | | |
| 3279 | // store_to_block_ptr for store_to_inferred_ptr. | | |
| 3280 | | | |
| 3281 | var src = init_scope.instructions_top; | | |
| 3282 | init_scope.instructions_top = GenZir.unstacked_top; | | |
| 3283 | while (src < gz.instructions.items.len) : (src += 1) { | | |
| 3284 | const src_inst = gz.instructions.items[src]; | | |
| 3285 | if (zir_tags[src_inst] == .store_to_block_ptr) { | | |
| 3286 | if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) { | | |
| 3287 | if (type_node != 0) { | | |
| 3288 | zir_tags[src_inst] = .store; | | |
| 3289 | } else { | | |
| 3290 | zir_tags[src_inst] = .store_to_inferred_ptr; | | |
| 3291 | } | | |
| 3292 | } | | |
| 3293 | } | | |
| 3294 | } | | |
| 3295 | if (resolve_inferred_alloc != .none) { | 3075 | if (resolve_inferred_alloc != .none) { |
| 3296 | _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); | 3076 | _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); |
| 3297 | } | 3077 | } |
| 3298 | const const_ptr = try gz.addUnNode(.make_ptr_const, init_scope.rl_ptr, node); | 3078 | const const_ptr = try gz.addUnNode(.make_ptr_const, var_ptr, node); |
| 3299 | | 3079 | |
| 3300 | try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr); | 3080 | try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr); |
| 3301 | | 3081 | |
| ... | @@ -3312,9 +3092,6 @@ fn varDecl( | ... | @@ -3312,9 +3092,6 @@ fn varDecl( |
| 3312 | return &sub_scope.base; | 3092 | return &sub_scope.base; |
| 3313 | }, | 3093 | }, |
| 3314 | .keyword_var => { | 3094 | .keyword_var => { |
| 3315 | const old_rl_ty_inst = gz.rl_ty_inst; | | |
| 3316 | defer gz.rl_ty_inst = old_rl_ty_inst; | | |
| 3317 | | | |
| 3318 | const is_comptime = var_decl.comptime_token != null or gz.is_comptime; | 3095 | const is_comptime = var_decl.comptime_token != null or gz.is_comptime; |
| 3319 | var resolve_inferred_alloc: Zir.Inst.Ref = .none; | 3096 | var resolve_inferred_alloc: Zir.Inst.Ref = .none; |
| 3320 | const var_data: struct { | 3097 | const var_data: struct { |
| ... | @@ -3339,7 +3116,6 @@ fn varDecl( | ... | @@ -3339,7 +3116,6 @@ fn varDecl( |
| 3339 | }); | 3116 | }); |
| 3340 | } | 3117 | } |
| 3341 | }; | 3118 | }; |
| 3342 | gz.rl_ty_inst = type_inst; | | |
| 3343 | break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .ptr = .{ .inst = alloc } } } }; | 3119 | break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .ptr = .{ .inst = alloc } } } }; |
| 3344 | } else a: { | 3120 | } else a: { |
| 3345 | const alloc = alloc: { | 3121 | const alloc = alloc: { |
| ... | @@ -3359,7 +3135,6 @@ fn varDecl( | ... | @@ -3359,7 +3135,6 @@ fn varDecl( |
| 3359 | }); | 3135 | }); |
| 3360 | } | 3136 | } |
| 3361 | }; | 3137 | }; |
| 3362 | gz.rl_ty_inst = .none; | | |
| 3363 | resolve_inferred_alloc = alloc; | 3138 | resolve_inferred_alloc = alloc; |
| 3364 | break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .inferred_ptr = alloc } } }; | 3139 | break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .inferred_ptr = alloc } } }; |
| 3365 | }; | 3140 | }; |
| ... | @@ -5530,17 +5305,30 @@ fn orelseCatchExpr( | ... | @@ -5530,17 +5305,30 @@ fn orelseCatchExpr( |
| 5530 | const astgen = parent_gz.astgen; | 5305 | const astgen = parent_gz.astgen; |
| 5531 | const tree = astgen.tree; | 5306 | const tree = astgen.tree; |
| 5532 | | 5307 | |
| | 5308 | const need_rl = astgen.nodes_need_rl.contains(node); |
| | 5309 | const block_ri: ResultInfo = if (need_rl) ri else .{ |
| | 5310 | .rl = switch (ri.rl) { |
| | 5311 | .ptr => .{ .ty = try ri.rl.resultType(parent_gz, node, undefined) }, |
| | 5312 | .inferred_ptr => .none, |
| | 5313 | else => ri.rl, |
| | 5314 | }, |
| | 5315 | .ctx = ri.ctx, |
| | 5316 | }; |
| | 5317 | // We need to call `rvalue` to write through to the pointer only if we had a |
| | 5318 | // result pointer and aren't forwarding it. |
| | 5319 | const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?; |
| | 5320 | const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); |
| | 5321 | |
| 5533 | const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr); | 5322 | const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr); |
| 5534 | | 5323 | |
| 5535 | var block_scope = parent_gz.makeSubBlock(scope); | 5324 | var block_scope = parent_gz.makeSubBlock(scope); |
| 5536 | block_scope.setBreakResultInfo(ri); | 5325 | block_scope.setBreakResultInfo(block_ri); |
| 5537 | defer block_scope.unstack(); | 5326 | defer block_scope.unstack(); |
| 5538 | | 5327 | |
| 5539 | const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) { | 5328 | const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) { |
| 5540 | .ref => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none }, | 5329 | .ref => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none }, |
| 5541 | else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none }, | 5330 | else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none }, |
| 5542 | }; | 5331 | }; |
| 5543 | block_scope.break_count += 1; | | |
| 5544 | // This could be a pointer or value depending on the `operand_ri` parameter. | 5332 | // This could be a pointer or value depending on the `operand_ri` parameter. |
| 5545 | // We cannot use `block_scope.break_result_info` because that has the bare | 5333 | // We cannot use `block_scope.break_result_info` because that has the bare |
| 5546 | // type, whereas this expression has the optional type. Later we make | 5334 | // type, whereas this expression has the optional type. Later we make |
| ... | @@ -5563,6 +5351,7 @@ fn orelseCatchExpr( | ... | @@ -5563,6 +5351,7 @@ fn orelseCatchExpr( |
| 5563 | .ref => unwrapped_payload, | 5351 | .ref => unwrapped_payload, |
| 5564 | else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node), | 5352 | else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node), |
| 5565 | }; | 5353 | }; |
| | 5354 | _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node); |
| 5566 | | 5355 | |
| 5567 | var else_scope = block_scope.makeSubBlock(scope); | 5356 | var else_scope = block_scope.makeSubBlock(scope); |
| 5568 | defer else_scope.unstack(); | 5357 | defer else_scope.unstack(); |
| ... | @@ -5596,93 +5385,20 @@ fn orelseCatchExpr( | ... | @@ -5596,93 +5385,20 @@ fn orelseCatchExpr( |
| 5596 | | 5385 | |
| 5597 | const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs); | 5386 | const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs); |
| 5598 | if (!else_scope.endsWithNoReturn()) { | 5387 | if (!else_scope.endsWithNoReturn()) { |
| 5599 | block_scope.break_count += 1; | | |
| 5600 | | | |
| 5601 | // As our last action before the break, "pop" the error trace if needed | 5388 | // As our last action before the break, "pop" the error trace if needed |
| 5602 | if (do_err_trace) | 5389 | if (do_err_trace) |
| 5603 | try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, rhs, else_result); | 5390 | try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, rhs, else_result); |
| | 5391 | |
| | 5392 | _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, rhs); |
| 5604 | } | 5393 | } |
| 5605 | try checkUsed(parent_gz, &else_scope.base, else_sub_scope); | 5394 | try checkUsed(parent_gz, &else_scope.base, else_sub_scope); |
| 5606 | | 5395 | |
| 5607 | // We hold off on the break instructions as well as copying the then/else | 5396 | try setCondBrPayload(condbr, cond, &then_scope, &else_scope); |
| 5608 | // instructions into place until we know whether to keep store_to_block_ptr | | |
| 5609 | // instructions or not. | | |
| 5610 | | | |
| 5611 | const result = try finishThenElseBlock( | | |
| 5612 | parent_gz, | | |
| 5613 | ri, | | |
| 5614 | node, | | |
| 5615 | &block_scope, | | |
| 5616 | &then_scope, | | |
| 5617 | &else_scope, | | |
| 5618 | condbr, | | |
| 5619 | cond, | | |
| 5620 | then_result, | | |
| 5621 | node, | | |
| 5622 | else_result, | | |
| 5623 | rhs, | | |
| 5624 | block, | | |
| 5625 | block, | | |
| 5626 | .@"break", | | |
| 5627 | ); | | |
| 5628 | return result; | | |
| 5629 | } | | |
| 5630 | | 5397 | |
| 5631 | /// Supports `else_scope` stacked on `then_scope` stacked on `block_scope`. Unstacks `else_scope` then `then_scope`. | 5398 | if (need_result_rvalue) { |
| 5632 | fn finishThenElseBlock( | 5399 | return rvalue(parent_gz, ri, indexToRef(block), node); |
| 5633 | parent_gz: *GenZir, | 5400 | } else { |
| 5634 | ri: ResultInfo, | 5401 | return indexToRef(block); |
| 5635 | node: Ast.Node.Index, | | |
| 5636 | block_scope: *GenZir, | | |
| 5637 | then_scope: *GenZir, | | |
| 5638 | else_scope: *GenZir, | | |
| 5639 | condbr: Zir.Inst.Index, | | |
| 5640 | cond: Zir.Inst.Ref, | | |
| 5641 | then_result: Zir.Inst.Ref, | | |
| 5642 | then_src_node: Ast.Node.Index, | | |
| 5643 | else_result: Zir.Inst.Ref, | | |
| 5644 | else_src_node: Ast.Node.Index, | | |
| 5645 | main_block: Zir.Inst.Index, | | |
| 5646 | then_break_block: Zir.Inst.Index, | | |
| 5647 | break_tag: Zir.Inst.Tag, | | |
| 5648 | ) InnerError!Zir.Inst.Ref { | | |
| 5649 | // We now have enough information to decide whether the result instruction should | | |
| 5650 | // be communicated via result location pointer or break instructions. | | |
| 5651 | const strat = ri.rl.strategy(block_scope); | | |
| 5652 | // else_scope may be stacked on then_scope, so check for no-return on then_scope manually | | |
| 5653 | const tags = parent_gz.astgen.instructions.items(.tag); | | |
| 5654 | const then_slice = then_scope.instructionsSliceUpto(else_scope); | | |
| 5655 | const then_no_return = then_slice.len > 0 and tags[then_slice[then_slice.len - 1]].isNoReturn(); | | |
| 5656 | const else_no_return = else_scope.endsWithNoReturn(); | | |
| 5657 | | | |
| 5658 | switch (strat.tag) { | | |
| 5659 | .break_void => { | | |
| 5660 | const then_break = if (!then_no_return) try then_scope.makeBreak(break_tag, then_break_block, .void_value) else 0; | | |
| 5661 | const else_break = if (!else_no_return) try else_scope.makeBreak(break_tag, main_block, .void_value) else 0; | | |
| 5662 | assert(!strat.elide_store_to_block_ptr_instructions); | | |
| 5663 | try setCondBrPayload(condbr, cond, then_scope, then_break, else_scope, else_break); | | |
| 5664 | return indexToRef(main_block); | | |
| 5665 | }, | | |
| 5666 | .break_operand => { | | |
| 5667 | const then_break = if (!then_no_return) try then_scope.makeBreakWithSrcNode(break_tag, then_break_block, then_result, then_src_node) else 0; | | |
| 5668 | const else_break = if (else_result == .none) | | |
| 5669 | try else_scope.makeBreak(break_tag, main_block, .void_value) | | |
| 5670 | else if (!else_no_return) | | |
| 5671 | try else_scope.makeBreakWithSrcNode(break_tag, main_block, else_result, else_src_node) | | |
| 5672 | else | | |
| 5673 | 0; | | |
| 5674 | | | |
| 5675 | if (strat.elide_store_to_block_ptr_instructions) { | | |
| 5676 | try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, then_break, then_src_node, else_scope, else_break, else_src_node, block_scope.rl_ptr); | | |
| 5677 | } else { | | |
| 5678 | try setCondBrPayload(condbr, cond, then_scope, then_break, else_scope, else_break); | | |
| 5679 | } | | |
| 5680 | const block_ref = indexToRef(main_block); | | |
| 5681 | switch (ri.rl) { | | |
| 5682 | .ref => return block_ref, | | |
| 5683 | else => return rvalue(parent_gz, ri, block_ref, node), | | |
| 5684 | } | | |
| 5685 | }, | | |
| 5686 | } | 5402 | } |
| 5687 | } | 5403 | } |
| 5688 | | 5404 | |
| ... | @@ -5858,8 +5574,22 @@ fn ifExpr( | ... | @@ -5858,8 +5574,22 @@ fn ifExpr( |
| 5858 | | 5574 | |
| 5859 | const do_err_trace = astgen.fn_block != null and if_full.error_token != null; | 5575 | const do_err_trace = astgen.fn_block != null and if_full.error_token != null; |
| 5860 | | 5576 | |
| | 5577 | const need_rl = astgen.nodes_need_rl.contains(node); |
| | 5578 | const block_ri: ResultInfo = if (need_rl) ri else .{ |
| | 5579 | .rl = switch (ri.rl) { |
| | 5580 | .ptr => .{ .ty = try ri.rl.resultType(parent_gz, node, undefined) }, |
| | 5581 | .inferred_ptr => .none, |
| | 5582 | else => ri.rl, |
| | 5583 | }, |
| | 5584 | .ctx = ri.ctx, |
| | 5585 | }; |
| | 5586 | // We need to call `rvalue` to write through to the pointer only if we had a |
| | 5587 | // result pointer and aren't forwarding it. |
| | 5588 | const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?; |
| | 5589 | const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); |
| | 5590 | |
| 5861 | var block_scope = parent_gz.makeSubBlock(scope); | 5591 | var block_scope = parent_gz.makeSubBlock(scope); |
| 5862 | block_scope.setBreakResultInfo(ri); | 5592 | block_scope.setBreakResultInfo(block_ri); |
| 5863 | defer block_scope.unstack(); | 5593 | defer block_scope.unstack(); |
| 5864 | | 5594 | |
| 5865 | const payload_is_ref = if (if_full.payload_token) |payload_token| | 5595 | const payload_is_ref = if (if_full.payload_token) |payload_token| |
| ... | @@ -5967,14 +5697,11 @@ fn ifExpr( | ... | @@ -5967,14 +5697,11 @@ fn ifExpr( |
| 5967 | }; | 5697 | }; |
| 5968 | | 5698 | |
| 5969 | const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node); | 5699 | const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node); |
| | 5700 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); |
| 5970 | if (!then_scope.endsWithNoReturn()) { | 5701 | if (!then_scope.endsWithNoReturn()) { |
| 5971 | block_scope.break_count += 1; | 5702 | try then_scope.addDbgBlockEnd(); |
| | 5703 | _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node); |
| 5972 | } | 5704 | } |
| 5973 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); | | |
| 5974 | try then_scope.addDbgBlockEnd(); | | |
| 5975 | // We hold off on the break instructions as well as copying the then/else | | |
| 5976 | // instructions into place until we know whether to keep store_to_block_ptr | | |
| 5977 | // instructions or not. | | |
| 5978 | | 5705 | |
| 5979 | var else_scope = parent_gz.makeSubBlock(scope); | 5706 | var else_scope = parent_gz.makeSubBlock(scope); |
| 5980 | defer else_scope.unstack(); | 5707 | defer else_scope.unstack(); |
| ... | @@ -5985,10 +5712,7 @@ fn ifExpr( | ... | @@ -5985,10 +5712,7 @@ fn ifExpr( |
| 5985 | _ = try else_scope.addSaveErrRetIndex(.always); | 5712 | _ = try else_scope.addSaveErrRetIndex(.always); |
| 5986 | | 5713 | |
| 5987 | const else_node = if_full.ast.else_expr; | 5714 | const else_node = if_full.ast.else_expr; |
| 5988 | const else_info: struct { | 5715 | if (else_node != 0) { |
| 5989 | src: Ast.Node.Index, | | |
| 5990 | result: Zir.Inst.Ref, | | |
| 5991 | } = if (else_node != 0) blk: { | | |
| 5992 | try else_scope.addDbgBlockBegin(); | 5716 | try else_scope.addDbgBlockBegin(); |
| 5993 | const sub_scope = s: { | 5717 | const sub_scope = s: { |
| 5994 | if (if_full.error_token) |error_token| { | 5718 | if (if_full.error_token) |error_token| { |
| ... | @@ -6016,47 +5740,27 @@ fn ifExpr( | ... | @@ -6016,47 +5740,27 @@ fn ifExpr( |
| 6016 | break :s &else_scope.base; | 5740 | break :s &else_scope.base; |
| 6017 | } | 5741 | } |
| 6018 | }; | 5742 | }; |
| 6019 | const e = try expr(&else_scope, sub_scope, block_scope.break_result_info, else_node); | 5743 | const else_result = try expr(&else_scope, sub_scope, block_scope.break_result_info, else_node); |
| | 5744 | try else_scope.addDbgBlockEnd(); |
| 6020 | if (!else_scope.endsWithNoReturn()) { | 5745 | if (!else_scope.endsWithNoReturn()) { |
| 6021 | block_scope.break_count += 1; | | |
| 6022 | | | |
| 6023 | // As our last action before the break, "pop" the error trace if needed | 5746 | // As our last action before the break, "pop" the error trace if needed |
| 6024 | if (do_err_trace) | 5747 | if (do_err_trace) |
| 6025 | try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, else_node, e); | 5748 | try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, else_node, else_result); |
| | 5749 | _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, else_node); |
| 6026 | } | 5750 | } |
| 6027 | try checkUsed(parent_gz, &else_scope.base, sub_scope); | 5751 | try checkUsed(parent_gz, &else_scope.base, sub_scope); |
| 6028 | try else_scope.addDbgBlockEnd(); | 5752 | } else { |
| 6029 | break :blk .{ | 5753 | const result = try rvalue(&else_scope, ri, .void_value, node); |
| 6030 | .src = else_node, | 5754 | _ = try else_scope.addBreak(.@"break", block, result); |
| 6031 | .result = e, | 5755 | } |
| 6032 | }; | | |
| 6033 | } else .{ | | |
| 6034 | .src = then_node, | | |
| 6035 | .result = switch (ri.rl) { | | |
| 6036 | // Explicitly store void to ptr result loc if there is no else branch | | |
| 6037 | .ptr, .block_ptr => try rvalue(&else_scope, ri, .void_value, node), | | |
| 6038 | else => .none, | | |
| 6039 | }, | | |
| 6040 | }; | | |
| 6041 | | 5756 | |
| 6042 | const result = try finishThenElseBlock( | 5757 | try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope); |
| 6043 | parent_gz, | 5758 | |
| 6044 | ri, | 5759 | if (need_result_rvalue) { |
| 6045 | node, | 5760 | return rvalue(parent_gz, ri, indexToRef(block), node); |
| 6046 | &block_scope, | 5761 | } else { |
| 6047 | &then_scope, | 5762 | return indexToRef(block); |
| 6048 | &else_scope, | 5763 | } |
| 6049 | condbr, | | |
| 6050 | cond.bool_bit, | | |
| 6051 | then_result, | | |
| 6052 | then_node, | | |
| 6053 | else_info.result, | | |
| 6054 | else_info.src, | | |
| 6055 | block, | | |
| 6056 | block, | | |
| 6057 | .@"break", | | |
| 6058 | ); | | |
| 6059 | return result; | | |
| 6060 | } | 5764 | } |
| 6061 | | 5765 | |
| 6062 | /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`. | 5766 | /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`. |
| ... | @@ -6064,17 +5768,15 @@ fn setCondBrPayload( | ... | @@ -6064,17 +5768,15 @@ fn setCondBrPayload( |
| 6064 | condbr: Zir.Inst.Index, | 5768 | condbr: Zir.Inst.Index, |
| 6065 | cond: Zir.Inst.Ref, | 5769 | cond: Zir.Inst.Ref, |
| 6066 | then_scope: *GenZir, | 5770 | then_scope: *GenZir, |
| 6067 | then_break: Zir.Inst.Index, | | |
| 6068 | else_scope: *GenZir, | 5771 | else_scope: *GenZir, |
| 6069 | else_break: Zir.Inst.Index, | | |
| 6070 | ) !void { | 5772 | ) !void { |
| 6071 | defer then_scope.unstack(); | 5773 | defer then_scope.unstack(); |
| 6072 | defer else_scope.unstack(); | 5774 | defer else_scope.unstack(); |
| 6073 | const astgen = then_scope.astgen; | 5775 | const astgen = then_scope.astgen; |
| 6074 | const then_body = then_scope.instructionsSliceUpto(else_scope); | 5776 | const then_body = then_scope.instructionsSliceUpto(else_scope); |
| 6075 | const else_body = else_scope.instructionsSlice(); | 5777 | const else_body = else_scope.instructionsSlice(); |
| 6076 | const then_body_len = astgen.countBodyLenAfterFixups(then_body) + @intFromBool(then_break != 0); | 5778 | const then_body_len = astgen.countBodyLenAfterFixups(then_body); |
| 6077 | const else_body_len = astgen.countBodyLenAfterFixups(else_body) + @intFromBool(else_break != 0); | 5779 | const else_body_len = astgen.countBodyLenAfterFixups(else_body); |
| 6078 | try astgen.extra.ensureUnusedCapacity( | 5780 | try astgen.extra.ensureUnusedCapacity( |
| 6079 | astgen.gpa, | 5781 | astgen.gpa, |
| 6080 | @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len, | 5782 | @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len, |
| ... | @@ -6087,112 +5789,7 @@ fn setCondBrPayload( | ... | @@ -6087,112 +5789,7 @@ fn setCondBrPayload( |
| 6087 | .else_body_len = else_body_len, | 5789 | .else_body_len = else_body_len, |
| 6088 | }); | 5790 | }); |
| 6089 | astgen.appendBodyWithFixups(then_body); | 5791 | astgen.appendBodyWithFixups(then_body); |
| 6090 | if (then_break != 0) astgen.extra.appendAssumeCapacity(then_break); | | |
| 6091 | astgen.appendBodyWithFixups(else_body); | 5792 | astgen.appendBodyWithFixups(else_body); |
| 6092 | if (else_break != 0) astgen.extra.appendAssumeCapacity(else_break); | | |
| 6093 | } | | |
| 6094 | | | |
| 6095 | /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`. | | |
| 6096 | fn setCondBrPayloadElideBlockStorePtr( | | |
| 6097 | condbr: Zir.Inst.Index, | | |
| 6098 | cond: Zir.Inst.Ref, | | |
| 6099 | then_scope: *GenZir, | | |
| 6100 | then_break: Zir.Inst.Index, | | |
| 6101 | then_src_node: Ast.Node.Index, | | |
| 6102 | else_scope: *GenZir, | | |
| 6103 | else_break: Zir.Inst.Index, | | |
| 6104 | else_src_node: Ast.Node.Index, | | |
| 6105 | block_ptr: Zir.Inst.Ref, | | |
| 6106 | ) !void { | | |
| 6107 | defer then_scope.unstack(); | | |
| 6108 | defer else_scope.unstack(); | | |
| 6109 | const astgen = then_scope.astgen; | | |
| 6110 | const then_body = then_scope.instructionsSliceUpto(else_scope); | | |
| 6111 | const else_body = else_scope.instructionsSlice(); | | |
| 6112 | const has_then_break = then_break != 0; | | |
| 6113 | const has_else_break = else_break != 0; | | |
| 6114 | const then_body_len = astgen.countBodyLenAfterFixups(then_body) + @intFromBool(has_then_break); | | |
| 6115 | const else_body_len = astgen.countBodyLenAfterFixups(else_body) + @intFromBool(has_else_break); | | |
| 6116 | try astgen.extra.ensureUnusedCapacity( | | |
| 6117 | astgen.gpa, | | |
| 6118 | @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len + | | |
| 6119 | @typeInfo(Zir.Inst.As).Struct.fields.len * 2, | | |
| 6120 | ); | | |
| 6121 | | | |
| 6122 | const zir_tags = astgen.instructions.items(.tag); | | |
| 6123 | const zir_datas = astgen.instructions.items(.data); | | |
| 6124 | | | |
| 6125 | const condbr_extra = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{ | | |
| 6126 | .condition = cond, | | |
| 6127 | .then_body_len = then_body_len, | | |
| 6128 | .else_body_len = else_body_len, | | |
| 6129 | }); | | |
| 6130 | zir_datas[condbr].pl_node.payload_index = condbr_extra; | | |
| 6131 | | | |
| 6132 | // The break instructions need to have their operands coerced if the | | |
| 6133 | // switch's result location is a `ty`. In this case we overwrite the | | |
| 6134 | // `store_to_block_ptr` instruction with an `as` instruction and repurpose | | |
| 6135 | // it as the break operand. | | |
| 6136 | // This corresponds to similar code in `labeledBlockExpr`. | | |
| 6137 | var then_as_inst: Zir.Inst.Index = 0; | | |
| 6138 | for (then_body) |src_inst| { | | |
| 6139 | if (zir_tags[src_inst] == .store_to_block_ptr and | | |
| 6140 | zir_datas[src_inst].bin.lhs == block_ptr) | | |
| 6141 | { | | |
| 6142 | if (then_scope.rl_ty_inst != .none and has_then_break) { | | |
| 6143 | then_as_inst = src_inst; | | |
| 6144 | } else { | | |
| 6145 | astgen.extra.items[ | | |
| 6146 | condbr_extra + std.meta.fieldIndex(Zir.Inst.CondBr, "then_body_len").? | | |
| 6147 | ] -= 1; | | |
| 6148 | continue; | | |
| 6149 | } | | |
| 6150 | } | | |
| 6151 | appendPossiblyRefdBodyInst(astgen, &astgen.extra, src_inst); | | |
| 6152 | } | | |
| 6153 | if (has_then_break) astgen.extra.appendAssumeCapacity(then_break); | | |
| 6154 | | | |
| 6155 | var else_as_inst: Zir.Inst.Index = 0; | | |
| 6156 | for (else_body) |src_inst| { | | |
| 6157 | if (zir_tags[src_inst] == .store_to_block_ptr and | | |
| 6158 | zir_datas[src_inst].bin.lhs == block_ptr) | | |
| 6159 | { | | |
| 6160 | if (else_scope.rl_ty_inst != .none and has_else_break) { | | |
| 6161 | else_as_inst = src_inst; | | |
| 6162 | } else { | | |
| 6163 | astgen.extra.items[ | | |
| 6164 | condbr_extra + std.meta.fieldIndex(Zir.Inst.CondBr, "else_body_len").? | | |
| 6165 | ] -= 1; | | |
| 6166 | continue; | | |
| 6167 | } | | |
| 6168 | } | | |
| 6169 | appendPossiblyRefdBodyInst(astgen, &astgen.extra, src_inst); | | |
| 6170 | } | | |
| 6171 | if (has_else_break) astgen.extra.appendAssumeCapacity(else_break); | | |
| 6172 | | | |
| 6173 | if (then_as_inst != 0) { | | |
| 6174 | zir_tags[then_as_inst] = .as_node; | | |
| 6175 | zir_datas[then_as_inst] = .{ .pl_node = .{ | | |
| 6176 | .src_node = then_scope.nodeIndexToRelative(then_src_node), | | |
| 6177 | .payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.As{ | | |
| 6178 | .dest_type = then_scope.rl_ty_inst, | | |
| 6179 | .operand = zir_datas[then_break].@"break".operand, | | |
| 6180 | }), | | |
| 6181 | } }; | | |
| 6182 | zir_datas[then_break].@"break".operand = indexToRef(then_as_inst); | | |
| 6183 | } | | |
| 6184 | | | |
| 6185 | if (else_as_inst != 0) { | | |
| 6186 | zir_tags[else_as_inst] = .as_node; | | |
| 6187 | zir_datas[else_as_inst] = .{ .pl_node = .{ | | |
| 6188 | .src_node = else_scope.nodeIndexToRelative(else_src_node), | | |
| 6189 | .payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.As{ | | |
| 6190 | .dest_type = else_scope.rl_ty_inst, | | |
| 6191 | .operand = zir_datas[else_break].@"break".operand, | | |
| 6192 | }), | | |
| 6193 | } }; | | |
| 6194 | zir_datas[else_break].@"break".operand = indexToRef(else_as_inst); | | |
| 6195 | } | | |
| 6196 | } | 5793 | } |
| 6197 | | 5794 | |
| 6198 | fn whileExpr( | 5795 | fn whileExpr( |
| ... | @@ -6207,6 +5804,20 @@ fn whileExpr( | ... | @@ -6207,6 +5804,20 @@ fn whileExpr( |
| 6207 | const tree = astgen.tree; | 5804 | const tree = astgen.tree; |
| 6208 | const token_tags = tree.tokens.items(.tag); | 5805 | const token_tags = tree.tokens.items(.tag); |
| 6209 | | 5806 | |
| | 5807 | const need_rl = astgen.nodes_need_rl.contains(node); |
| | 5808 | const block_ri: ResultInfo = if (need_rl) ri else .{ |
| | 5809 | .rl = switch (ri.rl) { |
| | 5810 | .ptr => .{ .ty = try ri.rl.resultType(parent_gz, node, undefined) }, |
| | 5811 | .inferred_ptr => .none, |
| | 5812 | else => ri.rl, |
| | 5813 | }, |
| | 5814 | .ctx = ri.ctx, |
| | 5815 | }; |
| | 5816 | // We need to call `rvalue` to write through to the pointer only if we had a |
| | 5817 | // result pointer and aren't forwarding it. |
| | 5818 | const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?; |
| | 5819 | const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); |
| | 5820 | |
| 6210 | if (while_full.label_token) |label_token| { | 5821 | if (while_full.label_token) |label_token| { |
| 6211 | try astgen.checkLabelRedefinition(scope, label_token); | 5822 | try astgen.checkLabelRedefinition(scope, label_token); |
| 6212 | } | 5823 | } |
| ... | @@ -6218,9 +5829,8 @@ fn whileExpr( | ... | @@ -6218,9 +5829,8 @@ fn whileExpr( |
| 6218 | | 5829 | |
| 6219 | var loop_scope = parent_gz.makeSubBlock(scope); | 5830 | var loop_scope = parent_gz.makeSubBlock(scope); |
| 6220 | loop_scope.is_inline = is_inline; | 5831 | loop_scope.is_inline = is_inline; |
| 6221 | loop_scope.setBreakResultInfo(ri); | 5832 | loop_scope.setBreakResultInfo(block_ri); |
| 6222 | defer loop_scope.unstack(); | 5833 | defer loop_scope.unstack(); |
| 6223 | defer loop_scope.labeled_breaks.deinit(astgen.gpa); | | |
| 6224 | | 5834 | |
| 6225 | var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); | 5835 | var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); |
| 6226 | defer cond_scope.unstack(); | 5836 | defer cond_scope.unstack(); |
| ... | @@ -6378,19 +5988,16 @@ fn whileExpr( | ... | @@ -6378,19 +5988,16 @@ fn whileExpr( |
| 6378 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); | 5988 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); |
| 6379 | const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; | 5989 | const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; |
| 6380 | if (!continue_scope.endsWithNoReturn()) { | 5990 | if (!continue_scope.endsWithNoReturn()) { |
| 6381 | const break_inst = try continue_scope.makeBreak(break_tag, continue_block, .void_value); | 5991 | _ = try continue_scope.addBreak(break_tag, continue_block, .void_value); |
| 6382 | try then_scope.instructions.append(astgen.gpa, break_inst); | | |
| 6383 | } | 5992 | } |
| 6384 | try continue_scope.setBlockBody(continue_block); | 5993 | try continue_scope.setBlockBody(continue_block); |
| | 5994 | _ = try then_scope.addBreak(break_tag, cond_block, .void_value); |
| 6385 | | 5995 | |
| 6386 | var else_scope = parent_gz.makeSubBlock(&cond_scope.base); | 5996 | var else_scope = parent_gz.makeSubBlock(&cond_scope.base); |
| 6387 | defer else_scope.unstack(); | 5997 | defer else_scope.unstack(); |
| 6388 | | 5998 | |
| 6389 | const else_node = while_full.ast.else_expr; | 5999 | const else_node = while_full.ast.else_expr; |
| 6390 | const else_info: struct { | 6000 | if (else_node != 0) { |
| 6391 | src: Ast.Node.Index, | | |
| 6392 | result: Zir.Inst.Ref, | | |
| 6393 | } = if (else_node != 0) blk: { | | |
| 6394 | try else_scope.addDbgBlockBegin(); | 6001 | try else_scope.addDbgBlockBegin(); |
| 6395 | const sub_scope = s: { | 6002 | const sub_scope = s: { |
| 6396 | if (while_full.error_token) |error_token| { | 6003 | if (while_full.error_token) |error_token| { |
| ... | @@ -6427,45 +6034,33 @@ fn whileExpr( | ... | @@ -6427,45 +6034,33 @@ fn whileExpr( |
| 6427 | _ = try addEnsureResult(&else_scope, else_result, else_node); | 6034 | _ = try addEnsureResult(&else_scope, else_result, else_node); |
| 6428 | } | 6035 | } |
| 6429 | | 6036 | |
| 6430 | if (!else_scope.endsWithNoReturn()) { | | |
| 6431 | loop_scope.break_count += 1; | | |
| 6432 | } | | |
| 6433 | try checkUsed(parent_gz, &else_scope.base, sub_scope); | 6037 | try checkUsed(parent_gz, &else_scope.base, sub_scope); |
| 6434 | try else_scope.addDbgBlockEnd(); | 6038 | try else_scope.addDbgBlockEnd(); |
| 6435 | break :blk .{ | 6039 | if (!else_scope.endsWithNoReturn()) { |
| 6436 | .src = else_node, | 6040 | _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node); |
| 6437 | .result = else_result, | 6041 | } |
| 6438 | }; | 6042 | } else { |
| 6439 | } else .{ | 6043 | const result = try rvalue(&else_scope, ri, .void_value, node); |
| 6440 | .src = then_node, | 6044 | _ = try else_scope.addBreak(break_tag, loop_block, result); |
| 6441 | .result = .none, | 6045 | } |
| 6442 | }; | | |
| 6443 | | 6046 | |
| 6444 | if (loop_scope.label) |some| { | 6047 | if (loop_scope.label) |some| { |
| 6445 | if (!some.used) { | 6048 | if (!some.used) { |
| 6446 | try astgen.appendErrorTok(some.token, "unused while loop label", .{}); | 6049 | try astgen.appendErrorTok(some.token, "unused while loop label", .{}); |
| 6447 | } | 6050 | } |
| 6448 | } | 6051 | } |
| 6449 | const result = try finishThenElseBlock( | 6052 | |
| 6450 | parent_gz, | 6053 | try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope); |
| 6451 | ri, | 6054 | |
| 6452 | node, | 6055 | const result = if (need_result_rvalue) |
| 6453 | &loop_scope, | 6056 | try rvalue(parent_gz, ri, indexToRef(loop_block), node) |
| 6454 | &then_scope, | 6057 | else |
| 6455 | &else_scope, | 6058 | indexToRef(loop_block); |
| 6456 | condbr, | 6059 | |
| 6457 | cond.bool_bit, | | |
| 6458 | .void_value, | | |
| 6459 | then_node, | | |
| 6460 | else_info.result, | | |
| 6461 | else_info.src, | | |
| 6462 | loop_block, | | |
| 6463 | cond_block, | | |
| 6464 | break_tag, | | |
| 6465 | ); | | |
| 6466 | if (is_statement) { | 6060 | if (is_statement) { |
| 6467 | _ = try parent_gz.addUnNode(.ensure_result_used, result, node); | 6061 | _ = try parent_gz.addUnNode(.ensure_result_used, result, node); |
| 6468 | } | 6062 | } |
| | 6063 | |
| 6469 | return result; | 6064 | return result; |
| 6470 | } | 6065 | } |
| 6471 | | 6066 | |
| ... | @@ -6483,6 +6078,20 @@ fn forExpr( | ... | @@ -6483,6 +6078,20 @@ fn forExpr( |
| 6483 | try astgen.checkLabelRedefinition(scope, label_token); | 6078 | try astgen.checkLabelRedefinition(scope, label_token); |
| 6484 | } | 6079 | } |
| 6485 | | 6080 | |
| | 6081 | const need_rl = astgen.nodes_need_rl.contains(node); |
| | 6082 | const block_ri: ResultInfo = if (need_rl) ri else .{ |
| | 6083 | .rl = switch (ri.rl) { |
| | 6084 | .ptr => .{ .ty = try ri.rl.resultType(parent_gz, node, undefined) }, |
| | 6085 | .inferred_ptr => .none, |
| | 6086 | else => ri.rl, |
| | 6087 | }, |
| | 6088 | .ctx = ri.ctx, |
| | 6089 | }; |
| | 6090 | // We need to call `rvalue` to write through to the pointer only if we had a |
| | 6091 | // result pointer and aren't forwarding it. |
| | 6092 | const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?; |
| | 6093 | const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); |
| | 6094 | |
| 6486 | const is_inline = for_full.inline_token != null; | 6095 | const is_inline = for_full.inline_token != null; |
| 6487 | const tree = astgen.tree; | 6096 | const tree = astgen.tree; |
| 6488 | const token_tags = tree.tokens.items(.tag); | 6097 | const token_tags = tree.tokens.items(.tag); |
| ... | @@ -6603,9 +6212,8 @@ fn forExpr( | ... | @@ -6603,9 +6212,8 @@ fn forExpr( |
| 6603 | | 6212 | |
| 6604 | var loop_scope = parent_gz.makeSubBlock(scope); | 6213 | var loop_scope = parent_gz.makeSubBlock(scope); |
| 6605 | loop_scope.is_inline = is_inline; | 6214 | loop_scope.is_inline = is_inline; |
| 6606 | loop_scope.setBreakResultInfo(ri); | 6215 | loop_scope.setBreakResultInfo(block_ri); |
| 6607 | defer loop_scope.unstack(); | 6216 | defer loop_scope.unstack(); |
| 6608 | defer loop_scope.labeled_breaks.deinit(gpa); | | |
| 6609 | | 6217 | |
| 6610 | // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the | 6218 | // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the |
| 6611 | // load must be removed from instructions in the meantime or it appears to be part of parent_gz. | 6219 | // load must be removed from instructions in the meantime or it appears to be part of parent_gz. |
| ... | @@ -6709,14 +6317,15 @@ fn forExpr( | ... | @@ -6709,14 +6317,15 @@ fn forExpr( |
| 6709 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); | 6317 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); |
| 6710 | try then_scope.addDbgBlockEnd(); | 6318 | try then_scope.addDbgBlockEnd(); |
| 6711 | | 6319 | |
| | 6320 | const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; |
| | 6321 | |
| | 6322 | _ = try then_scope.addBreak(break_tag, cond_block, .void_value); |
| | 6323 | |
| 6712 | var else_scope = parent_gz.makeSubBlock(&cond_scope.base); | 6324 | var else_scope = parent_gz.makeSubBlock(&cond_scope.base); |
| 6713 | defer else_scope.unstack(); | 6325 | defer else_scope.unstack(); |
| 6714 | | 6326 | |
| 6715 | const else_node = for_full.ast.else_expr; | 6327 | const else_node = for_full.ast.else_expr; |
| 6716 | const else_info: struct { | 6328 | if (else_node != 0) { |
| 6717 | src: Ast.Node.Index, | | |
| 6718 | result: Zir.Inst.Ref, | | |
| 6719 | } = if (else_node != 0) blk: { | | |
| 6720 | const sub_scope = &else_scope.base; | 6329 | const sub_scope = &else_scope.base; |
| 6721 | // Remove the continue block and break block so that `continue` and `break` | 6330 | // Remove the continue block and break block so that `continue` and `break` |
| 6722 | // control flow apply to outer loops; not this one. | 6331 | // control flow apply to outer loops; not this one. |
| ... | @@ -6726,42 +6335,21 @@ fn forExpr( | ... | @@ -6726,42 +6335,21 @@ fn forExpr( |
| 6726 | if (is_statement) { | 6335 | if (is_statement) { |
| 6727 | _ = try addEnsureResult(&else_scope, else_result, else_node); | 6336 | _ = try addEnsureResult(&else_scope, else_result, else_node); |
| 6728 | } | 6337 | } |
| 6729 | | | |
| 6730 | if (!else_scope.endsWithNoReturn()) { | 6338 | if (!else_scope.endsWithNoReturn()) { |
| 6731 | loop_scope.break_count += 1; | 6339 | _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node); |
| 6732 | } | 6340 | } |
| 6733 | break :blk .{ | 6341 | } else { |
| 6734 | .src = else_node, | 6342 | const result = try rvalue(&else_scope, ri, .void_value, node); |
| 6735 | .result = else_result, | 6343 | _ = try else_scope.addBreak(break_tag, loop_block, result); |
| 6736 | }; | 6344 | } |
| 6737 | } else .{ | | |
| 6738 | .src = then_node, | | |
| 6739 | .result = .none, | | |
| 6740 | }; | | |
| 6741 | | 6345 | |
| 6742 | if (loop_scope.label) |some| { | 6346 | if (loop_scope.label) |some| { |
| 6743 | if (!some.used) { | 6347 | if (!some.used) { |
| 6744 | try astgen.appendErrorTok(some.token, "unused for loop label", .{}); | 6348 | try astgen.appendErrorTok(some.token, "unused for loop label", .{}); |
| 6745 | } | 6349 | } |
| 6746 | } | 6350 | } |
| 6747 | const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; | 6351 | |
| 6748 | const result = try finishThenElseBlock( | 6352 | try setCondBrPayload(condbr, cond, &then_scope, &else_scope); |
| 6749 | parent_gz, | | |
| 6750 | ri, | | |
| 6751 | node, | | |
| 6752 | &loop_scope, | | |
| 6753 | &then_scope, | | |
| 6754 | &else_scope, | | |
| 6755 | condbr, | | |
| 6756 | cond, | | |
| 6757 | then_result, | | |
| 6758 | then_node, | | |
| 6759 | else_info.result, | | |
| 6760 | else_info.src, | | |
| 6761 | loop_block, | | |
| 6762 | cond_block, | | |
| 6763 | break_tag, | | |
| 6764 | ); | | |
| 6765 | | 6353 | |
| 6766 | // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it | 6354 | // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it |
| 6767 | { | 6355 | { |
| ... | @@ -6780,9 +6368,11 @@ fn forExpr( | ... | @@ -6780,9 +6368,11 @@ fn forExpr( |
| 6780 | try loop_scope.setBlockBody(loop_block); | 6368 | try loop_scope.setBlockBody(loop_block); |
| 6781 | } | 6369 | } |
| 6782 | | 6370 | |
| 6783 | if (ri.rl.strategy(&loop_scope).tag == .break_void and loop_scope.break_count == 0) { | 6371 | const result = if (need_result_rvalue) |
| 6784 | _ = try rvalue(parent_gz, ri, .void_value, node); | 6372 | try rvalue(parent_gz, ri, indexToRef(loop_block), node) |
| 6785 | } | 6373 | else |
| | 6374 | indexToRef(loop_block); |
| | 6375 | |
| 6786 | if (is_statement) { | 6376 | if (is_statement) { |
| 6787 | _ = try parent_gz.addUnNode(.ensure_result_used, result, node); | 6377 | _ = try parent_gz.addUnNode(.ensure_result_used, result, node); |
| 6788 | } | 6378 | } |
| ... | @@ -6806,6 +6396,20 @@ fn switchExpr( | ... | @@ -6806,6 +6396,20 @@ fn switchExpr( |
| 6806 | const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); | 6396 | const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); |
| 6807 | const case_nodes = tree.extra_data[extra.start..extra.end]; | 6397 | const case_nodes = tree.extra_data[extra.start..extra.end]; |
| 6808 | | 6398 | |
| | 6399 | const need_rl = astgen.nodes_need_rl.contains(switch_node); |
| | 6400 | const block_ri: ResultInfo = if (need_rl) ri else .{ |
| | 6401 | .rl = switch (ri.rl) { |
| | 6402 | .ptr => .{ .ty = try ri.rl.resultType(parent_gz, switch_node, undefined) }, |
| | 6403 | .inferred_ptr => .none, |
| | 6404 | else => ri.rl, |
| | 6405 | }, |
| | 6406 | .ctx = ri.ctx, |
| | 6407 | }; |
| | 6408 | // We need to call `rvalue` to write through to the pointer only if we had a |
| | 6409 | // result pointer and aren't forwarding it. |
| | 6410 | const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?; |
| | 6411 | const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl); |
| | 6412 | |
| 6809 | // We perform two passes over the AST. This first pass is to collect information | 6413 | // We perform two passes over the AST. This first pass is to collect information |
| 6810 | // for the following variables, make note of the special prong AST node index, | 6414 | // for the following variables, make note of the special prong AST node index, |
| 6811 | // and bail out with a compile error if there are multiple special prongs present. | 6415 | // and bail out with a compile error if there are multiple special prongs present. |
| ... | @@ -6952,7 +6556,7 @@ fn switchExpr( | ... | @@ -6952,7 +6556,7 @@ fn switchExpr( |
| 6952 | var block_scope = parent_gz.makeSubBlock(scope); | 6556 | var block_scope = parent_gz.makeSubBlock(scope); |
| 6953 | // block_scope not used for collecting instructions | 6557 | // block_scope not used for collecting instructions |
| 6954 | block_scope.instructions_top = GenZir.unstacked_top; | 6558 | block_scope.instructions_top = GenZir.unstacked_top; |
| 6955 | block_scope.setBreakResultInfo(ri); | 6559 | block_scope.setBreakResultInfo(block_ri); |
| 6956 | | 6560 | |
| 6957 | // Sema expects a dbg_stmt immediately before switch_block(_ref) | 6561 | // Sema expects a dbg_stmt immediately before switch_block(_ref) |
| 6958 | try emitDbgStmt(parent_gz, operand_lc); | 6562 | try emitDbgStmt(parent_gz, operand_lc); |
| ... | @@ -6973,7 +6577,7 @@ fn switchExpr( | ... | @@ -6973,7 +6577,7 @@ fn switchExpr( |
| 6973 | .opcode = .value_placeholder, | 6577 | .opcode = .value_placeholder, |
| 6974 | .small = undefined, | 6578 | .small = undefined, |
| 6975 | .operand = undefined, | 6579 | .operand = undefined, |
| 6976 | } }, // TODO rename opcode | 6580 | } }, |
| 6977 | }); | 6581 | }); |
| 6978 | break :tag_inst inst; | 6582 | break :tag_inst inst; |
| 6979 | } else undefined; | 6583 | } else undefined; |
| ... | @@ -7122,7 +6726,6 @@ fn switchExpr( | ... | @@ -7122,7 +6726,6 @@ fn switchExpr( |
| 7122 | try checkUsed(parent_gz, &case_scope.base, sub_scope); | 6726 | try checkUsed(parent_gz, &case_scope.base, sub_scope); |
| 7123 | try case_scope.addDbgBlockEnd(); | 6727 | try case_scope.addDbgBlockEnd(); |
| 7124 | if (!parent_gz.refIsNoReturn(case_result)) { | 6728 | if (!parent_gz.refIsNoReturn(case_result)) { |
| 7125 | block_scope.break_count += 1; | | |
| 7126 | _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node); | 6729 | _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node); |
| 7127 | } | 6730 | } |
| 7128 | | 6731 | |
| ... | @@ -7195,119 +6798,33 @@ fn switchExpr( | ... | @@ -7195,119 +6798,33 @@ fn switchExpr( |
| 7195 | } | 6798 | } |
| 7196 | | 6799 | |
| 7197 | const zir_datas = astgen.instructions.items(.data); | 6800 | const zir_datas = astgen.instructions.items(.data); |
| 7198 | const zir_tags = astgen.instructions.items(.tag); | | |
| 7199 | | | |
| 7200 | zir_datas[switch_block].pl_node.payload_index = payload_index; | 6801 | zir_datas[switch_block].pl_node.payload_index = payload_index; |
| 7201 | | 6802 | |
| 7202 | const strat = ri.rl.strategy(&block_scope); | 6803 | for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| { |
| 7203 | inline for (.{ .body, .breaks }) |pass| { | 6804 | var body_len_index = start_index; |
| 7204 | for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| { | 6805 | var end_index = start_index; |
| 7205 | var body_len_index = start_index; | 6806 | const table_index = case_table_start + i; |
| 7206 | var end_index = start_index; | 6807 | if (table_index < scalar_case_table) { |
| 7207 | const table_index = case_table_start + i; | 6808 | end_index += 1; |
| 7208 | if (table_index < scalar_case_table) { | 6809 | } else if (table_index < multi_case_table) { |
| 7209 | end_index += 1; | 6810 | body_len_index += 1; |
| 7210 | } else if (table_index < multi_case_table) { | 6811 | end_index += 2; |
| 7211 | body_len_index += 1; | 6812 | } else { |
| 7212 | end_index += 2; | 6813 | body_len_index += 2; |
| 7213 | } else { | 6814 | const items_len = payloads.items[start_index]; |
| 7214 | body_len_index += 2; | 6815 | const ranges_len = payloads.items[start_index + 1]; |
| 7215 | const items_len = payloads.items[start_index]; | 6816 | end_index += 3 + items_len + 2 * ranges_len; |
| 7216 | const ranges_len = payloads.items[start_index + 1]; | | |
| 7217 | end_index += 3 + items_len + 2 * ranges_len; | | |
| 7218 | } | | |
| 7219 | | | |
| 7220 | const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]); | | |
| 7221 | end_index += prong_info.body_len; | | |
| 7222 | | | |
| 7223 | switch (strat.tag) { | | |
| 7224 | .break_operand => blk: { | | |
| 7225 | // Switch expressions return `true` for `nodeMayNeedMemoryLocation` thus | | |
| 7226 | // `elide_store_to_block_ptr_instructions` will either be true, | | |
| 7227 | // or all prongs are noreturn. | | |
| 7228 | if (!strat.elide_store_to_block_ptr_instructions) | | |
| 7229 | break :blk; | | |
| 7230 | | | |
| 7231 | // There will necessarily be a store_to_block_ptr for | | |
| 7232 | // all prongs, except for prongs that ended with a noreturn instruction. | | |
| 7233 | // Elide all the `store_to_block_ptr` instructions. | | |
| 7234 | | | |
| 7235 | // The break instructions need to have their operands coerced if the | | |
| 7236 | // switch's result location is a `ty`. In this case we overwrite the | | |
| 7237 | // `store_to_block_ptr` instruction with an `as` instruction and repurpose | | |
| 7238 | // it as the break operand. | | |
| 7239 | if (prong_info.body_len < 2) | | |
| 7240 | break :blk; | | |
| 7241 | | | |
| 7242 | var store_index = end_index - 2; | | |
| 7243 | while (true) : (store_index -= 1) switch (zir_tags[payloads.items[store_index]]) { | | |
| 7244 | .dbg_block_end, .dbg_block_begin, .dbg_stmt, .dbg_var_val, .dbg_var_ptr => {}, | | |
| 7245 | else => break, | | |
| 7246 | }; | | |
| 7247 | const store_inst = payloads.items[store_index]; | | |
| 7248 | if (zir_tags[store_inst] != .store_to_block_ptr or | | |
| 7249 | zir_datas[store_inst].bin.lhs != block_scope.rl_ptr) | | |
| 7250 | break :blk; | | |
| 7251 | const break_inst = payloads.items[end_index - 1]; | | |
| 7252 | if (block_scope.rl_ty_inst != .none) { | | |
| 7253 | if (pass == .breaks) { | | |
| 7254 | const break_data = &zir_datas[break_inst].@"break"; | | |
| 7255 | const break_src: i32 = @bitCast(astgen.extra.items[ | | |
| 7256 | break_data.payload_index + | | |
| 7257 | std.meta.fieldIndex(Zir.Inst.Break, "operand_src_node").? | | |
| 7258 | ]); | | |
| 7259 | if (break_src == Zir.Inst.Break.no_src_node) { | | |
| 7260 | zir_tags[store_inst] = .as; | | |
| 7261 | zir_datas[store_inst].bin = .{ | | |
| 7262 | .lhs = block_scope.rl_ty_inst, | | |
| 7263 | .rhs = break_data.operand, | | |
| 7264 | }; | | |
| 7265 | } else { | | |
| 7266 | zir_tags[store_inst] = .as_node; | | |
| 7267 | zir_datas[store_inst] = .{ .pl_node = .{ | | |
| 7268 | .src_node = break_src, | | |
| 7269 | .payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.As{ | | |
| 7270 | .dest_type = block_scope.rl_ty_inst, | | |
| 7271 | .operand = break_data.operand, | | |
| 7272 | }), | | |
| 7273 | } }; | | |
| 7274 | } | | |
| 7275 | break_data.operand = indexToRef(store_inst); | | |
| 7276 | } | | |
| 7277 | } else { | | |
| 7278 | if (pass == .body) { | | |
| 7279 | payloads.items[body_len_index] -= 1; | | |
| 7280 | astgen.extra.appendSliceAssumeCapacity( | | |
| 7281 | payloads.items[start_index .. end_index - 2], | | |
| 7282 | ); | | |
| 7283 | astgen.extra.appendAssumeCapacity(break_inst); | | |
| 7284 | } | | |
| 7285 | continue; | | |
| 7286 | } | | |
| 7287 | }, | | |
| 7288 | .break_void => if (pass == .breaks) { | | |
| 7289 | assert(!strat.elide_store_to_block_ptr_instructions); | | |
| 7290 | const last_inst = payloads.items[end_index - 1]; | | |
| 7291 | if (zir_tags[last_inst] == .@"break") { | | |
| 7292 | const break_data = &zir_datas[last_inst].@"break"; | | |
| 7293 | const block_inst = astgen.extra.items[ | | |
| 7294 | break_data.payload_index + | | |
| 7295 | std.meta.fieldIndex(Zir.Inst.Break, "block_inst").? | | |
| 7296 | ]; | | |
| 7297 | if (block_inst == switch_block) break_data.operand = .void_value; | | |
| 7298 | } | | |
| 7299 | }, | | |
| 7300 | } | | |
| 7301 | | | |
| 7302 | if (pass == .body) | | |
| 7303 | astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]); | | |
| 7304 | } | 6817 | } |
| | 6818 | const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]); |
| | 6819 | end_index += prong_info.body_len; |
| | 6820 | astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]); |
| 7305 | } | 6821 | } |
| 7306 | | 6822 | |
| 7307 | const block_ref = indexToRef(switch_block); | 6823 | if (need_result_rvalue) { |
| 7308 | if (strat.tag == .break_operand and strat.elide_store_to_block_ptr_instructions and ri.rl != .ref) | 6824 | return rvalue(parent_gz, ri, indexToRef(switch_block), switch_node); |
| 7309 | return rvalue(parent_gz, ri, block_ref, switch_node); | 6825 | } else { |
| 7310 | return block_ref; | 6826 | return indexToRef(switch_block); |
| | 6827 | } |
| 7311 | } | 6828 | } |
| 7312 | | 6829 | |
| 7313 | fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { | 6830 | fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref { |
| ... | @@ -7372,7 +6889,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -7372,7 +6889,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref |
| 7372 | return Zir.Inst.Ref.unreachable_value; | 6889 | return Zir.Inst.Ref.unreachable_value; |
| 7373 | } | 6890 | } |
| 7374 | | 6891 | |
| 7375 | const ri: ResultInfo = if (nodeMayNeedMemoryLocation(tree, operand_node, true)) .{ | 6892 | const ri: ResultInfo = if (astgen.nodes_need_rl.contains(node)) .{ |
| 7376 | .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } }, | 6893 | .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } }, |
| 7377 | .ctx = .@"return", | 6894 | .ctx = .@"return", |
| 7378 | } else .{ | 6895 | } else .{ |
| ... | @@ -7445,7 +6962,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -7445,7 +6962,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref |
| 7445 | try emitDbgStmt(&else_scope, ret_lc); | 6962 | try emitDbgStmt(&else_scope, ret_lc); |
| 7446 | try else_scope.addRet(ri, operand, node); | 6963 | try else_scope.addRet(ri, operand, node); |
| 7447 | | 6964 | |
| 7448 | try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0); | 6965 | try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope); |
| 7449 | | 6966 | |
| 7450 | return Zir.Inst.Ref.unreachable_value; | 6967 | return Zir.Inst.Ref.unreachable_value; |
| 7451 | }, | 6968 | }, |
| ... | @@ -8003,9 +7520,6 @@ fn as( | ... | @@ -8003,9 +7520,6 @@ fn as( |
| 8003 | .inferred_ptr => |result_ptr| { | 7520 | .inferred_ptr => |result_ptr| { |
| 8004 | return asRlPtr(gz, scope, ri, node, result_ptr, rhs, dest_type); | 7521 | return asRlPtr(gz, scope, ri, node, result_ptr, rhs, dest_type); |
| 8005 | }, | 7522 | }, |
| 8006 | .block_ptr => |block_scope| { | | |
| 8007 | return asRlPtr(gz, scope, ri, node, block_scope.rl_ptr, rhs, dest_type); | | |
| 8008 | }, | | |
| 8009 | } | 7523 | } |
| 8010 | } | 7524 | } |
| 8011 | | 7525 | |
| ... | @@ -8032,7 +7546,7 @@ fn unionInit( | ... | @@ -8032,7 +7546,7 @@ fn unionInit( |
| 8032 | } | 7546 | } |
| 8033 | | 7547 | |
| 8034 | fn asRlPtr( | 7548 | fn asRlPtr( |
| 8035 | parent_gz: *GenZir, | 7549 | gz: *GenZir, |
| 8036 | scope: *Scope, | 7550 | scope: *Scope, |
| 8037 | ri: ResultInfo, | 7551 | ri: ResultInfo, |
| 8038 | src_node: Ast.Node.Index, | 7552 | src_node: Ast.Node.Index, |
| ... | @@ -8040,11 +7554,13 @@ fn asRlPtr( | ... | @@ -8040,11 +7554,13 @@ fn asRlPtr( |
| 8040 | operand_node: Ast.Node.Index, | 7554 | operand_node: Ast.Node.Index, |
| 8041 | dest_type: Zir.Inst.Ref, | 7555 | dest_type: Zir.Inst.Ref, |
| 8042 | ) InnerError!Zir.Inst.Ref { | 7556 | ) InnerError!Zir.Inst.Ref { |
| 8043 | var as_scope = try parent_gz.makeCoercionScope(scope, dest_type, result_ptr, src_node); | 7557 | if (gz.astgen.nodes_need_rl.contains(src_node)) { |
| 8044 | defer as_scope.unstack(); | 7558 | const casted_ptr = try gz.addPlNode(.coerce_result_ptr, src_node, Zir.Inst.Bin{ .lhs = dest_type, .rhs = result_ptr }); |
| 8045 | | 7559 | return reachableExpr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = casted_ptr } } }, operand_node, src_node); |
| 8046 | const result = try reachableExpr(&as_scope, &as_scope.base, .{ .rl = .{ .block_ptr = &as_scope } }, operand_node, src_node); | 7560 | } else { |
| 8047 | return as_scope.finishCoercion(parent_gz, ri, operand_node, result, dest_type); | 7561 | const result = try reachableExpr(gz, scope, .{ .rl = .{ .ty = dest_type } }, operand_node, src_node); |
| | 7562 | return rvalue(gz, ri, result, src_node); |
| | 7563 | } |
| 8048 | } | 7564 | } |
| 8049 | | 7565 | |
| 8050 | fn bitCast( | 7566 | fn bitCast( |
| ... | @@ -9226,7 +8742,6 @@ fn callExpr( | ... | @@ -9226,7 +8742,6 @@ fn callExpr( |
| 9226 | defer arg_block.unstack(); | 8742 | defer arg_block.unstack(); |
| 9227 | | 8743 | |
| 9228 | // `call_inst` is reused to provide the param type. | 8744 | // `call_inst` is reused to provide the param type. |
| 9229 | arg_block.rl_ty_inst = call_inst; | | |
| 9230 | const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node); | 8745 | const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node); |
| 9231 | _ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node); | 8746 | _ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node); |
| 9232 | | 8747 | |
| ... | @@ -9420,250 +8935,6 @@ fn nodeIsTriviallyZero(tree: *const Ast, node: Ast.Node.Index) bool { | ... | @@ -9420,250 +8935,6 @@ fn nodeIsTriviallyZero(tree: *const Ast, node: Ast.Node.Index) bool { |
| 9420 | } | 8935 | } |
| 9421 | } | 8936 | } |
| 9422 | | 8937 | |
| 9423 | fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_res_ty: bool) bool { | | |
| 9424 | const node_tags = tree.nodes.items(.tag); | | |
| 9425 | const node_datas = tree.nodes.items(.data); | | |
| 9426 | const main_tokens = tree.nodes.items(.main_token); | | |
| 9427 | const token_tags = tree.tokens.items(.tag); | | |
| 9428 | | | |
| 9429 | var node = start_node; | | |
| 9430 | while (true) { | | |
| 9431 | switch (node_tags[node]) { | | |
| 9432 | .root, | | |
| 9433 | .@"usingnamespace", | | |
| 9434 | .test_decl, | | |
| 9435 | .switch_case, | | |
| 9436 | .switch_case_inline, | | |
| 9437 | .switch_case_one, | | |
| 9438 | .switch_case_inline_one, | | |
| 9439 | .container_field_init, | | |
| 9440 | .container_field_align, | | |
| 9441 | .container_field, | | |
| 9442 | .asm_output, | | |
| 9443 | .asm_input, | | |
| 9444 | => unreachable, | | |
| 9445 | | | |
| 9446 | .@"return", | | |
| 9447 | .@"break", | | |
| 9448 | .@"continue", | | |
| 9449 | .bit_not, | | |
| 9450 | .bool_not, | | |
| 9451 | .global_var_decl, | | |
| 9452 | .local_var_decl, | | |
| 9453 | .simple_var_decl, | | |
| 9454 | .aligned_var_decl, | | |
| 9455 | .@"defer", | | |
| 9456 | .@"errdefer", | | |
| 9457 | .address_of, | | |
| 9458 | .optional_type, | | |
| 9459 | .negation, | | |
| 9460 | .negation_wrap, | | |
| 9461 | .@"resume", | | |
| 9462 | .array_type, | | |
| 9463 | .array_type_sentinel, | | |
| 9464 | .ptr_type_aligned, | | |
| 9465 | .ptr_type_sentinel, | | |
| 9466 | .ptr_type, | | |
| 9467 | .ptr_type_bit_range, | | |
| 9468 | .@"suspend", | | |
| 9469 | .fn_proto_simple, | | |
| 9470 | .fn_proto_multi, | | |
| 9471 | .fn_proto_one, | | |
| 9472 | .fn_proto, | | |
| 9473 | .fn_decl, | | |
| 9474 | .anyframe_type, | | |
| 9475 | .anyframe_literal, | | |
| 9476 | .number_literal, | | |
| 9477 | .enum_literal, | | |
| 9478 | .string_literal, | | |
| 9479 | .multiline_string_literal, | | |
| 9480 | .char_literal, | | |
| 9481 | .unreachable_literal, | | |
| 9482 | .identifier, | | |
| 9483 | .error_set_decl, | | |
| 9484 | .container_decl, | | |
| 9485 | .container_decl_trailing, | | |
| 9486 | .container_decl_two, | | |
| 9487 | .container_decl_two_trailing, | | |
| 9488 | .container_decl_arg, | | |
| 9489 | .container_decl_arg_trailing, | | |
| 9490 | .tagged_union, | | |
| 9491 | .tagged_union_trailing, | | |
| 9492 | .tagged_union_two, | | |
| 9493 | .tagged_union_two_trailing, | | |
| 9494 | .tagged_union_enum_tag, | | |
| 9495 | .tagged_union_enum_tag_trailing, | | |
| 9496 | .@"asm", | | |
| 9497 | .asm_simple, | | |
| 9498 | .add, | | |
| 9499 | .add_wrap, | | |
| 9500 | .add_sat, | | |
| 9501 | .array_cat, | | |
| 9502 | .array_mult, | | |
| 9503 | .assign, | | |
| 9504 | .assign_bit_and, | | |
| 9505 | .assign_bit_or, | | |
| 9506 | .assign_shl, | | |
| 9507 | .assign_shl_sat, | | |
| 9508 | .assign_shr, | | |
| 9509 | .assign_bit_xor, | | |
| 9510 | .assign_div, | | |
| 9511 | .assign_sub, | | |
| 9512 | .assign_sub_wrap, | | |
| 9513 | .assign_sub_sat, | | |
| 9514 | .assign_mod, | | |
| 9515 | .assign_add, | | |
| 9516 | .assign_add_wrap, | | |
| 9517 | .assign_add_sat, | | |
| 9518 | .assign_mul, | | |
| 9519 | .assign_mul_wrap, | | |
| 9520 | .assign_mul_sat, | | |
| 9521 | .bang_equal, | | |
| 9522 | .bit_and, | | |
| 9523 | .bit_or, | | |
| 9524 | .shl, | | |
| 9525 | .shl_sat, | | |
| 9526 | .shr, | | |
| 9527 | .bit_xor, | | |
| 9528 | .bool_and, | | |
| 9529 | .bool_or, | | |
| 9530 | .div, | | |
| 9531 | .equal_equal, | | |
| 9532 | .error_union, | | |
| 9533 | .greater_or_equal, | | |
| 9534 | .greater_than, | | |
| 9535 | .less_or_equal, | | |
| 9536 | .less_than, | | |
| 9537 | .merge_error_sets, | | |
| 9538 | .mod, | | |
| 9539 | .mul, | | |
| 9540 | .mul_wrap, | | |
| 9541 | .mul_sat, | | |
| 9542 | .switch_range, | | |
| 9543 | .for_range, | | |
| 9544 | .field_access, | | |
| 9545 | .sub, | | |
| 9546 | .sub_wrap, | | |
| 9547 | .sub_sat, | | |
| 9548 | .slice, | | |
| 9549 | .slice_open, | | |
| 9550 | .slice_sentinel, | | |
| 9551 | .deref, | | |
| 9552 | .array_access, | | |
| 9553 | .error_value, | | |
| 9554 | .while_simple, // This variant cannot have an else expression. | | |
| 9555 | .while_cont, // This variant cannot have an else expression. | | |
| 9556 | .for_simple, // This variant cannot have an else expression. | | |
| 9557 | .if_simple, // This variant cannot have an else expression. | | |
| 9558 | => return false, | | |
| 9559 | | | |
| 9560 | // Forward the question to the LHS sub-expression. | | |
| 9561 | .grouped_expression, | | |
| 9562 | .@"try", | | |
| 9563 | .@"await", | | |
| 9564 | .@"comptime", | | |
| 9565 | .@"nosuspend", | | |
| 9566 | .unwrap_optional, | | |
| 9567 | => node = node_datas[node].lhs, | | |
| 9568 | | | |
| 9569 | // Forward the question to the RHS sub-expression. | | |
| 9570 | .@"catch", | | |
| 9571 | .@"orelse", | | |
| 9572 | => node = node_datas[node].rhs, | | |
| 9573 | | | |
| 9574 | // Array and struct init exprs write to result locs, but anon literals do not. | | |
| 9575 | .array_init_one, | | |
| 9576 | .array_init_one_comma, | | |
| 9577 | .struct_init_one, | | |
| 9578 | .struct_init_one_comma, | | |
| 9579 | .array_init, | | |
| 9580 | .array_init_comma, | | |
| 9581 | .struct_init, | | |
| 9582 | .struct_init_comma, | | |
| 9583 | => return have_res_ty or node_datas[node].lhs != 0, | | |
| 9584 | | | |
| 9585 | // Anon literals do not need result location. | | |
| 9586 | .array_init_dot_two, | | |
| 9587 | .array_init_dot_two_comma, | | |
| 9588 | .array_init_dot, | | |
| 9589 | .array_init_dot_comma, | | |
| 9590 | .struct_init_dot_two, | | |
| 9591 | .struct_init_dot_two_comma, | | |
| 9592 | .struct_init_dot, | | |
| 9593 | .struct_init_dot_comma, | | |
| 9594 | => return have_res_ty, | | |
| 9595 | | | |
| 9596 | // True because depending on comptime conditions, sub-expressions | | |
| 9597 | // may be the kind that need memory locations. | | |
| 9598 | .@"while", // This variant always has an else expression. | | |
| 9599 | .@"if", // This variant always has an else expression. | | |
| 9600 | .@"for", // This variant always has an else expression. | | |
| 9601 | .@"switch", | | |
| 9602 | .switch_comma, | | |
| 9603 | .async_call_one, | | |
| 9604 | .async_call_one_comma, | | |
| 9605 | .async_call, | | |
| 9606 | .async_call_comma, | | |
| 9607 | => return true, | | |
| 9608 | | | |
| 9609 | // https://github.com/ziglang/zig/issues/2765 would change this. | | |
| 9610 | .call_one, | | |
| 9611 | .call_one_comma, | | |
| 9612 | .call, | | |
| 9613 | .call_comma, | | |
| 9614 | => return false, | | |
| 9615 | | | |
| 9616 | .block_two, | | |
| 9617 | .block_two_semicolon, | | |
| 9618 | .block, | | |
| 9619 | .block_semicolon, | | |
| 9620 | => { | | |
| 9621 | const lbrace = main_tokens[node]; | | |
| 9622 | if (token_tags[lbrace - 1] == .colon) { | | |
| 9623 | // Labeled blocks may need a memory location to forward | | |
| 9624 | // to their break statements. | | |
| 9625 | return true; | | |
| 9626 | } else { | | |
| 9627 | return false; | | |
| 9628 | } | | |
| 9629 | }, | | |
| 9630 | | | |
| 9631 | .builtin_call_two, .builtin_call_two_comma => { | | |
| 9632 | const builtin_token = main_tokens[node]; | | |
| 9633 | const builtin_name = tree.tokenSlice(builtin_token); | | |
| 9634 | // If the builtin is an invalid name, we don't cause an error here; instead | | |
| 9635 | // let it pass, and the error will be "invalid builtin function" later. | | |
| 9636 | const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false; | | |
| 9637 | switch (builtin_info.needs_mem_loc) { | | |
| 9638 | .never => return false, | | |
| 9639 | .always => return true, | | |
| 9640 | .forward0 => node = node_datas[node].lhs, | | |
| 9641 | .forward1 => node = node_datas[node].rhs, | | |
| 9642 | } | | |
| 9643 | // Missing builtin arg is not a parsing error, expect an error later. | | |
| 9644 | if (node == 0) return false; | | |
| 9645 | }, | | |
| 9646 | | | |
| 9647 | .builtin_call, .builtin_call_comma => { | | |
| 9648 | const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; | | |
| 9649 | const builtin_token = main_tokens[node]; | | |
| 9650 | const builtin_name = tree.tokenSlice(builtin_token); | | |
| 9651 | // If the builtin is an invalid name, we don't cause an error here; instead | | |
| 9652 | // let it pass, and the error will be "invalid builtin function" later. | | |
| 9653 | const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false; | | |
| 9654 | switch (builtin_info.needs_mem_loc) { | | |
| 9655 | .never => return false, | | |
| 9656 | .always => return true, | | |
| 9657 | .forward0 => node = params[0], | | |
| 9658 | .forward1 => node = params[1], | | |
| 9659 | } | | |
| 9660 | // Missing builtin arg is not a parsing error, expect an error later. | | |
| 9661 | if (node == 0) return false; | | |
| 9662 | }, | | |
| 9663 | } | | |
| 9664 | } | | |
| 9665 | } | | |
| 9666 | | | |
| 9667 | fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool { | 8938 | fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool { |
| 9668 | const node_tags = tree.nodes.items(.tag); | 8939 | const node_tags = tree.nodes.items(.tag); |
| 9669 | const node_datas = tree.nodes.items(.data); | 8940 | const node_datas = tree.nodes.items(.data); |
| ... | @@ -10450,7 +9721,7 @@ fn rvalue( | ... | @@ -10450,7 +9721,7 @@ fn rvalue( |
| 10450 | .discard => { | 9721 | .discard => { |
| 10451 | // Emit a compile error for discarding error values. | 9722 | // Emit a compile error for discarding error values. |
| 10452 | _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); | 9723 | _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); |
| 10453 | return result; | 9724 | return .void_value; |
| 10454 | }, | 9725 | }, |
| 10455 | .ref => { | 9726 | .ref => { |
| 10456 | // We need a pointer but we have a value. | 9727 | // We need a pointer but we have a value. |
| ... | @@ -10561,16 +9832,11 @@ fn rvalue( | ... | @@ -10561,16 +9832,11 @@ fn rvalue( |
| 10561 | .lhs = ptr_res.inst, | 9832 | .lhs = ptr_res.inst, |
| 10562 | .rhs = result, | 9833 | .rhs = result, |
| 10563 | }); | 9834 | }); |
| 10564 | return result; | 9835 | return .void_value; |
| 10565 | }, | 9836 | }, |
| 10566 | .inferred_ptr => |alloc| { | 9837 | .inferred_ptr => |alloc| { |
| 10567 | _ = try gz.addBin(.store_to_inferred_ptr, alloc, result); | 9838 | _ = try gz.addBin(.store_to_inferred_ptr, alloc, result); |
| 10568 | return result; | 9839 | return .void_value; |
| 10569 | }, | | |
| 10570 | .block_ptr => |block_scope| { | | |
| 10571 | block_scope.rvalue_rl_count += 1; | | |
| 10572 | _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result); | | |
| 10573 | return result; | | |
| 10574 | }, | 9840 | }, |
| 10575 | } | 9841 | } |
| 10576 | } | 9842 | } |
| ... | @@ -11260,22 +10526,6 @@ const GenZir = struct { | ... | @@ -11260,22 +10526,6 @@ const GenZir = struct { |
| 11260 | continue_block: Zir.Inst.Index = 0, | 10526 | continue_block: Zir.Inst.Index = 0, |
| 11261 | /// Only valid when setBreakResultInfo is called. | 10527 | /// Only valid when setBreakResultInfo is called. |
| 11262 | break_result_info: AstGen.ResultInfo = undefined, | 10528 | break_result_info: AstGen.ResultInfo = undefined, |
| 11263 | /// When a block has a pointer result location, here it is. | | |
| 11264 | rl_ptr: Zir.Inst.Ref = .none, | | |
| 11265 | /// When a block has a type result location, here it is. | | |
| 11266 | rl_ty_inst: Zir.Inst.Ref = .none, | | |
| 11267 | /// Keeps track of how many branches of a block did not actually | | |
| 11268 | /// consume the result location. astgen uses this to figure out | | |
| 11269 | /// whether to rely on break instructions or writing to the result | | |
| 11270 | /// pointer for the result instruction. | | |
| 11271 | rvalue_rl_count: usize = 0, | | |
| 11272 | /// Keeps track of how many break instructions there are. When astgen is finished | | |
| 11273 | /// with a block, it can check this against rvalue_rl_count to find out whether | | |
| 11274 | /// the break instructions should be downgraded to break_void. | | |
| 11275 | break_count: usize = 0, | | |
| 11276 | /// Tracks `break :foo bar` instructions so they can possibly be elided later if | | |
| 11277 | /// the labeled block ends up not needing a result location pointer. | | |
| 11278 | labeled_breaks: ArrayListUnmanaged(struct { br: Zir.Inst.Index, search: Zir.Inst.Index }) = .{}, | | |
| 11279 | | 10529 | |
| 11280 | suspend_node: Ast.Node.Index = 0, | 10530 | suspend_node: Ast.Node.Index = 0, |
| 11281 | nosuspend_node: Ast.Node.Index = 0, | 10531 | nosuspend_node: Ast.Node.Index = 0, |
| ... | @@ -11327,7 +10577,6 @@ const GenZir = struct { | ... | @@ -11327,7 +10577,6 @@ const GenZir = struct { |
| 11327 | .decl_node_index = gz.decl_node_index, | 10577 | .decl_node_index = gz.decl_node_index, |
| 11328 | .decl_line = gz.decl_line, | 10578 | .decl_line = gz.decl_line, |
| 11329 | .parent = scope, | 10579 | .parent = scope, |
| 11330 | .rl_ty_inst = gz.rl_ty_inst, | | |
| 11331 | .astgen = gz.astgen, | 10580 | .astgen = gz.astgen, |
| 11332 | .suspend_node = gz.suspend_node, | 10581 | .suspend_node = gz.suspend_node, |
| 11333 | .nosuspend_node = gz.nosuspend_node, | 10582 | .nosuspend_node = gz.nosuspend_node, |
| ... | @@ -11337,67 +10586,6 @@ const GenZir = struct { | ... | @@ -11337,67 +10586,6 @@ const GenZir = struct { |
| 11337 | }; | 10586 | }; |
| 11338 | } | 10587 | } |
| 11339 | | 10588 | |
| 11340 | fn makeCoercionScope( | | |
| 11341 | parent_gz: *GenZir, | | |
| 11342 | scope: *Scope, | | |
| 11343 | dest_type: Zir.Inst.Ref, | | |
| 11344 | result_ptr: Zir.Inst.Ref, | | |
| 11345 | src_node: Ast.Node.Index, | | |
| 11346 | ) !GenZir { | | |
| 11347 | // Detect whether this expr() call goes into rvalue() to store the result into the | | |
| 11348 | // result location. If it does, elide the coerce_result_ptr instruction | | |
| 11349 | // as well as the store instruction, instead passing the result as an rvalue. | | |
| 11350 | var as_scope = parent_gz.makeSubBlock(scope); | | |
| 11351 | errdefer as_scope.unstack(); | | |
| 11352 | as_scope.rl_ptr = try as_scope.addPlNode(.coerce_result_ptr, src_node, Zir.Inst.Bin{ .lhs = dest_type, .rhs = result_ptr }); | | |
| 11353 | | | |
| 11354 | // `rl_ty_inst` needs to be set in case the stores to `rl_ptr` are eliminated. | | |
| 11355 | as_scope.rl_ty_inst = dest_type; | | |
| 11356 | | | |
| 11357 | return as_scope; | | |
| 11358 | } | | |
| 11359 | | | |
| 11360 | /// Assumes `as_scope` is stacked immediately on top of `parent_gz`. Unstacks `as_scope`. | | |
| 11361 | fn finishCoercion( | | |
| 11362 | as_scope: *GenZir, | | |
| 11363 | parent_gz: *GenZir, | | |
| 11364 | ri: ResultInfo, | | |
| 11365 | src_node: Ast.Node.Index, | | |
| 11366 | result: Zir.Inst.Ref, | | |
| 11367 | dest_type: Zir.Inst.Ref, | | |
| 11368 | ) InnerError!Zir.Inst.Ref { | | |
| 11369 | assert(as_scope.instructions == parent_gz.instructions); | | |
| 11370 | const astgen = as_scope.astgen; | | |
| 11371 | if (as_scope.rvalue_rl_count == 1) { | | |
| 11372 | // Busted! This expression didn't actually need a pointer. | | |
| 11373 | const zir_tags = astgen.instructions.items(.tag); | | |
| 11374 | const zir_datas = astgen.instructions.items(.data); | | |
| 11375 | var src: usize = as_scope.instructions_top; | | |
| 11376 | var dst: usize = src; | | |
| 11377 | while (src < as_scope.instructions.items.len) : (src += 1) { | | |
| 11378 | const src_inst = as_scope.instructions.items[src]; | | |
| 11379 | if (indexToRef(src_inst) == as_scope.rl_ptr) continue; | | |
| 11380 | if (zir_tags[src_inst] == .store_to_block_ptr) { | | |
| 11381 | if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue; | | |
| 11382 | } | | |
| 11383 | as_scope.instructions.items[dst] = src_inst; | | |
| 11384 | dst += 1; | | |
| 11385 | } | | |
| 11386 | parent_gz.instructions.items.len -= src - dst; | | |
| 11387 | as_scope.instructions_top = GenZir.unstacked_top; | | |
| 11388 | // as_scope now unstacked, can add new instructions to parent_gz | | |
| 11389 | const casted_result = try parent_gz.addPlNode(.as_node, src_node, Zir.Inst.As{ | | |
| 11390 | .dest_type = dest_type, | | |
| 11391 | .operand = result, | | |
| 11392 | }); | | |
| 11393 | return rvalue(parent_gz, ri, casted_result, src_node); | | |
| 11394 | } else { | | |
| 11395 | // implicitly move all as_scope instructions to parent_gz | | |
| 11396 | as_scope.instructions_top = GenZir.unstacked_top; | | |
| 11397 | return result; | | |
| 11398 | } | | |
| 11399 | } | | |
| 11400 | | | |
| 11401 | const Label = struct { | 10589 | const Label = struct { |
| 11402 | token: Ast.TokenIndex, | 10590 | token: Ast.TokenIndex, |
| 11403 | block_inst: Zir.Inst.Index, | 10591 | block_inst: Zir.Inst.Index, |
| ... | @@ -11438,46 +10626,20 @@ const GenZir = struct { | ... | @@ -11438,46 +10626,20 @@ const GenZir = struct { |
| 11438 | // ZIR needs to be generated. In the former case we rely on storing to the | 10626 | // ZIR needs to be generated. In the former case we rely on storing to the |
| 11439 | // pointer to communicate the result, and use breakvoid; in the latter case | 10627 | // pointer to communicate the result, and use breakvoid; in the latter case |
| 11440 | // the block break instructions will have the result values. | 10628 | // the block break instructions will have the result values. |
| 11441 | // One more complication: when the result location is a pointer, we detect | | |
| 11442 | // the scenario where the result location is not consumed. In this case | | |
| 11443 | // we emit ZIR for the block break instructions to have the result values, | | |
| 11444 | // and then rvalue() on that to pass the value to the result location. | | |
| 11445 | switch (parent_ri.rl) { | 10629 | switch (parent_ri.rl) { |
| 11446 | .coerced_ty => |ty_inst| { | 10630 | .coerced_ty => |ty_inst| { |
| 11447 | // Type coercion needs to happend before breaks. | 10631 | // Type coercion needs to happen before breaks. |
| 11448 | gz.rl_ty_inst = ty_inst; | 10632 | gz.break_result_info = .{ .rl = .{ .ty = ty_inst }, .ctx = parent_ri.ctx }; |
| 11449 | gz.break_result_info = .{ .rl = .{ .ty = ty_inst } }; | | |
| 11450 | }, | | |
| 11451 | .ty => |ty_inst| { | | |
| 11452 | gz.rl_ty_inst = ty_inst; | | |
| 11453 | gz.break_result_info = parent_ri; | | |
| 11454 | }, | 10633 | }, |
| 11455 | | | |
| 11456 | .none, .ref => { | | |
| 11457 | gz.rl_ty_inst = .none; | | |
| 11458 | gz.break_result_info = parent_ri; | | |
| 11459 | }, | | |
| 11460 | | | |
| 11461 | .discard => { | 10634 | .discard => { |
| 11462 | gz.rl_ty_inst = .none; | 10635 | // We don't forward the result context here. This prevents |
| | 10636 | // "unnecessary discard" errors from being caused by expressions |
| | 10637 | // far from the actual discard, such as a `break` from a |
| | 10638 | // discarded block. |
| 11463 | gz.break_result_info = .{ .rl = .discard }; | 10639 | gz.break_result_info = .{ .rl = .discard }; |
| 11464 | }, | 10640 | }, |
| 11465 | | 10641 | else => { |
| 11466 | .ptr => |ptr_res| { | 10642 | gz.break_result_info = parent_ri; |
| 11467 | gz.rl_ty_inst = .none; | | |
| 11468 | gz.break_result_info = .{ .rl = .{ .ptr = .{ .inst = ptr_res.inst } }, .ctx = parent_ri.ctx }; | | |
| 11469 | }, | | |
| 11470 | | | |
| 11471 | .inferred_ptr => |ptr| { | | |
| 11472 | gz.rl_ty_inst = .none; | | |
| 11473 | gz.rl_ptr = ptr; | | |
| 11474 | gz.break_result_info = .{ .rl = .{ .block_ptr = gz }, .ctx = parent_ri.ctx }; | | |
| 11475 | }, | | |
| 11476 | | | |
| 11477 | .block_ptr => |parent_block_scope| { | | |
| 11478 | gz.rl_ty_inst = parent_block_scope.rl_ty_inst; | | |
| 11479 | gz.rl_ptr = parent_block_scope.rl_ptr; | | |
| 11480 | gz.break_result_info = .{ .rl = .{ .block_ptr = gz }, .ctx = parent_ri.ctx }; | | |
| 11481 | }, | 10643 | }, |
| 11482 | } | 10644 | } |
| 11483 | } | 10645 | } |