| ... | ... | @@ -17,9 +17,13 @@ const refToIndex = Zir.refToIndex; |
| 17 | 17 | const indexToRef = Zir.indexToRef; |
| 18 | 18 | const trace = @import("tracy.zig").trace; |
| 19 | 19 | const BuiltinFn = @import("BuiltinFn.zig"); |
| 20 | const AstRlAnnotate = @import("AstRlAnnotate.zig"); |
| 20 | 21 | |
| 21 | 22 | gpa: Allocator, |
| 22 | 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 | 27 | instructions: std.MultiArrayList(Zir.Inst) = .{}, |
| 24 | 28 | extra: ArrayListUnmanaged(u32) = .{}, |
| 25 | 29 | string_bytes: ArrayListUnmanaged(u8) = .{}, |
| ... | ... | @@ -113,10 +117,14 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { |
| 113 | 117 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 114 | 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 | 123 | var astgen: AstGen = .{ |
| 117 | 124 | .gpa = gpa, |
| 118 | 125 | .arena = arena.allocator(), |
| 119 | 126 | .tree = &tree, |
| 127 | .nodes_need_rl = &nodes_need_rl, |
| 120 | 128 | }; |
| 121 | 129 | defer astgen.deinit(gpa); |
| 122 | 130 | |
| ... | ... | @@ -272,69 +280,12 @@ const ResultInfo = struct { |
| 272 | 280 | /// The result instruction from the expression must be ignored. |
| 273 | 281 | /// Always an instruction with tag `alloc_inferred`. |
| 274 | 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 | 284 | const PtrResultLoc = struct { |
| 281 | 285 | inst: Zir.Inst.Ref, |
| 282 | 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 | 289 | /// Find the result type for a cast builtin given the result location. |
| 339 | 290 | /// If the location does not have a known result type, emits an error on |
| 340 | 291 | /// the given node. |
| ... | ... | @@ -347,13 +298,6 @@ const ResultInfo = struct { |
| 347 | 298 | const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node); |
| 348 | 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 | 303 | return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{ |
| ... | ... | @@ -1516,7 +1460,7 @@ fn arrayInitExpr( |
| 1516 | 1460 | return rvalue(gz, ri, result, node); |
| 1517 | 1461 | }, |
| 1518 | 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 | 1465 | .inferred_ptr => |ptr_inst| { |
| 1522 | 1466 | if (types.array == .none) { |
| ... | ... | @@ -1526,17 +1470,8 @@ fn arrayInitExpr( |
| 1526 | 1470 | const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon); |
| 1527 | 1471 | return rvalue(gz, ri, result, node); |
| 1528 | 1472 | } else { |
| 1529 | | return arrayInitExprRlPtr(gz, scope, ri, 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); |
| 1473 | return arrayInitExprRlPtr(gz, scope, node, ptr_inst, array_init.ast.elements, types.array); |
| 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 | 1544 | fn arrayInitExprRlPtr( |
| 1610 | 1545 | gz: *GenZir, |
| 1611 | 1546 | scope: *Scope, |
| 1612 | | ri: ResultInfo, |
| 1613 | 1547 | node: Ast.Node.Index, |
| 1614 | 1548 | result_ptr: Zir.Inst.Ref, |
| 1615 | 1549 | elements: []const Ast.Node.Index, |
| ... | ... | @@ -1620,11 +1554,8 @@ fn arrayInitExprRlPtr( |
| 1620 | 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); |
| 1624 | | defer as_scope.unstack(); |
| 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); |
| 1557 | const casted_ptr = try gz.addPlNode(.coerce_result_ptr, node, Zir.Inst.Bin{ .lhs = array_ty, .rhs = result_ptr }); |
| 1558 | return arrayInitExprRlPtrInner(gz, scope, node, casted_ptr, elements); |
| 1628 | 1559 | } |
| 1629 | 1560 | |
| 1630 | 1561 | fn arrayInitExprRlPtrInner( |
| ... | ... | @@ -1759,7 +1690,7 @@ fn structInitExpr( |
| 1759 | 1690 | const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init); |
| 1760 | 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 | 1694 | .inferred_ptr => |ptr_inst| { |
| 1764 | 1695 | if (struct_init.ast.type_expr == 0) { |
| 1765 | 1696 | // We treat this case differently so that we don't get a crash when |
| ... | ... | @@ -1768,19 +1699,9 @@ fn structInitExpr( |
| 1768 | 1699 | const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon); |
| 1769 | 1700 | return rvalue(gz, ri, result, node); |
| 1770 | 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 | 1745 | fn structInitExprRlPtr( |
| 1825 | 1746 | gz: *GenZir, |
| 1826 | 1747 | scope: *Scope, |
| 1827 | | ri: ResultInfo, |
| 1828 | 1748 | node: Ast.Node.Index, |
| 1829 | 1749 | struct_init: Ast.full.StructInit, |
| 1830 | 1750 | result_ptr: Zir.Inst.Ref, |
| ... | ... | @@ -1836,11 +1756,8 @@ fn structInitExprRlPtr( |
| 1836 | 1756 | const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr); |
| 1837 | 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); |
| 1840 | | defer as_scope.unstack(); |
| 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); |
| 1759 | const casted_ptr = try gz.addPlNode(.coerce_result_ptr, node, Zir.Inst.Bin{ .lhs = ty_inst, .rhs = result_ptr }); |
| 1760 | return structInitExprRlPtrInner(gz, scope, node, struct_init, casted_ptr); |
| 1844 | 1761 | } |
| 1845 | 1762 | |
| 1846 | 1763 | fn structInitExprRlPtrInner( |
| ... | ... | @@ -2039,22 +1956,13 @@ fn restoreErrRetIndex( |
| 2039 | 1956 | .maybe => switch (ri.ctx) { |
| 2040 | 1957 | .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) { |
| 2041 | 1958 | .ptr => |ptr_res| try gz.addUnNode(.load, ptr_res.inst, node), |
| 2042 | | .inferred_ptr => |ptr| try gz.addUnNode(.load, ptr, node), |
| 2043 | | .block_ptr => |block_scope| if (block_scope.rvalue_rl_count != block_scope.break_count) b: { |
| 2044 | | // The result location may have been used by this expression, in which case |
| 2045 | | // the operand is not the result and we need to load the rl ptr. |
| 2046 | | switch (gz.astgen.instructions.items(.tag)[Zir.refToIndex(block_scope.rl_ptr).?]) { |
| 2047 | | .alloc_inferred, .alloc_inferred_mut => { |
| 2048 | | // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr |
| 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, |
| 1959 | .inferred_ptr => blk: { |
| 1960 | // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr |
| 1961 | // before its type has been resolved. There is no valid operand to use here, so error |
| 1962 | // traces will be popped prematurely. |
| 1963 | // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it. |
| 1964 | break :blk .none; |
| 1965 | }, |
| 2058 | 1966 | else => result, |
| 2059 | 1967 | }, |
| 2060 | 1968 | else => .none, // always restore/pop |
| ... | ... | @@ -2110,7 +2018,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn |
| 2110 | 2018 | else |
| 2111 | 2019 | .@"break"; |
| 2112 | 2020 | |
| 2113 | | block_gz.break_count += 1; |
| 2114 | 2021 | if (rhs == 0) { |
| 2115 | 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 | 2032 | } |
| 2126 | 2033 | |
| 2127 | 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 | 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 | 2040 | try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand); |
| 2135 | 2041 | |
| 2136 | 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 | 2043 | .ptr => { |
| 2142 | 2044 | // In this case we don't have any mechanism to intercept it; |
| 2143 | 2045 | // we assume the result location is written, and we break with void. |
| ... | ... | @@ -2346,6 +2248,20 @@ fn labeledBlockExpr( |
| 2346 | 2248 | |
| 2347 | 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 | 2265 | // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct |
| 2350 | 2266 | // so that break statements can reference it. |
| 2351 | 2267 | const block_tag: Zir.Inst.Tag = if (force_comptime) .block_comptime else .block; |
| ... | ... | @@ -2356,10 +2272,9 @@ fn labeledBlockExpr( |
| 2356 | 2272 | .token = label_token, |
| 2357 | 2273 | .block_inst = block_inst, |
| 2358 | 2274 | }; |
| 2359 | | block_scope.setBreakResultInfo(ri); |
| 2275 | block_scope.setBreakResultInfo(block_ri); |
| 2360 | 2276 | if (force_comptime) block_scope.is_comptime = true; |
| 2361 | 2277 | defer block_scope.unstack(); |
| 2362 | | defer block_scope.labeled_breaks.deinit(astgen.gpa); |
| 2363 | 2278 | |
| 2364 | 2279 | try blockExprStmts(&block_scope, &block_scope.base, statements); |
| 2365 | 2280 | if (!block_scope.endsWithNoReturn()) { |
| ... | ... | @@ -2372,75 +2287,11 @@ fn labeledBlockExpr( |
| 2372 | 2287 | try astgen.appendErrorTok(label_token, "unused block label", .{}); |
| 2373 | 2288 | } |
| 2374 | 2289 | |
| 2375 | | const zir_datas = astgen.instructions.items(.data); |
| 2376 | | const zir_tags = astgen.instructions.items(.tag); |
| 2377 | | const strat = ri.rl.strategy(&block_scope); |
| 2378 | | switch (strat.tag) { |
| 2379 | | .break_void => { |
| 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 | | }, |
| 2290 | try block_scope.setBlockBody(block_inst); |
| 2291 | if (need_result_rvalue) { |
| 2292 | return rvalue(gz, ri, indexToRef(block_inst), block_node); |
| 2293 | } else { |
| 2294 | return indexToRef(block_inst); |
| 2444 | 2295 | } |
| 2445 | 2296 | } |
| 2446 | 2297 | |
| ... | ... | @@ -2818,7 +2669,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2818 | 2669 | .atomic_store, |
| 2819 | 2670 | .store, |
| 2820 | 2671 | .store_node, |
| 2821 | | .store_to_block_ptr, |
| 2822 | 2672 | .store_to_inferred_ptr, |
| 2823 | 2673 | .resolve_inferred_alloc, |
| 2824 | 2674 | .validate_struct_init, |
| ... | ... | @@ -3137,7 +2987,7 @@ fn varDecl( |
| 3137 | 2987 | // the variable, no memory location needed. |
| 3138 | 2988 | const type_node = var_decl.ast.type_node; |
| 3139 | 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 | 2992 | const result_info: ResultInfo = if (type_node != 0) .{ |
| 3143 | 2993 | .rl = .{ .ty = try typeExpr(gz, scope, type_node) }, |
| ... | ... | @@ -3170,132 +3020,62 @@ fn varDecl( |
| 3170 | 3020 | const is_comptime = gz.is_comptime or |
| 3171 | 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 | 3023 | var resolve_inferred_alloc: Zir.Inst.Ref = .none; |
| 3182 | 3024 | var opt_type_inst: Zir.Inst.Ref = .none; |
| 3183 | | if (type_node != 0) { |
| 3184 | | const type_inst = try typeExpr(gz, &init_scope.base, type_node); |
| 3025 | const init_rl: ResultInfo.Loc = if (type_node != 0) init_rl: { |
| 3026 | const type_inst = try typeExpr(gz, scope, type_node); |
| 3185 | 3027 | opt_type_inst = type_inst; |
| 3186 | 3028 | if (align_inst == .none) { |
| 3187 | | init_scope.instructions_top = gz.instructions.items.len; |
| 3188 | | init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node); |
| 3029 | break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } }; |
| 3189 | 3030 | } else { |
| 3190 | | init_scope.rl_ptr = try gz.addAllocExtended(.{ |
| 3031 | break :init_rl .{ .ptr = .{ .inst = try gz.addAllocExtended(.{ |
| 3191 | 3032 | .node = node, |
| 3192 | 3033 | .type_inst = type_inst, |
| 3193 | 3034 | .align_inst = align_inst, |
| 3194 | 3035 | .is_const = true, |
| 3195 | 3036 | .is_comptime = is_comptime, |
| 3196 | | }); |
| 3197 | | init_scope.instructions_top = gz.instructions.items.len; |
| 3037 | }) } }; |
| 3198 | 3038 | } |
| 3199 | | init_scope.rl_ty_inst = type_inst; |
| 3200 | | } else { |
| 3201 | | const alloc = if (align_inst == .none) alloc: { |
| 3202 | | init_scope.instructions_top = gz.instructions.items.len; |
| 3039 | } else init_rl: { |
| 3040 | const alloc_inst = if (align_inst == .none) ptr: { |
| 3203 | 3041 | const tag: Zir.Inst.Tag = if (is_comptime) |
| 3204 | 3042 | .alloc_inferred_comptime |
| 3205 | 3043 | else |
| 3206 | 3044 | .alloc_inferred; |
| 3207 | | break :alloc try init_scope.addNode(tag, node); |
| 3208 | | } else alloc: { |
| 3209 | | const ref = try gz.addAllocExtended(.{ |
| 3045 | break :ptr try gz.addNode(tag, node); |
| 3046 | } else ptr: { |
| 3047 | break :ptr try gz.addAllocExtended(.{ |
| 3210 | 3048 | .node = node, |
| 3211 | 3049 | .type_inst = .none, |
| 3212 | 3050 | .align_inst = align_inst, |
| 3213 | 3051 | .is_const = true, |
| 3214 | 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; |
| 3220 | | init_scope.rl_ptr = alloc; |
| 3221 | | init_scope.rl_ty_inst = .none; |
| 3222 | | } |
| 3223 | | const init_result_info: ResultInfo = .{ .rl = .{ .block_ptr = &init_scope }, .ctx = .const_init }; |
| 3224 | | const init_inst = try reachableExpr(&init_scope, &init_scope.base, init_result_info, var_decl.ast.init_node, node); |
| 3055 | resolve_inferred_alloc = alloc_inst; |
| 3056 | break :init_rl .{ .inferred_ptr = alloc_inst }; |
| 3057 | }; |
| 3058 | const var_ptr = switch (init_rl) { |
| 3059 | .ptr => |ptr| ptr.inst, |
| 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 | 3070 | // The const init expression may have modified the error return trace, so signal |
| 3227 | 3071 | // to Sema that it should save the new index for restoring later. |
| 3228 | 3072 | if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node)) |
| 3229 | | _ = try init_scope.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); |
| 3073 | _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }); |
| 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 | 3075 | if (resolve_inferred_alloc != .none) { |
| 3296 | 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 | 3080 | try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr); |
| 3301 | 3081 | |
| ... | ... | @@ -3312,9 +3092,6 @@ fn varDecl( |
| 3312 | 3092 | return &sub_scope.base; |
| 3313 | 3093 | }, |
| 3314 | 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 | 3095 | const is_comptime = var_decl.comptime_token != null or gz.is_comptime; |
| 3319 | 3096 | var resolve_inferred_alloc: Zir.Inst.Ref = .none; |
| 3320 | 3097 | const var_data: struct { |
| ... | ... | @@ -3339,7 +3116,6 @@ fn varDecl( |
| 3339 | 3116 | }); |
| 3340 | 3117 | } |
| 3341 | 3118 | }; |
| 3342 | | gz.rl_ty_inst = type_inst; |
| 3343 | 3119 | break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .ptr = .{ .inst = alloc } } } }; |
| 3344 | 3120 | } else a: { |
| 3345 | 3121 | const alloc = alloc: { |
| ... | ... | @@ -3359,7 +3135,6 @@ fn varDecl( |
| 3359 | 3135 | }); |
| 3360 | 3136 | } |
| 3361 | 3137 | }; |
| 3362 | | gz.rl_ty_inst = .none; |
| 3363 | 3138 | resolve_inferred_alloc = alloc; |
| 3364 | 3139 | break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .inferred_ptr = alloc } } }; |
| 3365 | 3140 | }; |
| ... | ... | @@ -5530,17 +5305,30 @@ fn orelseCatchExpr( |
| 5530 | 5305 | const astgen = parent_gz.astgen; |
| 5531 | 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 | 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 | 5324 | var block_scope = parent_gz.makeSubBlock(scope); |
| 5536 | | block_scope.setBreakResultInfo(ri); |
| 5325 | block_scope.setBreakResultInfo(block_ri); |
| 5537 | 5326 | defer block_scope.unstack(); |
| 5538 | 5327 | |
| 5539 | 5328 | const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) { |
| 5540 | 5329 | .ref => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none }, |
| 5541 | 5330 | else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none }, |
| 5542 | 5331 | }; |
| 5543 | | block_scope.break_count += 1; |
| 5544 | 5332 | // This could be a pointer or value depending on the `operand_ri` parameter. |
| 5545 | 5333 | // We cannot use `block_scope.break_result_info` because that has the bare |
| 5546 | 5334 | // type, whereas this expression has the optional type. Later we make |
| ... | ... | @@ -5563,6 +5351,7 @@ fn orelseCatchExpr( |
| 5563 | 5351 | .ref => unwrapped_payload, |
| 5564 | 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 | 5356 | var else_scope = block_scope.makeSubBlock(scope); |
| 5568 | 5357 | defer else_scope.unstack(); |
| ... | ... | @@ -5596,93 +5385,20 @@ fn orelseCatchExpr( |
| 5596 | 5385 | |
| 5597 | 5386 | const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs); |
| 5598 | 5387 | if (!else_scope.endsWithNoReturn()) { |
| 5599 | | block_scope.break_count += 1; |
| 5600 | | |
| 5601 | 5388 | // As our last action before the break, "pop" the error trace if needed |
| 5602 | 5389 | if (do_err_trace) |
| 5603 | 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 | 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 |
| 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 | | } |
| 5396 | try setCondBrPayload(condbr, cond, &then_scope, &else_scope); |
| 5630 | 5397 | |
| 5631 | | /// Supports `else_scope` stacked on `then_scope` stacked on `block_scope`. Unstacks `else_scope` then `then_scope`. |
| 5632 | | fn finishThenElseBlock( |
| 5633 | | parent_gz: *GenZir, |
| 5634 | | ri: ResultInfo, |
| 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 | | }, |
| 5398 | if (need_result_rvalue) { |
| 5399 | return rvalue(parent_gz, ri, indexToRef(block), node); |
| 5400 | } else { |
| 5401 | return indexToRef(block); |
| 5686 | 5402 | } |
| 5687 | 5403 | } |
| 5688 | 5404 | |
| ... | ... | @@ -5858,8 +5574,22 @@ fn ifExpr( |
| 5858 | 5574 | |
| 5859 | 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 | 5591 | var block_scope = parent_gz.makeSubBlock(scope); |
| 5862 | | block_scope.setBreakResultInfo(ri); |
| 5592 | block_scope.setBreakResultInfo(block_ri); |
| 5863 | 5593 | defer block_scope.unstack(); |
| 5864 | 5594 | |
| 5865 | 5595 | const payload_is_ref = if (if_full.payload_token) |payload_token| |
| ... | ... | @@ -5967,14 +5697,11 @@ fn ifExpr( |
| 5967 | 5697 | }; |
| 5968 | 5698 | |
| 5969 | 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 | 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 | 5706 | var else_scope = parent_gz.makeSubBlock(scope); |
| 5980 | 5707 | defer else_scope.unstack(); |
| ... | ... | @@ -5985,10 +5712,7 @@ fn ifExpr( |
| 5985 | 5712 | _ = try else_scope.addSaveErrRetIndex(.always); |
| 5986 | 5713 | |
| 5987 | 5714 | const else_node = if_full.ast.else_expr; |
| 5988 | | const else_info: struct { |
| 5989 | | src: Ast.Node.Index, |
| 5990 | | result: Zir.Inst.Ref, |
| 5991 | | } = if (else_node != 0) blk: { |
| 5715 | if (else_node != 0) { |
| 5992 | 5716 | try else_scope.addDbgBlockBegin(); |
| 5993 | 5717 | const sub_scope = s: { |
| 5994 | 5718 | if (if_full.error_token) |error_token| { |
| ... | ... | @@ -6016,47 +5740,27 @@ fn ifExpr( |
| 6016 | 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 | 5745 | if (!else_scope.endsWithNoReturn()) { |
| 6021 | | block_scope.break_count += 1; |
| 6022 | | |
| 6023 | 5746 | // As our last action before the break, "pop" the error trace if needed |
| 6024 | 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 | 5751 | try checkUsed(parent_gz, &else_scope.base, sub_scope); |
| 6028 | | try else_scope.addDbgBlockEnd(); |
| 6029 | | break :blk .{ |
| 6030 | | .src = else_node, |
| 6031 | | .result = e, |
| 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 | | }; |
| 5752 | } else { |
| 5753 | const result = try rvalue(&else_scope, ri, .void_value, node); |
| 5754 | _ = try else_scope.addBreak(.@"break", block, result); |
| 5755 | } |
| 6041 | 5756 | |
| 6042 | | const result = try finishThenElseBlock( |
| 6043 | | parent_gz, |
| 6044 | | ri, |
| 6045 | | node, |
| 6046 | | &block_scope, |
| 6047 | | &then_scope, |
| 6048 | | &else_scope, |
| 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; |
| 5757 | try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope); |
| 5758 | |
| 5759 | if (need_result_rvalue) { |
| 5760 | return rvalue(parent_gz, ri, indexToRef(block), node); |
| 5761 | } else { |
| 5762 | return indexToRef(block); |
| 5763 | } |
| 6060 | 5764 | } |
| 6061 | 5765 | |
| 6062 | 5766 | /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`. |
| ... | ... | @@ -6064,17 +5768,15 @@ fn setCondBrPayload( |
| 6064 | 5768 | condbr: Zir.Inst.Index, |
| 6065 | 5769 | cond: Zir.Inst.Ref, |
| 6066 | 5770 | then_scope: *GenZir, |
| 6067 | | then_break: Zir.Inst.Index, |
| 6068 | 5771 | else_scope: *GenZir, |
| 6069 | | else_break: Zir.Inst.Index, |
| 6070 | 5772 | ) !void { |
| 6071 | 5773 | defer then_scope.unstack(); |
| 6072 | 5774 | defer else_scope.unstack(); |
| 6073 | 5775 | const astgen = then_scope.astgen; |
| 6074 | 5776 | const then_body = then_scope.instructionsSliceUpto(else_scope); |
| 6075 | 5777 | const else_body = else_scope.instructionsSlice(); |
| 6076 | | const then_body_len = astgen.countBodyLenAfterFixups(then_body) + @intFromBool(then_break != 0); |
| 6077 | | const else_body_len = astgen.countBodyLenAfterFixups(else_body) + @intFromBool(else_break != 0); |
| 5778 | const then_body_len = astgen.countBodyLenAfterFixups(then_body); |
| 5779 | const else_body_len = astgen.countBodyLenAfterFixups(else_body); |
| 6078 | 5780 | try astgen.extra.ensureUnusedCapacity( |
| 6079 | 5781 | astgen.gpa, |
| 6080 | 5782 | @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len, |
| ... | ... | @@ -6087,112 +5789,7 @@ fn setCondBrPayload( |
| 6087 | 5789 | .else_body_len = else_body_len, |
| 6088 | 5790 | }); |
| 6089 | 5791 | astgen.appendBodyWithFixups(then_body); |
| 6090 | | if (then_break != 0) astgen.extra.appendAssumeCapacity(then_break); |
| 6091 | 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 | 5795 | fn whileExpr( |
| ... | ... | @@ -6207,6 +5804,20 @@ fn whileExpr( |
| 6207 | 5804 | const tree = astgen.tree; |
| 6208 | 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 | 5821 | if (while_full.label_token) |label_token| { |
| 6211 | 5822 | try astgen.checkLabelRedefinition(scope, label_token); |
| 6212 | 5823 | } |
| ... | ... | @@ -6218,9 +5829,8 @@ fn whileExpr( |
| 6218 | 5829 | |
| 6219 | 5830 | var loop_scope = parent_gz.makeSubBlock(scope); |
| 6220 | 5831 | loop_scope.is_inline = is_inline; |
| 6221 | | loop_scope.setBreakResultInfo(ri); |
| 5832 | loop_scope.setBreakResultInfo(block_ri); |
| 6222 | 5833 | defer loop_scope.unstack(); |
| 6223 | | defer loop_scope.labeled_breaks.deinit(astgen.gpa); |
| 6224 | 5834 | |
| 6225 | 5835 | var cond_scope = parent_gz.makeSubBlock(&loop_scope.base); |
| 6226 | 5836 | defer cond_scope.unstack(); |
| ... | ... | @@ -6378,19 +5988,16 @@ fn whileExpr( |
| 6378 | 5988 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); |
| 6379 | 5989 | const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break"; |
| 6380 | 5990 | if (!continue_scope.endsWithNoReturn()) { |
| 6381 | | const break_inst = try continue_scope.makeBreak(break_tag, continue_block, .void_value); |
| 6382 | | try then_scope.instructions.append(astgen.gpa, break_inst); |
| 5991 | _ = try continue_scope.addBreak(break_tag, continue_block, .void_value); |
| 6383 | 5992 | } |
| 6384 | 5993 | try continue_scope.setBlockBody(continue_block); |
| 5994 | _ = try then_scope.addBreak(break_tag, cond_block, .void_value); |
| 6385 | 5995 | |
| 6386 | 5996 | var else_scope = parent_gz.makeSubBlock(&cond_scope.base); |
| 6387 | 5997 | defer else_scope.unstack(); |
| 6388 | 5998 | |
| 6389 | 5999 | const else_node = while_full.ast.else_expr; |
| 6390 | | const else_info: struct { |
| 6391 | | src: Ast.Node.Index, |
| 6392 | | result: Zir.Inst.Ref, |
| 6393 | | } = if (else_node != 0) blk: { |
| 6000 | if (else_node != 0) { |
| 6394 | 6001 | try else_scope.addDbgBlockBegin(); |
| 6395 | 6002 | const sub_scope = s: { |
| 6396 | 6003 | if (while_full.error_token) |error_token| { |
| ... | ... | @@ -6427,45 +6034,33 @@ fn whileExpr( |
| 6427 | 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 | 6037 | try checkUsed(parent_gz, &else_scope.base, sub_scope); |
| 6434 | 6038 | try else_scope.addDbgBlockEnd(); |
| 6435 | | break :blk .{ |
| 6436 | | .src = else_node, |
| 6437 | | .result = else_result, |
| 6438 | | }; |
| 6439 | | } else .{ |
| 6440 | | .src = then_node, |
| 6441 | | .result = .none, |
| 6442 | | }; |
| 6039 | if (!else_scope.endsWithNoReturn()) { |
| 6040 | _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node); |
| 6041 | } |
| 6042 | } else { |
| 6043 | const result = try rvalue(&else_scope, ri, .void_value, node); |
| 6044 | _ = try else_scope.addBreak(break_tag, loop_block, result); |
| 6045 | } |
| 6443 | 6046 | |
| 6444 | 6047 | if (loop_scope.label) |some| { |
| 6445 | 6048 | if (!some.used) { |
| 6446 | 6049 | try astgen.appendErrorTok(some.token, "unused while loop label", .{}); |
| 6447 | 6050 | } |
| 6448 | 6051 | } |
| 6449 | | const result = try finishThenElseBlock( |
| 6450 | | parent_gz, |
| 6451 | | ri, |
| 6452 | | node, |
| 6453 | | &loop_scope, |
| 6454 | | &then_scope, |
| 6455 | | &else_scope, |
| 6456 | | condbr, |
| 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 | | ); |
| 6052 | |
| 6053 | try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope); |
| 6054 | |
| 6055 | const result = if (need_result_rvalue) |
| 6056 | try rvalue(parent_gz, ri, indexToRef(loop_block), node) |
| 6057 | else |
| 6058 | indexToRef(loop_block); |
| 6059 | |
| 6466 | 6060 | if (is_statement) { |
| 6467 | 6061 | _ = try parent_gz.addUnNode(.ensure_result_used, result, node); |
| 6468 | 6062 | } |
| 6063 | |
| 6469 | 6064 | return result; |
| 6470 | 6065 | } |
| 6471 | 6066 | |
| ... | ... | @@ -6483,6 +6078,20 @@ fn forExpr( |
| 6483 | 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 | 6095 | const is_inline = for_full.inline_token != null; |
| 6487 | 6096 | const tree = astgen.tree; |
| 6488 | 6097 | const token_tags = tree.tokens.items(.tag); |
| ... | ... | @@ -6603,9 +6212,8 @@ fn forExpr( |
| 6603 | 6212 | |
| 6604 | 6213 | var loop_scope = parent_gz.makeSubBlock(scope); |
| 6605 | 6214 | loop_scope.is_inline = is_inline; |
| 6606 | | loop_scope.setBreakResultInfo(ri); |
| 6215 | loop_scope.setBreakResultInfo(block_ri); |
| 6607 | 6216 | defer loop_scope.unstack(); |
| 6608 | | defer loop_scope.labeled_breaks.deinit(gpa); |
| 6609 | 6217 | |
| 6610 | 6218 | // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the |
| 6611 | 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 | 6317 | try checkUsed(parent_gz, &then_scope.base, then_sub_scope); |
| 6710 | 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 | 6324 | var else_scope = parent_gz.makeSubBlock(&cond_scope.base); |
| 6713 | 6325 | defer else_scope.unstack(); |
| 6714 | 6326 | |
| 6715 | 6327 | const else_node = for_full.ast.else_expr; |
| 6716 | | const else_info: struct { |
| 6717 | | src: Ast.Node.Index, |
| 6718 | | result: Zir.Inst.Ref, |
| 6719 | | } = if (else_node != 0) blk: { |
| 6328 | if (else_node != 0) { |
| 6720 | 6329 | const sub_scope = &else_scope.base; |
| 6721 | 6330 | // Remove the continue block and break block so that `continue` and `break` |
| 6722 | 6331 | // control flow apply to outer loops; not this one. |
| ... | ... | @@ -6726,42 +6335,21 @@ fn forExpr( |
| 6726 | 6335 | if (is_statement) { |
| 6727 | 6336 | _ = try addEnsureResult(&else_scope, else_result, else_node); |
| 6728 | 6337 | } |
| 6729 | | |
| 6730 | 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 .{ |
| 6734 | | .src = else_node, |
| 6735 | | .result = else_result, |
| 6736 | | }; |
| 6737 | | } else .{ |
| 6738 | | .src = then_node, |
| 6739 | | .result = .none, |
| 6740 | | }; |
| 6341 | } else { |
| 6342 | const result = try rvalue(&else_scope, ri, .void_value, node); |
| 6343 | _ = try else_scope.addBreak(break_tag, loop_block, result); |
| 6344 | } |
| 6741 | 6345 | |
| 6742 | 6346 | if (loop_scope.label) |some| { |
| 6743 | 6347 | if (!some.used) { |
| 6744 | 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"; |
| 6748 | | const result = try finishThenElseBlock( |
| 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 | | ); |
| 6351 | |
| 6352 | try setCondBrPayload(condbr, cond, &then_scope, &else_scope); |
| 6765 | 6353 | |
| 6766 | 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 | 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) { |
| 6784 | | _ = try rvalue(parent_gz, ri, .void_value, node); |
| 6785 | | } |
| 6371 | const result = if (need_result_rvalue) |
| 6372 | try rvalue(parent_gz, ri, indexToRef(loop_block), node) |
| 6373 | else |
| 6374 | indexToRef(loop_block); |
| 6375 | |
| 6786 | 6376 | if (is_statement) { |
| 6787 | 6377 | _ = try parent_gz.addUnNode(.ensure_result_used, result, node); |
| 6788 | 6378 | } |
| ... | ... | @@ -6806,6 +6396,20 @@ fn switchExpr( |
| 6806 | 6396 | const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); |
| 6807 | 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 | 6413 | // We perform two passes over the AST. This first pass is to collect information |
| 6810 | 6414 | // for the following variables, make note of the special prong AST node index, |
| 6811 | 6415 | // and bail out with a compile error if there are multiple special prongs present. |
| ... | ... | @@ -6952,7 +6556,7 @@ fn switchExpr( |
| 6952 | 6556 | var block_scope = parent_gz.makeSubBlock(scope); |
| 6953 | 6557 | // block_scope not used for collecting instructions |
| 6954 | 6558 | block_scope.instructions_top = GenZir.unstacked_top; |
| 6955 | | block_scope.setBreakResultInfo(ri); |
| 6559 | block_scope.setBreakResultInfo(block_ri); |
| 6956 | 6560 | |
| 6957 | 6561 | // Sema expects a dbg_stmt immediately before switch_block(_ref) |
| 6958 | 6562 | try emitDbgStmt(parent_gz, operand_lc); |
| ... | ... | @@ -6973,7 +6577,7 @@ fn switchExpr( |
| 6973 | 6577 | .opcode = .value_placeholder, |
| 6974 | 6578 | .small = undefined, |
| 6975 | 6579 | .operand = undefined, |
| 6976 | | } }, // TODO rename opcode |
| 6580 | } }, |
| 6977 | 6581 | }); |
| 6978 | 6582 | break :tag_inst inst; |
| 6979 | 6583 | } else undefined; |
| ... | ... | @@ -7122,7 +6726,6 @@ fn switchExpr( |
| 7122 | 6726 | try checkUsed(parent_gz, &case_scope.base, sub_scope); |
| 7123 | 6727 | try case_scope.addDbgBlockEnd(); |
| 7124 | 6728 | if (!parent_gz.refIsNoReturn(case_result)) { |
| 7125 | | block_scope.break_count += 1; |
| 7126 | 6729 | _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node); |
| 7127 | 6730 | } |
| 7128 | 6731 | |
| ... | ... | @@ -7195,119 +6798,33 @@ fn switchExpr( |
| 7195 | 6798 | } |
| 7196 | 6799 | |
| 7197 | 6800 | const zir_datas = astgen.instructions.items(.data); |
| 7198 | | const zir_tags = astgen.instructions.items(.tag); |
| 7199 | | |
| 7200 | 6801 | zir_datas[switch_block].pl_node.payload_index = payload_index; |
| 7201 | 6802 | |
| 7202 | | const strat = ri.rl.strategy(&block_scope); |
| 7203 | | inline for (.{ .body, .breaks }) |pass| { |
| 7204 | | for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| { |
| 7205 | | var body_len_index = start_index; |
| 7206 | | var end_index = start_index; |
| 7207 | | const table_index = case_table_start + i; |
| 7208 | | if (table_index < scalar_case_table) { |
| 7209 | | end_index += 1; |
| 7210 | | } else if (table_index < multi_case_table) { |
| 7211 | | body_len_index += 1; |
| 7212 | | end_index += 2; |
| 7213 | | } else { |
| 7214 | | body_len_index += 2; |
| 7215 | | const items_len = payloads.items[start_index]; |
| 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]); |
| 6803 | for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| { |
| 6804 | var body_len_index = start_index; |
| 6805 | var end_index = start_index; |
| 6806 | const table_index = case_table_start + i; |
| 6807 | if (table_index < scalar_case_table) { |
| 6808 | end_index += 1; |
| 6809 | } else if (table_index < multi_case_table) { |
| 6810 | body_len_index += 1; |
| 6811 | end_index += 2; |
| 6812 | } else { |
| 6813 | body_len_index += 2; |
| 6814 | const items_len = payloads.items[start_index]; |
| 6815 | const ranges_len = payloads.items[start_index + 1]; |
| 6816 | end_index += 3 + items_len + 2 * ranges_len; |
| 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); |
| 7308 | | if (strat.tag == .break_operand and strat.elide_store_to_block_ptr_instructions and ri.rl != .ref) |
| 7309 | | return rvalue(parent_gz, ri, block_ref, switch_node); |
| 7310 | | return block_ref; |
| 6823 | if (need_result_rvalue) { |
| 6824 | return rvalue(parent_gz, ri, indexToRef(switch_block), switch_node); |
| 6825 | } else { |
| 6826 | return indexToRef(switch_block); |
| 6827 | } |
| 7311 | 6828 | } |
| 7312 | 6829 | |
| 7313 | 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 | 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 | 6893 | .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } }, |
| 7377 | 6894 | .ctx = .@"return", |
| 7378 | 6895 | } else .{ |
| ... | ... | @@ -7445,7 +6962,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref |
| 7445 | 6962 | try emitDbgStmt(&else_scope, ret_lc); |
| 7446 | 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 | 6967 | return Zir.Inst.Ref.unreachable_value; |
| 7451 | 6968 | }, |
| ... | ... | @@ -8003,9 +7520,6 @@ fn as( |
| 8003 | 7520 | .inferred_ptr => |result_ptr| { |
| 8004 | 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 | 7546 | } |
| 8033 | 7547 | |
| 8034 | 7548 | fn asRlPtr( |
| 8035 | | parent_gz: *GenZir, |
| 7549 | gz: *GenZir, |
| 8036 | 7550 | scope: *Scope, |
| 8037 | 7551 | ri: ResultInfo, |
| 8038 | 7552 | src_node: Ast.Node.Index, |
| ... | ... | @@ -8040,11 +7554,13 @@ fn asRlPtr( |
| 8040 | 7554 | operand_node: Ast.Node.Index, |
| 8041 | 7555 | dest_type: Zir.Inst.Ref, |
| 8042 | 7556 | ) InnerError!Zir.Inst.Ref { |
| 8043 | | var as_scope = try parent_gz.makeCoercionScope(scope, dest_type, result_ptr, src_node); |
| 8044 | | defer as_scope.unstack(); |
| 8045 | | |
| 8046 | | const result = try reachableExpr(&as_scope, &as_scope.base, .{ .rl = .{ .block_ptr = &as_scope } }, operand_node, src_node); |
| 8047 | | return as_scope.finishCoercion(parent_gz, ri, operand_node, result, dest_type); |
| 7557 | if (gz.astgen.nodes_need_rl.contains(src_node)) { |
| 7558 | const casted_ptr = try gz.addPlNode(.coerce_result_ptr, src_node, Zir.Inst.Bin{ .lhs = dest_type, .rhs = result_ptr }); |
| 7559 | return reachableExpr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = casted_ptr } } }, operand_node, src_node); |
| 7560 | } else { |
| 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 | 7566 | fn bitCast( |
| ... | ... | @@ -9226,7 +8742,6 @@ fn callExpr( |
| 9226 | 8742 | defer arg_block.unstack(); |
| 9227 | 8743 | |
| 9228 | 8744 | // `call_inst` is reused to provide the param type. |
| 9229 | | arg_block.rl_ty_inst = call_inst; |
| 9230 | 8745 | const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node); |
| 9231 | 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 | 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 | 8938 | fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool { |
| 9668 | 8939 | const node_tags = tree.nodes.items(.tag); |
| 9669 | 8940 | const node_datas = tree.nodes.items(.data); |
| ... | ... | @@ -10450,7 +9721,7 @@ fn rvalue( |
| 10450 | 9721 | .discard => { |
| 10451 | 9722 | // Emit a compile error for discarding error values. |
| 10452 | 9723 | _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); |
| 10453 | | return result; |
| 9724 | return .void_value; |
| 10454 | 9725 | }, |
| 10455 | 9726 | .ref => { |
| 10456 | 9727 | // We need a pointer but we have a value. |
| ... | ... | @@ -10561,16 +9832,11 @@ fn rvalue( |
| 10561 | 9832 | .lhs = ptr_res.inst, |
| 10562 | 9833 | .rhs = result, |
| 10563 | 9834 | }); |
| 10564 | | return result; |
| 9835 | return .void_value; |
| 10565 | 9836 | }, |
| 10566 | 9837 | .inferred_ptr => |alloc| { |
| 10567 | 9838 | _ = try gz.addBin(.store_to_inferred_ptr, alloc, result); |
| 10568 | | return result; |
| 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; |
| 9839 | return .void_value; |
| 10574 | 9840 | }, |
| 10575 | 9841 | } |
| 10576 | 9842 | } |
| ... | ... | @@ -11260,22 +10526,6 @@ const GenZir = struct { |
| 11260 | 10526 | continue_block: Zir.Inst.Index = 0, |
| 11261 | 10527 | /// Only valid when setBreakResultInfo is called. |
| 11262 | 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 | 10530 | suspend_node: Ast.Node.Index = 0, |
| 11281 | 10531 | nosuspend_node: Ast.Node.Index = 0, |
| ... | ... | @@ -11327,7 +10577,6 @@ const GenZir = struct { |
| 11327 | 10577 | .decl_node_index = gz.decl_node_index, |
| 11328 | 10578 | .decl_line = gz.decl_line, |
| 11329 | 10579 | .parent = scope, |
| 11330 | | .rl_ty_inst = gz.rl_ty_inst, |
| 11331 | 10580 | .astgen = gz.astgen, |
| 11332 | 10581 | .suspend_node = gz.suspend_node, |
| 11333 | 10582 | .nosuspend_node = gz.nosuspend_node, |
| ... | ... | @@ -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 | 10589 | const Label = struct { |
| 11402 | 10590 | token: Ast.TokenIndex, |
| 11403 | 10591 | block_inst: Zir.Inst.Index, |
| ... | ... | @@ -11438,46 +10626,20 @@ const GenZir = struct { |
| 11438 | 10626 | // ZIR needs to be generated. In the former case we rely on storing to the |
| 11439 | 10627 | // pointer to communicate the result, and use breakvoid; in the latter case |
| 11440 | 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 | 10629 | switch (parent_ri.rl) { |
| 11446 | 10630 | .coerced_ty => |ty_inst| { |
| 11447 | | // Type coercion needs to happend before breaks. |
| 11448 | | gz.rl_ty_inst = ty_inst; |
| 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; |
| 10631 | // Type coercion needs to happen before breaks. |
| 10632 | gz.break_result_info = .{ .rl = .{ .ty = ty_inst }, .ctx = parent_ri.ctx }; |
| 11454 | 10633 | }, |
| 11455 | | |
| 11456 | | .none, .ref => { |
| 11457 | | gz.rl_ty_inst = .none; |
| 11458 | | gz.break_result_info = parent_ri; |
| 11459 | | }, |
| 11460 | | |
| 11461 | 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 | 10639 | gz.break_result_info = .{ .rl = .discard }; |
| 11464 | 10640 | }, |
| 11465 | | |
| 11466 | | .ptr => |ptr_res| { |
| 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 }; |
| 10641 | else => { |
| 10642 | gz.break_result_info = parent_ri; |
| 11481 | 10643 | }, |
| 11482 | 10644 | } |
| 11483 | 10645 | } |