authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-08-20 15:38:36-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-08-20 15:38:36-07:00
log411462e1cdac518ccb67a8dd7aa5ef93332f8d19
tree06759e2ef15cdb58bda051fe0ca17f013ba41f6b
parent020105d0dde614538a5839ede697e63a43bf6aa6
parent4ac20f69acd446e6d9ac59f1fc4172147c05559e
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #16875 from mlugg/astgen-rl-pass

AstGen: add result location analysis pass

13 files changed, 1429 insertions(+), 1166 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+1088
......@@ -0,0 +1,1088 @@
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 .error_return_trace,
862 .frame,
863 .breakpoint,
864 .in_comptime,
865 .panic,
866 .trap,
867 .c_va_start,
868 => return false,
869 // TODO: this is a workaround for llvm/llvm-project#68409
870 // Zig tracking issue: #16876
871 .frame_address => return true,
872 // These builtins take a single argument with a known result type, but do not consume their
873 // result pointer.
874 .size_of,
875 .bit_size_of,
876 .align_of,
877 .compile_error,
878 .set_eval_branch_quota,
879 .int_from_bool,
880 .int_from_error,
881 .error_from_int,
882 .embed_file,
883 .error_name,
884 .set_runtime_safety,
885 .Type,
886 .c_undef,
887 .c_include,
888 .wasm_memory_size,
889 .splat,
890 .fence,
891 .set_float_mode,
892 .set_align_stack,
893 .set_cold,
894 .type_info,
895 .work_item_id,
896 .work_group_size,
897 .work_group_id,
898 => {
899 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
900 return false;
901 },
902 // These builtins take a single argument with no result information and do not consume their
903 // result pointer.
904 .int_from_ptr,
905 .int_from_enum,
906 .sqrt,
907 .sin,
908 .cos,
909 .tan,
910 .exp,
911 .exp2,
912 .log,
913 .log2,
914 .log10,
915 .fabs,
916 .floor,
917 .ceil,
918 .trunc,
919 .round,
920 .tag_name,
921 .type_name,
922 .Frame,
923 .frame_size,
924 .int_from_float,
925 .float_from_int,
926 .ptr_from_int,
927 .enum_from_int,
928 .float_cast,
929 .int_cast,
930 .truncate,
931 .err_set_cast,
932 .ptr_cast,
933 .align_cast,
934 .addrspace_cast,
935 .const_cast,
936 .volatile_cast,
937 .clz,
938 .ctz,
939 .pop_count,
940 .byte_swap,
941 .bit_reverse,
942 => {
943 _ = try astrl.expr(args[0], block, ResultInfo.none);
944 return false;
945 },
946 .div_exact,
947 .div_floor,
948 .div_trunc,
949 .mod,
950 .rem,
951 => {
952 _ = try astrl.expr(args[0], block, ResultInfo.none);
953 _ = try astrl.expr(args[1], block, ResultInfo.none);
954 return false;
955 },
956 .shl_exact, .shr_exact => {
957 _ = try astrl.expr(args[0], block, ResultInfo.none);
958 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
959 return false;
960 },
961 .bit_offset_of,
962 .offset_of,
963 .field_parent_ptr,
964 .has_decl,
965 .has_field,
966 .field,
967 => {
968 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
969 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
970 return false;
971 },
972 .wasm_memory_grow => {
973 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
974 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
975 return false;
976 },
977 .c_define => {
978 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
979 _ = try astrl.expr(args[1], block, ResultInfo.none);
980 return false;
981 },
982 .reduce => {
983 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
984 _ = try astrl.expr(args[1], block, ResultInfo.none);
985 return false;
986 },
987 .add_with_overflow, .sub_with_overflow, .mul_with_overflow, .shl_with_overflow => {
988 _ = try astrl.expr(args[0], block, ResultInfo.none);
989 _ = try astrl.expr(args[1], block, ResultInfo.none);
990 return false;
991 },
992 .atomic_load => {
993 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
994 _ = try astrl.expr(args[1], block, ResultInfo.none);
995 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
996 return false;
997 },
998 .atomic_rmw => {
999 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1000 _ = try astrl.expr(args[1], block, ResultInfo.none);
1001 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1002 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1003 _ = try astrl.expr(args[4], block, ResultInfo.type_only);
1004 return false;
1005 },
1006 .atomic_store => {
1007 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1008 _ = try astrl.expr(args[1], block, ResultInfo.none);
1009 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1010 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1011 return false;
1012 },
1013 .mul_add => {
1014 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1015 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1016 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1017 return false;
1018 },
1019 .call => {
1020 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1021 _ = try astrl.expr(args[1], block, ResultInfo.none);
1022 _ = try astrl.expr(args[2], block, ResultInfo.none);
1023 return false;
1024 },
1025 .memcpy => {
1026 _ = try astrl.expr(args[0], block, ResultInfo.none);
1027 _ = try astrl.expr(args[1], block, ResultInfo.none);
1028 return false;
1029 },
1030 .memset => {
1031 _ = try astrl.expr(args[0], block, ResultInfo.none);
1032 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1033 return false;
1034 },
1035 .shuffle => {
1036 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1037 _ = try astrl.expr(args[1], block, ResultInfo.none);
1038 _ = try astrl.expr(args[2], block, ResultInfo.none);
1039 _ = try astrl.expr(args[3], block, ResultInfo.none);
1040 return false;
1041 },
1042 .select => {
1043 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1044 _ = try astrl.expr(args[1], block, ResultInfo.none);
1045 _ = try astrl.expr(args[2], block, ResultInfo.none);
1046 _ = try astrl.expr(args[3], block, ResultInfo.none);
1047 return false;
1048 },
1049 .async_call => {
1050 _ = try astrl.expr(args[0], block, ResultInfo.none);
1051 _ = try astrl.expr(args[1], block, ResultInfo.none);
1052 _ = try astrl.expr(args[2], block, ResultInfo.none);
1053 _ = try astrl.expr(args[3], block, ResultInfo.none);
1054 return false; // buffer passed as arg for frame data
1055 },
1056 .Vector => {
1057 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1058 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1059 return false;
1060 },
1061 .prefetch => {
1062 _ = try astrl.expr(args[0], block, ResultInfo.none);
1063 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1064 return false;
1065 },
1066 .c_va_arg => {
1067 _ = try astrl.expr(args[0], block, ResultInfo.none);
1068 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1069 return false;
1070 },
1071 .c_va_copy => {
1072 _ = try astrl.expr(args[0], block, ResultInfo.none);
1073 return false;
1074 },
1075 .c_va_end => {
1076 _ = try astrl.expr(args[0], block, ResultInfo.none);
1077 return false;
1078 },
1079 .cmpxchg_strong, .cmpxchg_weak => {
1080 _ = try astrl.expr(args[0], block, ResultInfo.none);
1081 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1082 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1083 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1084 _ = try astrl.expr(args[4], block, ResultInfo.type_only);
1085 return false;
1086 },
1087 }
1088}
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/codegen/c.zig+1-1
......@@ -4285,7 +4285,7 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
42854285 const writer = f.object.writer();
42864286
42874287 const inst_ty = f.typeOfIndex(inst);
4288 const result = if (inst_ty.ip_index != .void_type and !f.liveness.isUnused(inst))
4288 const result = if (inst_ty.hasRuntimeBitsIgnoreComptime(mod) and !f.liveness.isUnused(inst))
42894289 try f.allocLocal(inst, inst_ty)
42904290 else
42914291 .none;
src/codegen/llvm/Builder.zig+4-4
......@@ -10092,8 +10092,8 @@ fn vectorTypeAssumeCapacity(
1009210092 .data = self.addTypeExtraAssumeCapacity(data),
1009310093 });
1009410094 if (self.useLibLlvm()) self.llvm.types.appendAssumeCapacity(switch (kind) {
10095 .normal => llvm.Type.vectorType,
10096 .scalable => llvm.Type.scalableVectorType,
10095 .normal => &llvm.Type.vectorType,
10096 .scalable => &llvm.Type.scalableVectorType,
1009710097 }(child.toLlvm(self), @intCast(len)));
1009810098 }
1009910099 return @enumFromInt(gop.index);
......@@ -11274,8 +11274,8 @@ fn gepConstAssumeCapacity(
1127411274 for (llvm_indices, indices) |*llvm_index, index| llvm_index.* = index.toLlvm(self);
1127511275
1127611276 self.llvm.constants.appendAssumeCapacity(switch (kind) {
11277 .normal => llvm.Type.constGEP,
11278 .inbounds => llvm.Type.constInBoundsGEP,
11277 .normal => &llvm.Type.constGEP,
11278 .inbounds => &llvm.Type.constInBoundsGEP,
1127911279 }(ty.toLlvm(self), base.toLlvm(self), llvm_indices.ptr, @intCast(llvm_indices.len)));
1128011280 }
1128111281 }
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
stage1/zig.h+76-13
......@@ -1176,10 +1176,10 @@ typedef signed __int128 zig_i128;
11761176
11771177#if zig_little_endian
11781178typedef struct { zig_align(16) uint64_t lo; uint64_t hi; } zig_u128;
1179typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128;
1179typedef struct { zig_align(16) uint64_t lo; int64_t hi; } zig_i128;
11801180#else
11811181typedef struct { zig_align(16) uint64_t hi; uint64_t lo; } zig_u128;
1182typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128;
1182typedef struct { zig_align(16) int64_t hi; uint64_t lo; } zig_i128;
11831183#endif
11841184
11851185#define zig_make_u128(hi, lo) ((zig_u128){ .h##i = (hi), .l##o = (lo) })
......@@ -3197,11 +3197,9 @@ zig_convert_builtin(zig_compiler_rt_f16, zig_f16, trunc, zig_f64,
31973197zig_convert_builtin(zig_f16, zig_f16, trunc, zig_f80, zig_f80, 2)
31983198zig_convert_builtin(zig_f16, zig_f16, trunc, zig_f128, zig_f128, 2)
31993199zig_convert_builtin(zig_f32, zig_f32, extend, zig_compiler_rt_f16, zig_f16, 2)
3200zig_convert_builtin(zig_f32, zig_f32, trunc, zig_f64, zig_f64, 2)
32013200zig_convert_builtin(zig_f32, zig_f32, trunc, zig_f80, zig_f80, 2)
32023201zig_convert_builtin(zig_f32, zig_f32, trunc, zig_f128, zig_f128, 2)
32033202zig_convert_builtin(zig_f64, zig_f64, extend, zig_compiler_rt_f16, zig_f16, 2)
3204zig_convert_builtin(zig_f64, zig_f64, extend, zig_f32, zig_f32, 2)
32053203zig_convert_builtin(zig_f64, zig_f64, trunc, zig_f80, zig_f80, 2)
32063204zig_convert_builtin(zig_f64, zig_f64, trunc, zig_f128, zig_f128, 2)
32073205zig_convert_builtin(zig_f80, zig_f80, extend, zig_f16, zig_f16, 2)
......@@ -3213,6 +3211,21 @@ zig_convert_builtin(zig_f128, zig_f128, extend, zig_f32,
32133211zig_convert_builtin(zig_f128, zig_f128, extend, zig_f64, zig_f64, 2)
32143212zig_convert_builtin(zig_f128, zig_f128, extend, zig_f80, zig_f80, 2)
32153213
3214#ifdef __ARM_EABI__
3215
3216zig_extern zig_callconv(pcs("aapcs")) zig_f32 __aeabi_d2f(zig_f64);
3217static inline zig_f32 zig_truncdfsf(zig_f64 arg) { return __aeabi_d2f(arg); }
3218
3219zig_extern zig_callconv(pcs("aapcs")) zig_f64 __aeabi_f2d(zig_f32);
3220static inline zig_f64 zig_extendsfdf(zig_f32 arg) { return __aeabi_f2d(arg); }
3221
3222#else /* __ARM_EABI__ */
3223
3224zig_convert_builtin(zig_f32, zig_f32, trunc, zig_f64, zig_f64, 2)
3225zig_convert_builtin(zig_f64, zig_f64, extend, zig_f32, zig_f32, 2)
3226
3227#endif /* __ARM_EABI__ */
3228
32163229#define zig_float_negate_builtin_0(w, c, sb) \
32173230 zig_expand_concat(zig_xor_, zig_repr_f##w)(arg, zig_make_f##w(-0x0.0p0, c sb))
32183231#define zig_float_negate_builtin_1(w, c, sb) -arg
......@@ -3255,17 +3268,11 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))
32553268 return lhs operator rhs; \
32563269 }
32573270
3258#define zig_float_builtins(w) \
3259 zig_convert_builtin( int32_t, int32_t, fix, zig_f##w, zig_f##w, ) \
3260 zig_convert_builtin(uint32_t, uint32_t, fixuns, zig_f##w, zig_f##w, ) \
3271#define zig_common_float_builtins(w) \
32613272 zig_convert_builtin( int64_t, int64_t, fix, zig_f##w, zig_f##w, ) \
3262 zig_convert_builtin(uint64_t, uint64_t, fixuns, zig_f##w, zig_f##w, ) \
32633273 zig_convert_builtin(zig_i128, zig_i128, fix, zig_f##w, zig_f##w, ) \
32643274 zig_convert_builtin(zig_u128, zig_u128, fixuns, zig_f##w, zig_f##w, ) \
3265 zig_convert_builtin(zig_f##w, zig_f##w, float, int32_t, int32_t, ) \
3266 zig_convert_builtin(zig_f##w, zig_f##w, floatun, uint32_t, uint32_t, ) \
32673275 zig_convert_builtin(zig_f##w, zig_f##w, float, int64_t, int64_t, ) \
3268 zig_convert_builtin(zig_f##w, zig_f##w, floatun, uint64_t, uint64_t, ) \
32693276 zig_convert_builtin(zig_f##w, zig_f##w, float, zig_i128, zig_i128, ) \
32703277 zig_convert_builtin(zig_f##w, zig_f##w, floatun, zig_u128, zig_u128, ) \
32713278 zig_expand_concat(zig_float_less_builtin_, zig_has_f##w)(f##w, cmp) \
......@@ -3309,12 +3316,68 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))
33093316 static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \
33103317 return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \
33113318 }
3319zig_common_float_builtins(16)
3320zig_common_float_builtins(32)
3321zig_common_float_builtins(64)
3322zig_common_float_builtins(80)
3323zig_common_float_builtins(128)
3324
3325#define zig_float_builtins(w) \
3326 zig_convert_builtin( int32_t, int32_t, fix, zig_f##w, zig_f##w, ) \
3327 zig_convert_builtin(uint32_t, uint32_t, fixuns, zig_f##w, zig_f##w, ) \
3328 zig_convert_builtin(uint64_t, uint64_t, fixuns, zig_f##w, zig_f##w, ) \
3329 zig_convert_builtin(zig_f##w, zig_f##w, float, int32_t, int32_t, ) \
3330 zig_convert_builtin(zig_f##w, zig_f##w, floatun, uint32_t, uint32_t, ) \
3331 zig_convert_builtin(zig_f##w, zig_f##w, floatun, uint64_t, uint64_t, )
33123332zig_float_builtins(16)
3313zig_float_builtins(32)
3314zig_float_builtins(64)
33153333zig_float_builtins(80)
33163334zig_float_builtins(128)
33173335
3336#ifdef __ARM_EABI__
3337
3338zig_extern zig_callconv(pcs("aapcs")) int32_t __aeabi_f2iz(zig_f32);
3339static inline int32_t zig_fixsfsi(zig_f32 arg) { return __aeabi_f2iz(arg); }
3340
3341zig_extern zig_callconv(pcs("aapcs")) uint32_t __aeabi_f2uiz(zig_f32);
3342static inline uint32_t zig_fixunssfsi(zig_f32 arg) { return __aeabi_f2uiz(arg); }
3343
3344zig_extern zig_callconv(pcs("aapcs")) uint64_t __aeabi_f2ulz(zig_f32);
3345static inline uint64_t zig_fixunssfdi(zig_f32 arg) { return __aeabi_f2ulz(arg); }
3346
3347zig_extern zig_callconv(pcs("aapcs")) zig_f32 __aeabi_i2f(int32_t);
3348static inline zig_f32 zig_floatsisf(int32_t arg) { return __aeabi_i2f(arg); }
3349
3350zig_extern zig_callconv(pcs("aapcs")) zig_f32 __aeabi_ui2f(uint32_t);
3351static inline zig_f32 zig_floatunsisf(uint32_t arg) { return __aeabi_ui2f(arg); }
3352
3353zig_extern zig_callconv(pcs("aapcs")) zig_f32 __aeabi_ul2f(uint64_t);
3354static inline zig_f32 zig_floatundisf(uint64_t arg) { return __aeabi_ul2f(arg); }
3355
3356zig_extern zig_callconv(pcs("aapcs")) int32_t __aeabi_d2iz(zig_f64);
3357static inline int32_t zig_fixdfsi(zig_f64 arg) { return __aeabi_d2iz(arg); }
3358
3359zig_extern zig_callconv(pcs("aapcs")) uint32_t __aeabi_d2uiz(zig_f64);
3360static inline uint32_t zig_fixunsdfsi(zig_f64 arg) { return __aeabi_d2uiz(arg); }
3361
3362zig_extern zig_callconv(pcs("aapcs")) uint64_t __aeabi_d2ulz(zig_f64);
3363static inline uint64_t zig_fixunsdfdi(zig_f64 arg) { return __aeabi_d2ulz(arg); }
3364
3365zig_extern zig_callconv(pcs("aapcs")) zig_f64 __aeabi_i2d(int32_t);
3366static inline zig_f64 zig_floatsidf(int32_t arg) { return __aeabi_i2d(arg); }
3367
3368zig_extern zig_callconv(pcs("aapcs")) zig_f64 __aeabi_ui2d(uint32_t);
3369static inline zig_f64 zig_floatunsidf(uint32_t arg) { return __aeabi_ui2d(arg); }
3370
3371zig_extern zig_callconv(pcs("aapcs")) zig_f64 __aeabi_ul2d(uint64_t);
3372static inline zig_f64 zig_floatundidf(uint64_t arg) { return __aeabi_ul2d(arg); }
3373
3374#else /* __ARM_EABI__ */
3375
3376zig_float_builtins(32)
3377zig_float_builtins(64)
3378
3379#endif /* __ARM_EABI__ */
3380
33183381/* ============================ Atomics Support ============================= */
33193382
33203383/* Note that atomics should be implemented as macros because most
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
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'