authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-08-18 15:11:38+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-08-20 11:58:14-07:00
log321961d860b03967358b5602e0e7832364e1e11c
tree33179c402d474ddf18f61ca8b301df072678e09d
parent020105d0dde614538a5839ede697e63a43bf6aa6

AstGen: add result location analysis pass

The main motivation for this change is eliminating the `block_ptr` result location and corresponding `store_to_block_ptr` ZIR instruction. This is achieved through a simple pass over the AST before AstGen which determines, for AST nodes which have a choice on whether to provide a result location, which choice to make, based on whether the result pointer is consumed non-trivially. This eliminates so much logic from AstGen that we almost break even on line count! AstGen no longer has to worry about instruction rewriting based on whether or not a result location was consumed: it always knows what to do ahead of time, which simplifies a *lot* of logic. This also incidentally fixes a few random AstGen bugs related to result location handling, leading to the changes in `test/` and `lib/std/`. This opens the door to future RLS improvements by making them much easier to implement correctly, and fixes many bugs. Most ZIR is made more compact after this commit, mainly due to not having redundant `store_to_block_ptr` instructions lying around, but also due to a few bugs in the old system which are implicitly fixed here.

9 files changed, 1346 insertions(+), 1148 deletions(-)

CMakeLists.txt+1
......@@ -515,6 +515,7 @@ set(ZIG_STAGE2_SOURCES
515515 "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"
516516 "${CMAKE_SOURCE_DIR}/src/Air.zig"
517517 "${CMAKE_SOURCE_DIR}/src/AstGen.zig"
518 "${CMAKE_SOURCE_DIR}/src/AstRlAnnotate.zig"
518519 "${CMAKE_SOURCE_DIR}/src/Compilation.zig"
519520 "${CMAKE_SOURCE_DIR}/src/Liveness.zig"
520521 "${CMAKE_SOURCE_DIR}/src/Module.zig"
lib/std/Build/Step/ConfigHeader.zig+2-3
......@@ -370,10 +370,9 @@ fn render_cmake(
370370 }
371371 },
372372
373 else => {
374 break :blk value;
375 },
373 else => {},
376374 }
375 break :blk value;
377376 };
378377
379378 if (booldefine) {
src/AstGen.zig+254-1092
......@@ -17,9 +17,13 @@ const refToIndex = Zir.refToIndex;
1717const indexToRef = Zir.indexToRef;
1818const trace = @import("tracy.zig").trace;
1919const BuiltinFn = @import("BuiltinFn.zig");
20const AstRlAnnotate = @import("AstRlAnnotate.zig");
2021
2122gpa: Allocator,
2223tree: *const Ast,
24/// The set of nodes which, given the choice, must expose a result pointer to
25/// sub-expressions. See `AstRlAnnotate` for details.
26nodes_need_rl: *const AstRlAnnotate.RlNeededSet,
2327instructions: std.MultiArrayList(Zir.Inst) = .{},
2428extra: ArrayListUnmanaged(u32) = .{},
2529string_bytes: ArrayListUnmanaged(u8) = .{},
......@@ -113,10 +117,14 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
113117 var arena = std.heap.ArenaAllocator.init(gpa);
114118 defer arena.deinit();
115119
120 var nodes_need_rl = try AstRlAnnotate.annotate(gpa, arena.allocator(), tree);
121 defer nodes_need_rl.deinit(gpa);
122
116123 var astgen: AstGen = .{
117124 .gpa = gpa,
118125 .arena = arena.allocator(),
119126 .tree = &tree,
127 .nodes_need_rl = &nodes_need_rl,
120128 };
121129 defer astgen.deinit(gpa);
122130
......@@ -272,69 +280,12 @@ const ResultInfo = struct {
272280 /// The result instruction from the expression must be ignored.
273281 /// Always an instruction with tag `alloc_inferred`.
274282 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,
279283
280284 const PtrResultLoc = struct {
281285 inst: Zir.Inst.Ref,
282286 src_node: ?Ast.Node.Index = null,
283287 };
284288
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
338289 /// Find the result type for a cast builtin given the result location.
339290 /// If the location does not have a known result type, emits an error on
340291 /// the given node.
......@@ -347,13 +298,6 @@ const ResultInfo = struct {
347298 const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node);
348299 return gz.addUnNode(.elem_type, ptr_ty, node);
349300 },
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 },
357301 }
358302
359303 return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
......@@ -1516,7 +1460,7 @@ fn arrayInitExpr(
15161460 return rvalue(gz, ri, result, node);
15171461 },
15181462 .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);
15201464 },
15211465 .inferred_ptr => |ptr_inst| {
15221466 if (types.array == .none) {
......@@ -1526,17 +1470,8 @@ fn arrayInitExpr(
15261470 const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
15271471 return rvalue(gz, ri, result, node);
15281472 } 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);
15381474 }
1539 return arrayInitExprRlPtr(gz, scope, ri, node, block_gz.rl_ptr, array_init.ast.elements, types.array);
15401475 },
15411476 }
15421477}
......@@ -1609,7 +1544,6 @@ fn arrayInitExprInner(
16091544fn arrayInitExprRlPtr(
16101545 gz: *GenZir,
16111546 scope: *Scope,
1612 ri: ResultInfo,
16131547 node: Ast.Node.Index,
16141548 result_ptr: Zir.Inst.Ref,
16151549 elements: []const Ast.Node.Index,
......@@ -1620,11 +1554,8 @@ fn arrayInitExprRlPtr(
16201554 return arrayInitExprRlPtrInner(gz, scope, node, base_ptr, elements);
16211555 }
16221556
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);
16281559}
16291560
16301561fn arrayInitExprRlPtrInner(
......@@ -1759,7 +1690,7 @@ fn structInitExpr(
17591690 const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init);
17601691 return rvalue(gz, ri, result, node);
17611692 },
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),
17631694 .inferred_ptr => |ptr_inst| {
17641695 if (struct_init.ast.type_expr == 0) {
17651696 // We treat this case differently so that we don't get a crash when
......@@ -1768,19 +1699,9 @@ fn structInitExpr(
17681699 const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
17691700 return rvalue(gz, ri, result, node);
17701701 } else {
1771 return structInitExprRlPtr(gz, scope, ri, node, struct_init, ptr_inst);
1702 return structInitExprRlPtr(gz, scope, node, struct_init, ptr_inst);
17721703 }
17731704 },
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 },
17841705 }
17851706}
17861707
......@@ -1824,7 +1745,6 @@ fn structInitExprRlNone(
18241745fn structInitExprRlPtr(
18251746 gz: *GenZir,
18261747 scope: *Scope,
1827 ri: ResultInfo,
18281748 node: Ast.Node.Index,
18291749 struct_init: Ast.full.StructInit,
18301750 result_ptr: Zir.Inst.Ref,
......@@ -1836,11 +1756,8 @@ fn structInitExprRlPtr(
18361756 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
18371757 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
18381758
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);
18441761}
18451762
18461763fn structInitExprRlPtrInner(
......@@ -2039,22 +1956,13 @@ fn restoreErrRetIndex(
20391956 .maybe => switch (ri.ctx) {
20401957 .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) {
20411958 .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 },
20581966 else => result,
20591967 },
20601968 else => .none, // always restore/pop
......@@ -2110,7 +2018,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
21102018 else
21112019 .@"break";
21122020
2113 block_gz.break_count += 1;
21142021 if (rhs == 0) {
21152022 _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node);
21162023
......@@ -2125,7 +2032,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
21252032 }
21262033
21272034 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);
21292035
21302036 try genDefers(parent_gz, scope, parent_scope, .normal_only);
21312037
......@@ -2134,10 +2040,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
21342040 try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand);
21352041
21362042 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 },
21412043 .ptr => {
21422044 // In this case we don't have any mechanism to intercept it;
21432045 // we assume the result location is written, and we break with void.
......@@ -2346,6 +2248,20 @@ fn labeledBlockExpr(
23462248
23472249 try astgen.checkLabelRedefinition(parent_scope, label_token);
23482250
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
23492265 // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
23502266 // so that break statements can reference it.
23512267 const block_tag: Zir.Inst.Tag = if (force_comptime) .block_comptime else .block;
......@@ -2356,10 +2272,9 @@ fn labeledBlockExpr(
23562272 .token = label_token,
23572273 .block_inst = block_inst,
23582274 };
2359 block_scope.setBreakResultInfo(ri);
2275 block_scope.setBreakResultInfo(block_ri);
23602276 if (force_comptime) block_scope.is_comptime = true;
23612277 defer block_scope.unstack();
2362 defer block_scope.labeled_breaks.deinit(astgen.gpa);
23632278
23642279 try blockExprStmts(&block_scope, &block_scope.base, statements);
23652280 if (!block_scope.endsWithNoReturn()) {
......@@ -2372,75 +2287,11 @@ fn labeledBlockExpr(
23722287 try astgen.appendErrorTok(label_token, "unused block label", .{});
23732288 }
23742289
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);
24442295 }
24452296}
24462297
......@@ -2818,7 +2669,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
28182669 .atomic_store,
28192670 .store,
28202671 .store_node,
2821 .store_to_block_ptr,
28222672 .store_to_inferred_ptr,
28232673 .resolve_inferred_alloc,
28242674 .validate_struct_init,
......@@ -3137,7 +2987,7 @@ fn varDecl(
31372987 // the variable, no memory location needed.
31382988 const type_node = var_decl.ast.type_node;
31392989 if (align_inst == .none and
3140 !nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node, type_node != 0))
2990 !astgen.nodes_need_rl.contains(node))
31412991 {
31422992 const result_info: ResultInfo = if (type_node != 0) .{
31432993 .rl = .{ .ty = try typeExpr(gz, scope, type_node) },
......@@ -3170,132 +3020,62 @@ fn varDecl(
31703020 const is_comptime = gz.is_comptime or
31713021 tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime";
31723022
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
31813023 var resolve_inferred_alloc: Zir.Inst.Ref = .none;
31823024 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);
31853027 opt_type_inst = type_inst;
31863028 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) } };
31893030 } else {
3190 init_scope.rl_ptr = try gz.addAllocExtended(.{
3031 break :init_rl .{ .ptr = .{ .inst = try gz.addAllocExtended(.{
31913032 .node = node,
31923033 .type_inst = type_inst,
31933034 .align_inst = align_inst,
31943035 .is_const = true,
31953036 .is_comptime = is_comptime,
3196 });
3197 init_scope.instructions_top = gz.instructions.items.len;
3037 }) } };
31983038 }
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: {
32033041 const tag: Zir.Inst.Tag = if (is_comptime)
32043042 .alloc_inferred_comptime
32053043 else
32063044 .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(.{
32103048 .node = node,
32113049 .type_inst = .none,
32123050 .align_inst = align_inst,
32133051 .is_const = true,
32143052 .is_comptime = is_comptime,
32153053 });
3216 init_scope.instructions_top = gz.instructions.items.len;
3217 break :alloc ref;
32183054 };
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);
32253069
32263070 // The const init expression may have modified the error return trace, so signal
32273071 // to Sema that it should save the new index for restoring later.
32283072 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 });
32643074
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 }
32953075 if (resolve_inferred_alloc != .none) {
32963076 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
32973077 }
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);
32993079
33003080 try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr);
33013081
......@@ -3312,9 +3092,6 @@ fn varDecl(
33123092 return &sub_scope.base;
33133093 },
33143094 .keyword_var => {
3315 const old_rl_ty_inst = gz.rl_ty_inst;
3316 defer gz.rl_ty_inst = old_rl_ty_inst;
3317
33183095 const is_comptime = var_decl.comptime_token != null or gz.is_comptime;
33193096 var resolve_inferred_alloc: Zir.Inst.Ref = .none;
33203097 const var_data: struct {
......@@ -3339,7 +3116,6 @@ fn varDecl(
33393116 });
33403117 }
33413118 };
3342 gz.rl_ty_inst = type_inst;
33433119 break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .ptr = .{ .inst = alloc } } } };
33443120 } else a: {
33453121 const alloc = alloc: {
......@@ -3359,7 +3135,6 @@ fn varDecl(
33593135 });
33603136 }
33613137 };
3362 gz.rl_ty_inst = .none;
33633138 resolve_inferred_alloc = alloc;
33643139 break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .inferred_ptr = alloc } } };
33653140 };
......@@ -5530,17 +5305,30 @@ fn orelseCatchExpr(
55305305 const astgen = parent_gz.astgen;
55315306 const tree = astgen.tree;
55325307
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
55335322 const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr);
55345323
55355324 var block_scope = parent_gz.makeSubBlock(scope);
5536 block_scope.setBreakResultInfo(ri);
5325 block_scope.setBreakResultInfo(block_ri);
55375326 defer block_scope.unstack();
55385327
55395328 const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) {
55405329 .ref => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none },
55415330 else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none },
55425331 };
5543 block_scope.break_count += 1;
55445332 // This could be a pointer or value depending on the `operand_ri` parameter.
55455333 // We cannot use `block_scope.break_result_info` because that has the bare
55465334 // type, whereas this expression has the optional type. Later we make
......@@ -5563,6 +5351,7 @@ fn orelseCatchExpr(
55635351 .ref => unwrapped_payload,
55645352 else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node),
55655353 };
5354 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node);
55665355
55675356 var else_scope = block_scope.makeSubBlock(scope);
55685357 defer else_scope.unstack();
......@@ -5596,93 +5385,20 @@ fn orelseCatchExpr(
55965385
55975386 const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs);
55985387 if (!else_scope.endsWithNoReturn()) {
5599 block_scope.break_count += 1;
5600
56015388 // As our last action before the break, "pop" the error trace if needed
56025389 if (do_err_trace)
56035390 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);
56045393 }
56055394 try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
56065395
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);
56305397
5631/// Supports `else_scope` stacked on `then_scope` stacked on `block_scope`. Unstacks `else_scope` then `then_scope`.
5632fn 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);
56865402 }
56875403}
56885404
......@@ -5858,8 +5574,22 @@ fn ifExpr(
58585574
58595575 const do_err_trace = astgen.fn_block != null and if_full.error_token != null;
58605576
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
58615591 var block_scope = parent_gz.makeSubBlock(scope);
5862 block_scope.setBreakResultInfo(ri);
5592 block_scope.setBreakResultInfo(block_ri);
58635593 defer block_scope.unstack();
58645594
58655595 const payload_is_ref = if (if_full.payload_token) |payload_token|
......@@ -5967,14 +5697,11 @@ fn ifExpr(
59675697 };
59685698
59695699 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);
59705701 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);
59725704 }
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.
59785705
59795706 var else_scope = parent_gz.makeSubBlock(scope);
59805707 defer else_scope.unstack();
......@@ -5985,10 +5712,7 @@ fn ifExpr(
59855712 _ = try else_scope.addSaveErrRetIndex(.always);
59865713
59875714 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) {
59925716 try else_scope.addDbgBlockBegin();
59935717 const sub_scope = s: {
59945718 if (if_full.error_token) |error_token| {
......@@ -6016,47 +5740,27 @@ fn ifExpr(
60165740 break :s &else_scope.base;
60175741 }
60185742 };
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();
60205745 if (!else_scope.endsWithNoReturn()) {
6021 block_scope.break_count += 1;
6022
60235746 // As our last action before the break, "pop" the error trace if needed
60245747 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);
60265750 }
60275751 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 }
60415756
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 }
60605764}
60615765
60625766/// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
......@@ -6064,17 +5768,15 @@ fn setCondBrPayload(
60645768 condbr: Zir.Inst.Index,
60655769 cond: Zir.Inst.Ref,
60665770 then_scope: *GenZir,
6067 then_break: Zir.Inst.Index,
60685771 else_scope: *GenZir,
6069 else_break: Zir.Inst.Index,
60705772) !void {
60715773 defer then_scope.unstack();
60725774 defer else_scope.unstack();
60735775 const astgen = then_scope.astgen;
60745776 const then_body = then_scope.instructionsSliceUpto(else_scope);
60755777 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);
60785780 try astgen.extra.ensureUnusedCapacity(
60795781 astgen.gpa,
60805782 @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len,
......@@ -6087,112 +5789,7 @@ fn setCondBrPayload(
60875789 .else_body_len = else_body_len,
60885790 });
60895791 astgen.appendBodyWithFixups(then_body);
6090 if (then_break != 0) astgen.extra.appendAssumeCapacity(then_break);
60915792 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`.
6096fn 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 }
61965793}
61975794
61985795fn whileExpr(
......@@ -6207,6 +5804,20 @@ fn whileExpr(
62075804 const tree = astgen.tree;
62085805 const token_tags = tree.tokens.items(.tag);
62095806
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
62105821 if (while_full.label_token) |label_token| {
62115822 try astgen.checkLabelRedefinition(scope, label_token);
62125823 }
......@@ -6218,9 +5829,8 @@ fn whileExpr(
62185829
62195830 var loop_scope = parent_gz.makeSubBlock(scope);
62205831 loop_scope.is_inline = is_inline;
6221 loop_scope.setBreakResultInfo(ri);
5832 loop_scope.setBreakResultInfo(block_ri);
62225833 defer loop_scope.unstack();
6223 defer loop_scope.labeled_breaks.deinit(astgen.gpa);
62245834
62255835 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
62265836 defer cond_scope.unstack();
......@@ -6378,19 +5988,16 @@ fn whileExpr(
63785988 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
63795989 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
63805990 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);
63835992 }
63845993 try continue_scope.setBlockBody(continue_block);
5994 _ = try then_scope.addBreak(break_tag, cond_block, .void_value);
63855995
63865996 var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
63875997 defer else_scope.unstack();
63885998
63895999 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) {
63946001 try else_scope.addDbgBlockBegin();
63956002 const sub_scope = s: {
63966003 if (while_full.error_token) |error_token| {
......@@ -6427,45 +6034,33 @@ fn whileExpr(
64276034 _ = try addEnsureResult(&else_scope, else_result, else_node);
64286035 }
64296036
6430 if (!else_scope.endsWithNoReturn()) {
6431 loop_scope.break_count += 1;
6432 }
64336037 try checkUsed(parent_gz, &else_scope.base, sub_scope);
64346038 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 }
64436046
64446047 if (loop_scope.label) |some| {
64456048 if (!some.used) {
64466049 try astgen.appendErrorTok(some.token, "unused while loop label", .{});
64476050 }
64486051 }
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
64666060 if (is_statement) {
64676061 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
64686062 }
6063
64696064 return result;
64706065}
64716066
......@@ -6483,6 +6078,20 @@ fn forExpr(
64836078 try astgen.checkLabelRedefinition(scope, label_token);
64846079 }
64856080
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
64866095 const is_inline = for_full.inline_token != null;
64876096 const tree = astgen.tree;
64886097 const token_tags = tree.tokens.items(.tag);
......@@ -6603,9 +6212,8 @@ fn forExpr(
66036212
66046213 var loop_scope = parent_gz.makeSubBlock(scope);
66056214 loop_scope.is_inline = is_inline;
6606 loop_scope.setBreakResultInfo(ri);
6215 loop_scope.setBreakResultInfo(block_ri);
66076216 defer loop_scope.unstack();
6608 defer loop_scope.labeled_breaks.deinit(gpa);
66096217
66106218 // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the
66116219 // load must be removed from instructions in the meantime or it appears to be part of parent_gz.
......@@ -6709,14 +6317,15 @@ fn forExpr(
67096317 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
67106318 try then_scope.addDbgBlockEnd();
67116319
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
67126324 var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
67136325 defer else_scope.unstack();
67146326
67156327 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) {
67206329 const sub_scope = &else_scope.base;
67216330 // Remove the continue block and break block so that `continue` and `break`
67226331 // control flow apply to outer loops; not this one.
......@@ -6726,42 +6335,21 @@ fn forExpr(
67266335 if (is_statement) {
67276336 _ = try addEnsureResult(&else_scope, else_result, else_node);
67286337 }
6729
67306338 if (!else_scope.endsWithNoReturn()) {
6731 loop_scope.break_count += 1;
6339 _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node);
67326340 }
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 }
67416345
67426346 if (loop_scope.label) |some| {
67436347 if (!some.used) {
67446348 try astgen.appendErrorTok(some.token, "unused for loop label", .{});
67456349 }
67466350 }
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);
67656353
67666354 // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it
67676355 {
......@@ -6780,9 +6368,11 @@ fn forExpr(
67806368 try loop_scope.setBlockBody(loop_block);
67816369 }
67826370
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
67866376 if (is_statement) {
67876377 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
67886378 }
......@@ -6806,6 +6396,20 @@ fn switchExpr(
68066396 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
68076397 const case_nodes = tree.extra_data[extra.start..extra.end];
68086398
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
68096413 // We perform two passes over the AST. This first pass is to collect information
68106414 // for the following variables, make note of the special prong AST node index,
68116415 // and bail out with a compile error if there are multiple special prongs present.
......@@ -6952,7 +6556,7 @@ fn switchExpr(
69526556 var block_scope = parent_gz.makeSubBlock(scope);
69536557 // block_scope not used for collecting instructions
69546558 block_scope.instructions_top = GenZir.unstacked_top;
6955 block_scope.setBreakResultInfo(ri);
6559 block_scope.setBreakResultInfo(block_ri);
69566560
69576561 // Sema expects a dbg_stmt immediately before switch_block(_ref)
69586562 try emitDbgStmt(parent_gz, operand_lc);
......@@ -6973,7 +6577,7 @@ fn switchExpr(
69736577 .opcode = .value_placeholder,
69746578 .small = undefined,
69756579 .operand = undefined,
6976 } }, // TODO rename opcode
6580 } },
69776581 });
69786582 break :tag_inst inst;
69796583 } else undefined;
......@@ -7122,7 +6726,6 @@ fn switchExpr(
71226726 try checkUsed(parent_gz, &case_scope.base, sub_scope);
71236727 try case_scope.addDbgBlockEnd();
71246728 if (!parent_gz.refIsNoReturn(case_result)) {
7125 block_scope.break_count += 1;
71266729 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
71276730 }
71286731
......@@ -7195,119 +6798,33 @@ fn switchExpr(
71956798 }
71966799
71976800 const zir_datas = astgen.instructions.items(.data);
7198 const zir_tags = astgen.instructions.items(.tag);
7199
72006801 zir_datas[switch_block].pl_node.payload_index = payload_index;
72016802
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;
73046817 }
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]);
73056821 }
73066822
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 }
73116828}
73126829
73136830fn 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
73726889 return Zir.Inst.Ref.unreachable_value;
73736890 }
73746891
7375 const ri: ResultInfo = if (nodeMayNeedMemoryLocation(tree, operand_node, true)) .{
6892 const ri: ResultInfo = if (astgen.nodes_need_rl.contains(node)) .{
73766893 .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } },
73776894 .ctx = .@"return",
73786895 } else .{
......@@ -7445,7 +6962,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
74456962 try emitDbgStmt(&else_scope, ret_lc);
74466963 try else_scope.addRet(ri, operand, node);
74476964
7448 try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0);
6965 try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope);
74496966
74506967 return Zir.Inst.Ref.unreachable_value;
74516968 },
......@@ -8003,9 +7520,6 @@ fn as(
80037520 .inferred_ptr => |result_ptr| {
80047521 return asRlPtr(gz, scope, ri, node, result_ptr, rhs, dest_type);
80057522 },
8006 .block_ptr => |block_scope| {
8007 return asRlPtr(gz, scope, ri, node, block_scope.rl_ptr, rhs, dest_type);
8008 },
80097523 }
80107524}
80117525
......@@ -8032,7 +7546,7 @@ fn unionInit(
80327546}
80337547
80347548fn asRlPtr(
8035 parent_gz: *GenZir,
7549 gz: *GenZir,
80367550 scope: *Scope,
80377551 ri: ResultInfo,
80387552 src_node: Ast.Node.Index,
......@@ -8040,11 +7554,13 @@ fn asRlPtr(
80407554 operand_node: Ast.Node.Index,
80417555 dest_type: Zir.Inst.Ref,
80427556) 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 }
80487564}
80497565
80507566fn bitCast(
......@@ -9226,7 +8742,6 @@ fn callExpr(
92268742 defer arg_block.unstack();
92278743
92288744 // `call_inst` is reused to provide the param type.
9229 arg_block.rl_ty_inst = call_inst;
92308745 const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node);
92318746 _ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node);
92328747
......@@ -9420,250 +8935,6 @@ fn nodeIsTriviallyZero(tree: *const Ast, node: Ast.Node.Index) bool {
94208935 }
94218936}
94228937
9423fn 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
96678938fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool {
96688939 const node_tags = tree.nodes.items(.tag);
96698940 const node_datas = tree.nodes.items(.data);
......@@ -10450,7 +9721,7 @@ fn rvalue(
104509721 .discard => {
104519722 // Emit a compile error for discarding error values.
104529723 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
10453 return result;
9724 return .void_value;
104549725 },
104559726 .ref => {
104569727 // We need a pointer but we have a value.
......@@ -10561,16 +9832,11 @@ fn rvalue(
105619832 .lhs = ptr_res.inst,
105629833 .rhs = result,
105639834 });
10564 return result;
9835 return .void_value;
105659836 },
105669837 .inferred_ptr => |alloc| {
105679838 _ = 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;
105749840 },
105759841 }
105769842}
......@@ -11260,22 +10526,6 @@ const GenZir = struct {
1126010526 continue_block: Zir.Inst.Index = 0,
1126110527 /// Only valid when setBreakResultInfo is called.
1126210528 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 }) = .{},
1127910529
1128010530 suspend_node: Ast.Node.Index = 0,
1128110531 nosuspend_node: Ast.Node.Index = 0,
......@@ -11327,7 +10577,6 @@ const GenZir = struct {
1132710577 .decl_node_index = gz.decl_node_index,
1132810578 .decl_line = gz.decl_line,
1132910579 .parent = scope,
11330 .rl_ty_inst = gz.rl_ty_inst,
1133110580 .astgen = gz.astgen,
1133210581 .suspend_node = gz.suspend_node,
1133310582 .nosuspend_node = gz.nosuspend_node,
......@@ -11337,67 +10586,6 @@ const GenZir = struct {
1133710586 };
1133810587 }
1133910588
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
1140110589 const Label = struct {
1140210590 token: Ast.TokenIndex,
1140310591 block_inst: Zir.Inst.Index,
......@@ -11438,46 +10626,20 @@ const GenZir = struct {
1143810626 // ZIR needs to be generated. In the former case we rely on storing to the
1143910627 // pointer to communicate the result, and use breakvoid; in the latter case
1144010628 // 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.
1144510629 switch (parent_ri.rl) {
1144610630 .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 };
1145410633 },
11455
11456 .none, .ref => {
11457 gz.rl_ty_inst = .none;
11458 gz.break_result_info = parent_ri;
11459 },
11460
1146110634 .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.
1146310639 gz.break_result_info = .{ .rl = .discard };
1146410640 },
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;
1148110643 },
1148210644 }
1148310645 }
src/AstRlAnnotate.zig created+1086
......@@ -0,0 +1,1086 @@
1//! AstRlAnnotate is a simple pass which runs over the AST before AstGen to
2//! determine which expressions require result locations.
3//!
4//! In some cases, AstGen can choose whether to provide a result pointer or to
5//! just use standard `break` instructions from a block. The latter choice can
6//! result in more efficient ZIR and runtime code, but does not allow for RLS to
7//! occur. Thus, we want to provide a real result pointer (from an alloc) only
8//! when necessary.
9//!
10//! To achive this, we need to determine which expressions require a result
11//! pointer. This pass is reponsible for analyzing all syntax forms which may
12//! provide a result location and, if sub-expressions consume this result
13//! pointer non-trivially (e.g. writing through field pointers), marking the
14//! node as requiring a result location.
15
16const std = @import("std");
17const AstRlAnnotate = @This();
18const Ast = std.zig.Ast;
19const Allocator = std.mem.Allocator;
20const AutoHashMapUnmanaged = std.AutoHashMapUnmanaged;
21const BuiltinFn = @import("BuiltinFn.zig");
22const assert = std.debug.assert;
23
24gpa: Allocator,
25arena: Allocator,
26tree: *const Ast,
27
28/// Certain nodes are placed in this set under the following conditions:
29/// * if-else: either branch consumes the result location
30/// * labeled block: any break consumes the result location
31/// * switch: any prong consumes the result location
32/// * orelse/catch: the RHS expression consumes the result location
33/// * while/for: any break consumes the result location
34/// * @as: the second operand consumes the result location
35/// * const: the init expression consumes the result location
36/// * return: the return expression consumes the result location
37nodes_need_rl: RlNeededSet = .{},
38
39pub const RlNeededSet = AutoHashMapUnmanaged(Ast.Node.Index, void);
40
41const ResultInfo = packed struct {
42 /// Do we have a known result type?
43 have_type: bool,
44 /// Do we (potentially) have a result pointer? Note that this pointer's type
45 /// may not be known due to it being an inferred alloc.
46 have_ptr: bool,
47
48 const none: ResultInfo = .{ .have_type = false, .have_ptr = false };
49 const typed_ptr: ResultInfo = .{ .have_type = true, .have_ptr = true };
50 const inferred_ptr: ResultInfo = .{ .have_type = false, .have_ptr = true };
51 const type_only: ResultInfo = .{ .have_type = true, .have_ptr = false };
52};
53
54/// A labeled block or a loop. When this block is broken from, `consumes_res_ptr`
55/// should be set if the break expression consumed the result pointer.
56const Block = struct {
57 parent: ?*Block,
58 label: ?[]const u8,
59 is_loop: bool,
60 ri: ResultInfo,
61 consumes_res_ptr: bool,
62};
63
64pub fn annotate(gpa: Allocator, arena: Allocator, tree: Ast) Allocator.Error!RlNeededSet {
65 var astrl: AstRlAnnotate = .{
66 .gpa = gpa,
67 .arena = arena,
68 .tree = &tree,
69 };
70 defer astrl.deinit(gpa);
71
72 if (tree.errors.len != 0) {
73 // We can't perform analysis on a broken AST. AstGen will not run in
74 // this case.
75 return .{};
76 }
77
78 for (tree.containerDeclRoot().ast.members) |member_node| {
79 _ = try astrl.expr(member_node, null, ResultInfo.none);
80 }
81
82 return astrl.nodes_need_rl.move();
83}
84
85fn deinit(astrl: *AstRlAnnotate, gpa: Allocator) void {
86 astrl.nodes_need_rl.deinit(gpa);
87}
88
89fn containerDecl(
90 astrl: *AstRlAnnotate,
91 block: ?*Block,
92 full: Ast.full.ContainerDecl,
93) !void {
94 const tree = astrl.tree;
95 const token_tags = tree.tokens.items(.tag);
96 switch (token_tags[full.ast.main_token]) {
97 .keyword_struct => {
98 if (full.ast.arg != 0) {
99 _ = try astrl.expr(full.ast.arg, block, ResultInfo.type_only);
100 }
101 for (full.ast.members) |member_node| {
102 _ = try astrl.expr(member_node, block, ResultInfo.none);
103 }
104 },
105 .keyword_union => {
106 if (full.ast.arg != 0) {
107 _ = try astrl.expr(full.ast.arg, block, ResultInfo.type_only);
108 }
109 for (full.ast.members) |member_node| {
110 _ = try astrl.expr(member_node, block, ResultInfo.none);
111 }
112 },
113 .keyword_enum => {
114 if (full.ast.arg != 0) {
115 _ = try astrl.expr(full.ast.arg, block, ResultInfo.type_only);
116 }
117 for (full.ast.members) |member_node| {
118 _ = try astrl.expr(member_node, block, ResultInfo.none);
119 }
120 },
121 .keyword_opaque => {
122 for (full.ast.members) |member_node| {
123 _ = try astrl.expr(member_node, block, ResultInfo.none);
124 }
125 },
126 else => unreachable,
127 }
128}
129
130/// Returns true if `rl` provides a result pointer and the expression consumes it.
131fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultInfo) Allocator.Error!bool {
132 const tree = astrl.tree;
133 const token_tags = tree.tokens.items(.tag);
134 const node_datas = tree.nodes.items(.data);
135 const node_tags = tree.nodes.items(.tag);
136 switch (node_tags[node]) {
137 .root,
138 .switch_case_one,
139 .switch_case_inline_one,
140 .switch_case,
141 .switch_case_inline,
142 .switch_range,
143 .for_range,
144 .asm_output,
145 .asm_input,
146 => unreachable,
147
148 .@"errdefer", .@"defer" => {
149 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
150 return false;
151 },
152
153 .container_field_init,
154 .container_field_align,
155 .container_field,
156 => {
157 const full = tree.fullContainerField(node).?;
158 _ = try astrl.expr(full.ast.type_expr, block, ResultInfo.type_only);
159 if (full.ast.align_expr != 0) {
160 _ = try astrl.expr(full.ast.align_expr, block, ResultInfo.type_only);
161 }
162 if (full.ast.value_expr != 0) {
163 _ = try astrl.expr(full.ast.value_expr, block, ResultInfo.type_only);
164 }
165 return false;
166 },
167 .@"usingnamespace" => {
168 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
169 return false;
170 },
171 .test_decl => {
172 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
173 return false;
174 },
175 .global_var_decl,
176 .local_var_decl,
177 .simple_var_decl,
178 .aligned_var_decl,
179 => {
180 const full = tree.fullVarDecl(node).?;
181 const init_ri = if (full.ast.type_node != 0) init_ri: {
182 _ = try astrl.expr(full.ast.type_node, block, ResultInfo.type_only);
183 break :init_ri ResultInfo.typed_ptr;
184 } else ResultInfo.inferred_ptr;
185 if (full.ast.init_node == 0) {
186 // No init node, so we're done.
187 return false;
188 }
189 switch (token_tags[full.ast.mut_token]) {
190 .keyword_const => {
191 const init_consumes_rl = try astrl.expr(full.ast.init_node, block, init_ri);
192 if (init_consumes_rl) {
193 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
194 }
195 return false;
196 },
197 .keyword_var => {
198 // We'll create an alloc either way, so don't care if the
199 // result pointer is consumed.
200 _ = try astrl.expr(full.ast.init_node, block, init_ri);
201 return false;
202 },
203 else => unreachable,
204 }
205 },
206 .assign => {
207 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
208 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.typed_ptr);
209 return false;
210 },
211 .assign_shl,
212 .assign_shl_sat,
213 .assign_shr,
214 .assign_bit_and,
215 .assign_bit_or,
216 .assign_bit_xor,
217 .assign_div,
218 .assign_sub,
219 .assign_sub_wrap,
220 .assign_sub_sat,
221 .assign_mod,
222 .assign_add,
223 .assign_add_wrap,
224 .assign_add_sat,
225 .assign_mul,
226 .assign_mul_wrap,
227 .assign_mul_sat,
228 => {
229 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
230 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
231 return false;
232 },
233 .shl, .shr => {
234 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
235 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
236 return false;
237 },
238 .add,
239 .add_wrap,
240 .add_sat,
241 .sub,
242 .sub_wrap,
243 .sub_sat,
244 .mul,
245 .mul_wrap,
246 .mul_sat,
247 .div,
248 .mod,
249 .shl_sat,
250 .bit_and,
251 .bit_or,
252 .bit_xor,
253 .bang_equal,
254 .equal_equal,
255 .greater_than,
256 .greater_or_equal,
257 .less_than,
258 .less_or_equal,
259 .array_cat,
260 => {
261 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
262 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
263 return false;
264 },
265 .array_mult => {
266 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
267 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
268 return false;
269 },
270 .error_union, .merge_error_sets => {
271 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
272 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
273 return false;
274 },
275 .bool_and,
276 .bool_or,
277 => {
278 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
279 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
280 return false;
281 },
282 .bool_not => {
283 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
284 return false;
285 },
286 .bit_not, .negation, .negation_wrap => {
287 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
288 return false;
289 },
290
291 // These nodes are leaves and never consume a result location.
292 .identifier,
293 .string_literal,
294 .multiline_string_literal,
295 .number_literal,
296 .unreachable_literal,
297 .asm_simple,
298 .@"asm",
299 .enum_literal,
300 .error_value,
301 .anyframe_literal,
302 .@"continue",
303 .char_literal,
304 .error_set_decl,
305 => return false,
306
307 .builtin_call_two, .builtin_call_two_comma => {
308 if (node_datas[node].lhs == 0) {
309 return astrl.builtinCall(block, ri, node, &.{});
310 } else if (node_datas[node].rhs == 0) {
311 return astrl.builtinCall(block, ri, node, &.{node_datas[node].lhs});
312 } else {
313 return astrl.builtinCall(block, ri, node, &.{ node_datas[node].lhs, node_datas[node].rhs });
314 }
315 },
316 .builtin_call, .builtin_call_comma => {
317 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
318 return astrl.builtinCall(block, ri, node, params);
319 },
320
321 .call_one,
322 .call_one_comma,
323 .async_call_one,
324 .async_call_one_comma,
325 .call,
326 .call_comma,
327 .async_call,
328 .async_call_comma,
329 => {
330 var buf: [1]Ast.Node.Index = undefined;
331 const full = tree.fullCall(&buf, node).?;
332 _ = try astrl.expr(full.ast.fn_expr, block, ResultInfo.none);
333 for (full.ast.params) |param_node| {
334 _ = try astrl.expr(param_node, block, ResultInfo.type_only);
335 }
336 return switch (node_tags[node]) {
337 .call_one,
338 .call_one_comma,
339 .call,
340 .call_comma,
341 => false, // TODO: once function calls are passed result locations this will change
342 .async_call_one,
343 .async_call_one_comma,
344 .async_call,
345 .async_call_comma,
346 => ri.have_ptr, // always use result ptr for frames
347 else => unreachable,
348 };
349 },
350
351 .@"return" => {
352 if (node_datas[node].lhs != 0) {
353 const ret_val_consumes_rl = try astrl.expr(node_datas[node].lhs, block, ResultInfo.typed_ptr);
354 if (ret_val_consumes_rl) {
355 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
356 }
357 }
358 return false;
359 },
360
361 .field_access => {
362 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
363 return false;
364 },
365
366 .if_simple, .@"if" => {
367 const full = tree.fullIf(node).?;
368 if (full.error_token != null or full.payload_token != null) {
369 _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.none);
370 } else {
371 _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.type_only); // bool
372 }
373
374 if (full.ast.else_expr == 0) {
375 _ = try astrl.expr(full.ast.then_expr, block, ResultInfo.none);
376 return false;
377 } else {
378 const then_uses_rl = try astrl.expr(full.ast.then_expr, block, ri);
379 const else_uses_rl = try astrl.expr(full.ast.else_expr, block, ri);
380 const uses_rl = then_uses_rl or else_uses_rl;
381 if (uses_rl) try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
382 return uses_rl;
383 }
384 },
385
386 .while_simple, .while_cont, .@"while" => {
387 const full = tree.fullWhile(node).?;
388 const label: ?[]const u8 = if (full.label_token) |label_token| label: {
389 break :label try astrl.identString(label_token);
390 } else null;
391 if (full.error_token != null or full.payload_token != null) {
392 _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.none);
393 } else {
394 _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.type_only); // bool
395 }
396 var new_block: Block = .{
397 .parent = block,
398 .label = label,
399 .is_loop = true,
400 .ri = ri,
401 .consumes_res_ptr = false,
402 };
403 if (full.ast.cont_expr != 0) {
404 _ = try astrl.expr(full.ast.cont_expr, &new_block, ResultInfo.none);
405 }
406 _ = try astrl.expr(full.ast.then_expr, &new_block, ResultInfo.none);
407 const else_consumes_rl = if (full.ast.else_expr != 0) else_rl: {
408 break :else_rl try astrl.expr(full.ast.else_expr, block, ri);
409 } else false;
410 if (new_block.consumes_res_ptr or else_consumes_rl) {
411 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
412 return true;
413 } else {
414 return false;
415 }
416 },
417
418 .for_simple, .@"for" => {
419 const full = tree.fullFor(node).?;
420 const label: ?[]const u8 = if (full.label_token) |label_token| label: {
421 break :label try astrl.identString(label_token);
422 } else null;
423 for (full.ast.inputs) |input| {
424 if (node_tags[input] == .for_range) {
425 _ = try astrl.expr(node_datas[input].lhs, block, ResultInfo.type_only);
426 if (node_datas[input].rhs != 0) {
427 _ = try astrl.expr(node_datas[input].rhs, block, ResultInfo.type_only);
428 }
429 } else {
430 _ = try astrl.expr(input, block, ResultInfo.none);
431 }
432 }
433 var new_block: Block = .{
434 .parent = block,
435 .label = label,
436 .is_loop = true,
437 .ri = ri,
438 .consumes_res_ptr = false,
439 };
440 _ = try astrl.expr(full.ast.then_expr, &new_block, ResultInfo.none);
441 const else_consumes_rl = if (full.ast.else_expr != 0) else_rl: {
442 break :else_rl try astrl.expr(full.ast.else_expr, block, ri);
443 } else false;
444 if (new_block.consumes_res_ptr or else_consumes_rl) {
445 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
446 return true;
447 } else {
448 return false;
449 }
450 },
451
452 .slice_open => {
453 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
454 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
455 return false;
456 },
457 .slice => {
458 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
459 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
460 _ = try astrl.expr(extra.start, block, ResultInfo.type_only);
461 _ = try astrl.expr(extra.end, block, ResultInfo.type_only);
462 return false;
463 },
464 .slice_sentinel => {
465 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
466 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
467 _ = try astrl.expr(extra.start, block, ResultInfo.type_only);
468 if (extra.end != 0) {
469 _ = try astrl.expr(extra.end, block, ResultInfo.type_only);
470 }
471 _ = try astrl.expr(extra.sentinel, block, ResultInfo.none);
472 return false;
473 },
474 .deref => {
475 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
476 return false;
477 },
478 .address_of => {
479 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
480 return false;
481 },
482 .optional_type => {
483 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
484 return false;
485 },
486 .grouped_expression,
487 .@"try",
488 .@"await",
489 .@"nosuspend",
490 .unwrap_optional,
491 => return astrl.expr(node_datas[node].lhs, block, ri),
492
493 .block_two, .block_two_semicolon => {
494 if (node_datas[node].lhs == 0) {
495 return astrl.blockExpr(block, ri, node, &.{});
496 } else if (node_datas[node].rhs == 0) {
497 return astrl.blockExpr(block, ri, node, &.{node_datas[node].lhs});
498 } else {
499 return astrl.blockExpr(block, ri, node, &.{ node_datas[node].lhs, node_datas[node].rhs });
500 }
501 },
502 .block, .block_semicolon => {
503 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
504 return astrl.blockExpr(block, ri, node, statements);
505 },
506 .anyframe_type => {
507 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
508 return false;
509 },
510 .@"catch", .@"orelse" => {
511 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
512 const rhs_consumes_rl = try astrl.expr(node_datas[node].rhs, block, ri);
513 if (rhs_consumes_rl) {
514 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
515 }
516 return rhs_consumes_rl;
517 },
518
519 .ptr_type_aligned,
520 .ptr_type_sentinel,
521 .ptr_type,
522 .ptr_type_bit_range,
523 => {
524 const full = tree.fullPtrType(node).?;
525 _ = try astrl.expr(full.ast.child_type, block, ResultInfo.type_only);
526 if (full.ast.sentinel != 0) {
527 _ = try astrl.expr(full.ast.sentinel, block, ResultInfo.type_only);
528 }
529 if (full.ast.addrspace_node != 0) {
530 _ = try astrl.expr(full.ast.addrspace_node, block, ResultInfo.type_only);
531 }
532 if (full.ast.align_node != 0) {
533 _ = try astrl.expr(full.ast.align_node, block, ResultInfo.type_only);
534 }
535 if (full.ast.bit_range_start != 0) {
536 assert(full.ast.bit_range_end != 0);
537 _ = try astrl.expr(full.ast.bit_range_start, block, ResultInfo.type_only);
538 _ = try astrl.expr(full.ast.bit_range_end, block, ResultInfo.type_only);
539 }
540 return false;
541 },
542
543 .container_decl,
544 .container_decl_trailing,
545 .container_decl_arg,
546 .container_decl_arg_trailing,
547 .container_decl_two,
548 .container_decl_two_trailing,
549 .tagged_union,
550 .tagged_union_trailing,
551 .tagged_union_enum_tag,
552 .tagged_union_enum_tag_trailing,
553 .tagged_union_two,
554 .tagged_union_two_trailing,
555 => {
556 var buf: [2]Ast.Node.Index = undefined;
557 try astrl.containerDecl(block, tree.fullContainerDecl(&buf, node).?);
558 return false;
559 },
560
561 .@"break" => {
562 if (node_datas[node].rhs == 0) {
563 // Breaks with void are not interesting
564 return false;
565 }
566
567 var opt_cur_block = block;
568 if (node_datas[node].lhs == 0) {
569 // No label - we're breaking from a loop.
570 while (opt_cur_block) |cur_block| : (opt_cur_block = cur_block.parent) {
571 if (cur_block.is_loop) break;
572 }
573 } else {
574 const break_label = try astrl.identString(node_datas[node].lhs);
575 while (opt_cur_block) |cur_block| : (opt_cur_block = cur_block.parent) {
576 const block_label = cur_block.label orelse continue;
577 if (std.mem.eql(u8, block_label, break_label)) break;
578 }
579 }
580
581 if (opt_cur_block) |target_block| {
582 const consumes_break_rl = try astrl.expr(node_datas[node].rhs, block, target_block.ri);
583 if (consumes_break_rl) target_block.consumes_res_ptr = true;
584 } else {
585 // No corresponding scope to break from - AstGen will emit an error.
586 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
587 }
588
589 return false;
590 },
591
592 .array_type => {
593 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
594 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
595 return false;
596 },
597 .array_type_sentinel => {
598 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel);
599 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
600 _ = try astrl.expr(extra.elem_type, block, ResultInfo.type_only);
601 _ = try astrl.expr(extra.sentinel, block, ResultInfo.type_only);
602 return false;
603 },
604 .array_access => {
605 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
606 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
607 return false;
608 },
609 .@"comptime" => {
610 // AstGen will emit an error if the scope is already comptime, so we can assume it is
611 // not. This means the result location is not forwarded.
612 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
613 return false;
614 },
615 .@"switch", .switch_comma => {
616 const operand_node = node_datas[node].lhs;
617 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SubRange);
618 const case_nodes = tree.extra_data[extra.start..extra.end];
619
620 _ = try astrl.expr(operand_node, block, ResultInfo.none);
621
622 var any_prong_consumed_rl = false;
623 for (case_nodes) |case_node| {
624 const case = tree.fullSwitchCase(case_node).?;
625 for (case.ast.values) |item_node| {
626 if (node_tags[item_node] == .switch_range) {
627 _ = try astrl.expr(node_datas[item_node].lhs, block, ResultInfo.none);
628 _ = try astrl.expr(node_datas[item_node].rhs, block, ResultInfo.none);
629 } else {
630 _ = try astrl.expr(item_node, block, ResultInfo.none);
631 }
632 }
633 if (try astrl.expr(case.ast.target_expr, block, ri)) {
634 any_prong_consumed_rl = true;
635 }
636 }
637 if (any_prong_consumed_rl) {
638 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
639 }
640 return any_prong_consumed_rl;
641 },
642 .@"suspend" => {
643 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
644 return false;
645 },
646 .@"resume" => {
647 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
648 return false;
649 },
650
651 .array_init_one,
652 .array_init_one_comma,
653 .array_init_dot_two,
654 .array_init_dot_two_comma,
655 .array_init_dot,
656 .array_init_dot_comma,
657 .array_init,
658 .array_init_comma,
659 => {
660 var buf: [2]Ast.Node.Index = undefined;
661 const full = tree.fullArrayInit(&buf, node).?;
662 const have_type = if (full.ast.type_expr != 0) have_type: {
663 _ = try astrl.expr(full.ast.type_expr, block, ResultInfo.none);
664 break :have_type true;
665 } else ri.have_type;
666 if (have_type) {
667 const elem_ri: ResultInfo = .{
668 .have_type = true,
669 .have_ptr = ri.have_ptr,
670 };
671 for (full.ast.elements) |elem_init| {
672 _ = try astrl.expr(elem_init, block, elem_ri);
673 }
674 return ri.have_ptr;
675 } else {
676 // Untyped init does not consume result location
677 for (full.ast.elements) |elem_init| {
678 _ = try astrl.expr(elem_init, block, ResultInfo.none);
679 }
680 return false;
681 }
682 },
683
684 .struct_init_one,
685 .struct_init_one_comma,
686 .struct_init_dot_two,
687 .struct_init_dot_two_comma,
688 .struct_init_dot,
689 .struct_init_dot_comma,
690 .struct_init,
691 .struct_init_comma,
692 => {
693 var buf: [2]Ast.Node.Index = undefined;
694 const full = tree.fullStructInit(&buf, node).?;
695 const have_type = if (full.ast.type_expr != 0) have_type: {
696 _ = try astrl.expr(full.ast.type_expr, block, ResultInfo.none);
697 break :have_type true;
698 } else ri.have_type;
699 if (have_type) {
700 const elem_ri: ResultInfo = .{
701 .have_type = true,
702 .have_ptr = ri.have_ptr,
703 };
704 for (full.ast.fields) |field_init| {
705 _ = try astrl.expr(field_init, block, elem_ri);
706 }
707 return ri.have_ptr;
708 } else {
709 // Untyped init does not consume result location
710 for (full.ast.fields) |field_init| {
711 _ = try astrl.expr(field_init, block, ResultInfo.none);
712 }
713 return false;
714 }
715 },
716
717 .fn_proto_simple,
718 .fn_proto_multi,
719 .fn_proto_one,
720 .fn_proto,
721 .fn_decl,
722 => {
723 var buf: [1]Ast.Node.Index = undefined;
724 const full = tree.fullFnProto(&buf, node).?;
725 const body_node = if (node_tags[node] == .fn_decl) node_datas[node].rhs else 0;
726 {
727 var it = full.iterate(tree);
728 while (it.next()) |param| {
729 if (param.anytype_ellipsis3 == null) {
730 _ = try astrl.expr(param.type_expr, block, ResultInfo.type_only);
731 }
732 }
733 }
734 if (full.ast.align_expr != 0) {
735 _ = try astrl.expr(full.ast.align_expr, block, ResultInfo.type_only);
736 }
737 if (full.ast.addrspace_expr != 0) {
738 _ = try astrl.expr(full.ast.addrspace_expr, block, ResultInfo.type_only);
739 }
740 if (full.ast.section_expr != 0) {
741 _ = try astrl.expr(full.ast.section_expr, block, ResultInfo.type_only);
742 }
743 if (full.ast.callconv_expr != 0) {
744 _ = try astrl.expr(full.ast.callconv_expr, block, ResultInfo.type_only);
745 }
746 _ = try astrl.expr(full.ast.return_type, block, ResultInfo.type_only);
747 if (body_node != 0) {
748 _ = try astrl.expr(body_node, block, ResultInfo.none);
749 }
750 return false;
751 },
752 }
753}
754
755fn identString(astrl: *AstRlAnnotate, token: Ast.TokenIndex) ![]const u8 {
756 const tree = astrl.tree;
757 const token_tags = tree.tokens.items(.tag);
758 assert(token_tags[token] == .identifier);
759 const ident_name = tree.tokenSlice(token);
760 if (!std.mem.startsWith(u8, ident_name, "@")) {
761 return ident_name;
762 }
763 return std.zig.string_literal.parseAlloc(astrl.arena, ident_name[1..]) catch |err| switch (err) {
764 error.OutOfMemory => error.OutOfMemory,
765 error.InvalidLiteral => "", // This pass can safely return garbage on invalid AST
766 };
767}
768
769fn blockExpr(astrl: *AstRlAnnotate, parent_block: ?*Block, ri: ResultInfo, node: Ast.Node.Index, statements: []const Ast.Node.Index) !bool {
770 const tree = astrl.tree;
771 const token_tags = tree.tokens.items(.tag);
772 const main_tokens = tree.nodes.items(.main_token);
773
774 const lbrace = main_tokens[node];
775 if (token_tags[lbrace - 1] == .colon and
776 token_tags[lbrace - 2] == .identifier)
777 {
778 // Labeled block
779 var new_block: Block = .{
780 .parent = parent_block,
781 .label = try astrl.identString(lbrace - 2),
782 .is_loop = false,
783 .ri = ri,
784 .consumes_res_ptr = false,
785 };
786 for (statements) |statement| {
787 _ = try astrl.expr(statement, &new_block, ResultInfo.none);
788 }
789 if (new_block.consumes_res_ptr) {
790 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
791 }
792 return new_block.consumes_res_ptr;
793 } else {
794 // Unlabeled block
795 for (statements) |statement| {
796 _ = try astrl.expr(statement, parent_block, ResultInfo.none);
797 }
798 return false;
799 }
800}
801
802fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.Node.Index, args: []const Ast.Node.Index) !bool {
803 const tree = astrl.tree;
804 const main_tokens = tree.nodes.items(.main_token);
805 const builtin_token = main_tokens[node];
806 const builtin_name = tree.tokenSlice(builtin_token);
807 const info = BuiltinFn.list.get(builtin_name) orelse return false;
808 if (info.param_count) |expected| {
809 if (expected != args.len) return false;
810 }
811 switch (info.tag) {
812 .import => return false,
813 .compile_log, .TypeOf => {
814 for (args) |arg_node| {
815 _ = try astrl.expr(arg_node, block, ResultInfo.none);
816 }
817 return false;
818 },
819 .as => {
820 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
821 const rhs_consumes_rl = try astrl.expr(args[1], block, ri);
822 if (rhs_consumes_rl) {
823 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
824 }
825 return rhs_consumes_rl;
826 },
827 .bit_cast => {
828 _ = try astrl.expr(args[0], block, ResultInfo.none);
829 return false;
830 },
831 .union_init => {
832 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
833 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
834 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
835 return false;
836 },
837 .c_import => {
838 _ = try astrl.expr(args[0], block, ResultInfo.none);
839 return false;
840 },
841 .min, .max => {
842 for (args) |arg_node| {
843 _ = try astrl.expr(arg_node, block, ResultInfo.none);
844 }
845 return false;
846 },
847 .@"export" => {
848 _ = try astrl.expr(args[0], block, ResultInfo.none);
849 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
850 return false;
851 },
852 .@"extern" => {
853 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
854 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
855 return false;
856 },
857 // These builtins take no args and do not consume the result pointer.
858 .src,
859 .This,
860 .return_address,
861 .frame_address,
862 .error_return_trace,
863 .frame,
864 .breakpoint,
865 .in_comptime,
866 .panic,
867 .trap,
868 .c_va_start,
869 => return false,
870 // These builtins take a single argument with a known result type, but do not consume their
871 // result pointer.
872 .size_of,
873 .bit_size_of,
874 .align_of,
875 .compile_error,
876 .set_eval_branch_quota,
877 .int_from_bool,
878 .int_from_error,
879 .error_from_int,
880 .embed_file,
881 .error_name,
882 .set_runtime_safety,
883 .Type,
884 .c_undef,
885 .c_include,
886 .wasm_memory_size,
887 .splat,
888 .fence,
889 .set_float_mode,
890 .set_align_stack,
891 .set_cold,
892 .type_info,
893 .work_item_id,
894 .work_group_size,
895 .work_group_id,
896 => {
897 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
898 return false;
899 },
900 // These builtins take a single argument with no result information and do not consume their
901 // result pointer.
902 .int_from_ptr,
903 .int_from_enum,
904 .sqrt,
905 .sin,
906 .cos,
907 .tan,
908 .exp,
909 .exp2,
910 .log,
911 .log2,
912 .log10,
913 .fabs,
914 .floor,
915 .ceil,
916 .trunc,
917 .round,
918 .tag_name,
919 .type_name,
920 .Frame,
921 .frame_size,
922 .int_from_float,
923 .float_from_int,
924 .ptr_from_int,
925 .enum_from_int,
926 .float_cast,
927 .int_cast,
928 .truncate,
929 .err_set_cast,
930 .ptr_cast,
931 .align_cast,
932 .addrspace_cast,
933 .const_cast,
934 .volatile_cast,
935 .clz,
936 .ctz,
937 .pop_count,
938 .byte_swap,
939 .bit_reverse,
940 => {
941 _ = try astrl.expr(args[0], block, ResultInfo.none);
942 return false;
943 },
944 .div_exact,
945 .div_floor,
946 .div_trunc,
947 .mod,
948 .rem,
949 => {
950 _ = try astrl.expr(args[0], block, ResultInfo.none);
951 _ = try astrl.expr(args[1], block, ResultInfo.none);
952 return false;
953 },
954 .shl_exact, .shr_exact => {
955 _ = try astrl.expr(args[0], block, ResultInfo.none);
956 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
957 return false;
958 },
959 .bit_offset_of,
960 .offset_of,
961 .field_parent_ptr,
962 .has_decl,
963 .has_field,
964 .field,
965 => {
966 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
967 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
968 return false;
969 },
970 .wasm_memory_grow => {
971 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
972 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
973 return false;
974 },
975 .c_define => {
976 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
977 _ = try astrl.expr(args[1], block, ResultInfo.none);
978 return false;
979 },
980 .reduce => {
981 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
982 _ = try astrl.expr(args[1], block, ResultInfo.none);
983 return false;
984 },
985 .add_with_overflow, .sub_with_overflow, .mul_with_overflow, .shl_with_overflow => {
986 _ = try astrl.expr(args[0], block, ResultInfo.none);
987 _ = try astrl.expr(args[1], block, ResultInfo.none);
988 return false;
989 },
990 .atomic_load => {
991 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
992 _ = try astrl.expr(args[1], block, ResultInfo.none);
993 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
994 return false;
995 },
996 .atomic_rmw => {
997 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
998 _ = try astrl.expr(args[1], block, ResultInfo.none);
999 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1000 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1001 _ = try astrl.expr(args[4], block, ResultInfo.type_only);
1002 return false;
1003 },
1004 .atomic_store => {
1005 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1006 _ = try astrl.expr(args[1], block, ResultInfo.none);
1007 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1008 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1009 return false;
1010 },
1011 .mul_add => {
1012 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1013 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1014 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1015 return false;
1016 },
1017 .call => {
1018 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1019 _ = try astrl.expr(args[1], block, ResultInfo.none);
1020 _ = try astrl.expr(args[2], block, ResultInfo.none);
1021 return false;
1022 },
1023 .memcpy => {
1024 _ = try astrl.expr(args[0], block, ResultInfo.none);
1025 _ = try astrl.expr(args[1], block, ResultInfo.none);
1026 return false;
1027 },
1028 .memset => {
1029 _ = try astrl.expr(args[0], block, ResultInfo.none);
1030 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1031 return false;
1032 },
1033 .shuffle => {
1034 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1035 _ = try astrl.expr(args[1], block, ResultInfo.none);
1036 _ = try astrl.expr(args[2], block, ResultInfo.none);
1037 _ = try astrl.expr(args[3], block, ResultInfo.none);
1038 return false;
1039 },
1040 .select => {
1041 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1042 _ = try astrl.expr(args[1], block, ResultInfo.none);
1043 _ = try astrl.expr(args[2], block, ResultInfo.none);
1044 _ = try astrl.expr(args[3], block, ResultInfo.none);
1045 return false;
1046 },
1047 .async_call => {
1048 _ = try astrl.expr(args[0], block, ResultInfo.none);
1049 _ = try astrl.expr(args[1], block, ResultInfo.none);
1050 _ = try astrl.expr(args[2], block, ResultInfo.none);
1051 _ = try astrl.expr(args[3], block, ResultInfo.none);
1052 return false; // buffer passed as arg for frame data
1053 },
1054 .Vector => {
1055 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1056 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1057 return false;
1058 },
1059 .prefetch => {
1060 _ = try astrl.expr(args[0], block, ResultInfo.none);
1061 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1062 return false;
1063 },
1064 .c_va_arg => {
1065 _ = try astrl.expr(args[0], block, ResultInfo.none);
1066 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1067 return false;
1068 },
1069 .c_va_copy => {
1070 _ = try astrl.expr(args[0], block, ResultInfo.none);
1071 return false;
1072 },
1073 .c_va_end => {
1074 _ = try astrl.expr(args[0], block, ResultInfo.none);
1075 return false;
1076 },
1077 .cmpxchg_strong, .cmpxchg_weak => {
1078 _ = try astrl.expr(args[0], block, ResultInfo.none);
1079 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1080 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1081 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1082 _ = try astrl.expr(args[4], block, ResultInfo.type_only);
1083 return false;
1084 },
1085 }
1086}
src/Sema.zig-34
......@@ -1346,11 +1346,6 @@ fn analyzeBodyInner(
13461346 i += 1;
13471347 continue;
13481348 },
1349 .store_to_block_ptr => {
1350 try sema.zirStoreToBlockPtr(block, inst);
1351 i += 1;
1352 continue;
1353 },
13541349 .store_to_inferred_ptr => {
13551350 try sema.zirStoreToInferredPtr(block, inst);
13561351 i += 1;
......@@ -5269,35 +5264,6 @@ fn addDeclaredHereNote(sema: *Sema, parent: *Module.ErrorMsg, decl_ty: Type) !vo
52695264 try mod.errNoteNonLazy(src_loc, parent, "{s} declared here", .{category});
52705265}
52715266
5272fn zirStoreToBlockPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
5273 const tracy = trace(@src());
5274 defer tracy.end();
5275
5276 const bin_inst = sema.code.instructions.items(.data)[inst].bin;
5277 const ptr = sema.inst_map.get(Zir.refToIndex(bin_inst.lhs).?) orelse {
5278 // This is an elided instruction, but AstGen was unable to omit it.
5279 return;
5280 };
5281 const operand = try sema.resolveInst(bin_inst.rhs);
5282 const src: LazySrcLoc = sema.src;
5283 blk: {
5284 const ptr_inst = Air.refToIndex(ptr) orelse break :blk;
5285 switch (sema.air_instructions.items(.tag)[ptr_inst]) {
5286 .inferred_alloc_comptime => {
5287 const iac = &sema.air_instructions.items(.data)[ptr_inst].inferred_alloc_comptime;
5288 return sema.storeToInferredAllocComptime(block, src, operand, iac);
5289 },
5290 .inferred_alloc => {
5291 const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?;
5292 return sema.storeToInferredAlloc(block, ptr, operand, ia);
5293 },
5294 else => break :blk,
5295 }
5296 }
5297
5298 return sema.storePtr(block, src, ptr, operand);
5299}
5300
53015267fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
53025268 const tracy = trace(@src());
53035269 defer tracy.end();
src/Zir.zig+1-15
......@@ -602,20 +602,9 @@ pub const Inst = struct {
602602 /// Same as `store` except provides a source location.
603603 /// Uses the `pl_node` union field. Payload is `Bin`.
604604 store_node,
605 /// This instruction is not really supposed to be emitted from AstGen; nevertheless it
606 /// is sometimes emitted due to deficiencies in AstGen. When Sema sees this instruction,
607 /// it must clean up after AstGen's mess by looking at various context clues and
608 /// then treating it as one of the following:
609 /// * no-op
610 /// * store_to_inferred_ptr
611 /// * store
612 /// Uses the `bin` union field with LHS as the pointer to store to.
613 store_to_block_ptr,
614605 /// Same as `store` but the type of the value being stored will be used to infer
615606 /// the pointer type.
616 /// Uses the `bin` union field - Astgen.zig depends on the ability to change
617 /// the tag of an instruction from `store_to_block_ptr` to `store_to_inferred_ptr`
618 /// without changing the data.
607 /// Uses the `bin` union field.
619608 store_to_inferred_ptr,
620609 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
621610 /// Uses the `str` union field.
......@@ -1109,7 +1098,6 @@ pub const Inst = struct {
11091098 .shr,
11101099 .store,
11111100 .store_node,
1112 .store_to_block_ptr,
11131101 .store_to_inferred_ptr,
11141102 .str,
11151103 .sub,
......@@ -1295,7 +1283,6 @@ pub const Inst = struct {
12951283 .atomic_store,
12961284 .store,
12971285 .store_node,
1298 .store_to_block_ptr,
12991286 .store_to_inferred_ptr,
13001287 .resolve_inferred_alloc,
13011288 .validate_array_init_ty,
......@@ -1667,7 +1654,6 @@ pub const Inst = struct {
16671654 .slice_length = .pl_node,
16681655 .store = .bin,
16691656 .store_node = .pl_node,
1670 .store_to_block_ptr = .bin,
16711657 .store_to_inferred_ptr = .bin,
16721658 .str = .str,
16731659 .negate = .un_node,
src/print_zir.zig-1
......@@ -145,7 +145,6 @@ const Writer = struct {
145145 switch (tag) {
146146 .as,
147147 .store,
148 .store_to_block_ptr,
149148 .store_to_inferred_ptr,
150149 => try self.writeBin(stream, inst),
151150
test/cases/compile_errors/break_void_result_location.zig+1-1
......@@ -29,4 +29,4 @@ export fn f4() void {
2929// :2:22: error: expected type 'usize', found 'void'
3030// :7:9: error: expected type 'usize', found 'void'
3131// :14:9: error: expected type 'usize', found 'void'
32// :19:27: error: expected type 'usize', found 'void'
32// :20:9: error: expected type 'usize', found 'void'
test/cases/compile_errors/missing_else_clause.zig+1-2
......@@ -34,8 +34,7 @@ export fn entry() void {
3434// backend=stage2
3535// target=native
3636//
37// :2:20: error: incompatible types: 'i32' and 'void'
38// :2:30: note: type 'i32' here
37// :2:20: error: expected type 'i32', found 'void'
3938// :8:15: error: incompatible types: 'i32' and 'void'
4039// :8:25: note: type 'i32' here
4140// :16:16: error: expected type 'tmp.h.T', found 'void'