authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2023-11-24 17:09:08-08:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2023-11-24 17:09:08-08:00
log121d995fcb061272202649a964f4788a3c9c8305
tree197f5ff8d36fef30457a3566e6791719be723c29
parent84d58aaa1fc8508cb6ec9a455e8127ff13f17e16

frontend: move AstRlAnnotate to std.zig namespace


4 files changed, 1107 insertions(+), 1106 deletions(-)

lib/std/zig.zig+1
...@@ -18,6 +18,7 @@ pub const Ast = @import("zig/Ast.zig");...@@ -18,6 +18,7 @@ pub const Ast = @import("zig/Ast.zig");
18pub const system = @import("zig/system.zig");18pub const system = @import("zig/system.zig");
19pub const CrossTarget = @import("zig/CrossTarget.zig");19pub const CrossTarget = @import("zig/CrossTarget.zig");
20pub const BuiltinFn = @import("zig/BuiltinFn.zig");20pub const BuiltinFn = @import("zig/BuiltinFn.zig");
21pub const AstRlAnnotate = @import("zig/AstRlAnnotate.zig");
2122
22// Character literal parsing23// Character literal parsing
23pub const ParsedCharLiteral = string_literal.ParsedCharLiteral;24pub const ParsedCharLiteral = string_literal.ParsedCharLiteral;
lib/std/zig/AstRlAnnotate.zig created+1105
...@@ -0,0 +1,1105 @@
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 = std.zig.BuiltinFn;
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_destructure => {
207 const lhs_count = tree.extra_data[node_datas[node].lhs];
208 const all_lhs = tree.extra_data[node_datas[node].lhs + 1 ..][0..lhs_count];
209 for (all_lhs) |lhs| {
210 _ = try astrl.expr(lhs, block, ResultInfo.none);
211 }
212 // We don't need to gather any meaningful data here, because destructures always use RLS
213 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
214 return false;
215 },
216 .assign => {
217 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
218 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.typed_ptr);
219 return false;
220 },
221 .assign_shl,
222 .assign_shl_sat,
223 .assign_shr,
224 .assign_bit_and,
225 .assign_bit_or,
226 .assign_bit_xor,
227 .assign_div,
228 .assign_sub,
229 .assign_sub_wrap,
230 .assign_sub_sat,
231 .assign_mod,
232 .assign_add,
233 .assign_add_wrap,
234 .assign_add_sat,
235 .assign_mul,
236 .assign_mul_wrap,
237 .assign_mul_sat,
238 => {
239 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
240 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
241 return false;
242 },
243 .shl, .shr => {
244 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
245 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
246 return false;
247 },
248 .add,
249 .add_wrap,
250 .add_sat,
251 .sub,
252 .sub_wrap,
253 .sub_sat,
254 .mul,
255 .mul_wrap,
256 .mul_sat,
257 .div,
258 .mod,
259 .shl_sat,
260 .bit_and,
261 .bit_or,
262 .bit_xor,
263 .bang_equal,
264 .equal_equal,
265 .greater_than,
266 .greater_or_equal,
267 .less_than,
268 .less_or_equal,
269 .array_cat,
270 => {
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 .array_mult => {
276 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
277 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
278 return false;
279 },
280 .error_union, .merge_error_sets => {
281 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
282 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
283 return false;
284 },
285 .bool_and,
286 .bool_or,
287 => {
288 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
289 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
290 return false;
291 },
292 .bool_not => {
293 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
294 return false;
295 },
296 .bit_not, .negation, .negation_wrap => {
297 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
298 return false;
299 },
300
301 // These nodes are leaves and never consume a result location.
302 .identifier,
303 .string_literal,
304 .multiline_string_literal,
305 .number_literal,
306 .unreachable_literal,
307 .asm_simple,
308 .@"asm",
309 .enum_literal,
310 .error_value,
311 .anyframe_literal,
312 .@"continue",
313 .char_literal,
314 .error_set_decl,
315 => return false,
316
317 .builtin_call_two, .builtin_call_two_comma => {
318 if (node_datas[node].lhs == 0) {
319 return astrl.builtinCall(block, ri, node, &.{});
320 } else if (node_datas[node].rhs == 0) {
321 return astrl.builtinCall(block, ri, node, &.{node_datas[node].lhs});
322 } else {
323 return astrl.builtinCall(block, ri, node, &.{ node_datas[node].lhs, node_datas[node].rhs });
324 }
325 },
326 .builtin_call, .builtin_call_comma => {
327 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
328 return astrl.builtinCall(block, ri, node, params);
329 },
330
331 .call_one,
332 .call_one_comma,
333 .async_call_one,
334 .async_call_one_comma,
335 .call,
336 .call_comma,
337 .async_call,
338 .async_call_comma,
339 => {
340 var buf: [1]Ast.Node.Index = undefined;
341 const full = tree.fullCall(&buf, node).?;
342 _ = try astrl.expr(full.ast.fn_expr, block, ResultInfo.none);
343 for (full.ast.params) |param_node| {
344 _ = try astrl.expr(param_node, block, ResultInfo.type_only);
345 }
346 return switch (node_tags[node]) {
347 .call_one,
348 .call_one_comma,
349 .call,
350 .call_comma,
351 => false, // TODO: once function calls are passed result locations this will change
352 .async_call_one,
353 .async_call_one_comma,
354 .async_call,
355 .async_call_comma,
356 => ri.have_ptr, // always use result ptr for frames
357 else => unreachable,
358 };
359 },
360
361 .@"return" => {
362 if (node_datas[node].lhs != 0) {
363 const ret_val_consumes_rl = try astrl.expr(node_datas[node].lhs, block, ResultInfo.typed_ptr);
364 if (ret_val_consumes_rl) {
365 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
366 }
367 }
368 return false;
369 },
370
371 .field_access => {
372 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
373 return false;
374 },
375
376 .if_simple, .@"if" => {
377 const full = tree.fullIf(node).?;
378 if (full.error_token != null or full.payload_token != null) {
379 _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.none);
380 } else {
381 _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.type_only); // bool
382 }
383
384 if (full.ast.else_expr == 0) {
385 _ = try astrl.expr(full.ast.then_expr, block, ResultInfo.none);
386 return false;
387 } else {
388 const then_uses_rl = try astrl.expr(full.ast.then_expr, block, ri);
389 const else_uses_rl = try astrl.expr(full.ast.else_expr, block, ri);
390 const uses_rl = then_uses_rl or else_uses_rl;
391 if (uses_rl) try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
392 return uses_rl;
393 }
394 },
395
396 .while_simple, .while_cont, .@"while" => {
397 const full = tree.fullWhile(node).?;
398 const label: ?[]const u8 = if (full.label_token) |label_token| label: {
399 break :label try astrl.identString(label_token);
400 } else null;
401 if (full.error_token != null or full.payload_token != null) {
402 _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.none);
403 } else {
404 _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.type_only); // bool
405 }
406 var new_block: Block = .{
407 .parent = block,
408 .label = label,
409 .is_loop = true,
410 .ri = ri,
411 .consumes_res_ptr = false,
412 };
413 if (full.ast.cont_expr != 0) {
414 _ = try astrl.expr(full.ast.cont_expr, &new_block, ResultInfo.none);
415 }
416 _ = try astrl.expr(full.ast.then_expr, &new_block, ResultInfo.none);
417 const else_consumes_rl = if (full.ast.else_expr != 0) else_rl: {
418 break :else_rl try astrl.expr(full.ast.else_expr, block, ri);
419 } else false;
420 if (new_block.consumes_res_ptr or else_consumes_rl) {
421 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
422 return true;
423 } else {
424 return false;
425 }
426 },
427
428 .for_simple, .@"for" => {
429 const full = tree.fullFor(node).?;
430 const label: ?[]const u8 = if (full.label_token) |label_token| label: {
431 break :label try astrl.identString(label_token);
432 } else null;
433 for (full.ast.inputs) |input| {
434 if (node_tags[input] == .for_range) {
435 _ = try astrl.expr(node_datas[input].lhs, block, ResultInfo.type_only);
436 if (node_datas[input].rhs != 0) {
437 _ = try astrl.expr(node_datas[input].rhs, block, ResultInfo.type_only);
438 }
439 } else {
440 _ = try astrl.expr(input, block, ResultInfo.none);
441 }
442 }
443 var new_block: Block = .{
444 .parent = block,
445 .label = label,
446 .is_loop = true,
447 .ri = ri,
448 .consumes_res_ptr = false,
449 };
450 _ = try astrl.expr(full.ast.then_expr, &new_block, ResultInfo.none);
451 const else_consumes_rl = if (full.ast.else_expr != 0) else_rl: {
452 break :else_rl try astrl.expr(full.ast.else_expr, block, ri);
453 } else false;
454 if (new_block.consumes_res_ptr or else_consumes_rl) {
455 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
456 return true;
457 } else {
458 return false;
459 }
460 },
461
462 .slice_open => {
463 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
464 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
465 return false;
466 },
467 .slice => {
468 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
469 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
470 _ = try astrl.expr(extra.start, block, ResultInfo.type_only);
471 _ = try astrl.expr(extra.end, block, ResultInfo.type_only);
472 return false;
473 },
474 .slice_sentinel => {
475 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
476 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
477 _ = try astrl.expr(extra.start, block, ResultInfo.type_only);
478 if (extra.end != 0) {
479 _ = try astrl.expr(extra.end, block, ResultInfo.type_only);
480 }
481 _ = try astrl.expr(extra.sentinel, block, ResultInfo.none);
482 return false;
483 },
484 .deref => {
485 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
486 return false;
487 },
488 .address_of => {
489 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
490 return false;
491 },
492 .optional_type => {
493 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
494 return false;
495 },
496 .grouped_expression,
497 .@"try",
498 .@"await",
499 .@"nosuspend",
500 .unwrap_optional,
501 => return astrl.expr(node_datas[node].lhs, block, ri),
502
503 .block_two, .block_two_semicolon => {
504 if (node_datas[node].lhs == 0) {
505 return astrl.blockExpr(block, ri, node, &.{});
506 } else if (node_datas[node].rhs == 0) {
507 return astrl.blockExpr(block, ri, node, &.{node_datas[node].lhs});
508 } else {
509 return astrl.blockExpr(block, ri, node, &.{ node_datas[node].lhs, node_datas[node].rhs });
510 }
511 },
512 .block, .block_semicolon => {
513 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
514 return astrl.blockExpr(block, ri, node, statements);
515 },
516 .anyframe_type => {
517 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
518 return false;
519 },
520 .@"catch", .@"orelse" => {
521 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
522 const rhs_consumes_rl = try astrl.expr(node_datas[node].rhs, block, ri);
523 if (rhs_consumes_rl) {
524 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
525 }
526 return rhs_consumes_rl;
527 },
528
529 .ptr_type_aligned,
530 .ptr_type_sentinel,
531 .ptr_type,
532 .ptr_type_bit_range,
533 => {
534 const full = tree.fullPtrType(node).?;
535 _ = try astrl.expr(full.ast.child_type, block, ResultInfo.type_only);
536 if (full.ast.sentinel != 0) {
537 _ = try astrl.expr(full.ast.sentinel, block, ResultInfo.type_only);
538 }
539 if (full.ast.addrspace_node != 0) {
540 _ = try astrl.expr(full.ast.addrspace_node, block, ResultInfo.type_only);
541 }
542 if (full.ast.align_node != 0) {
543 _ = try astrl.expr(full.ast.align_node, block, ResultInfo.type_only);
544 }
545 if (full.ast.bit_range_start != 0) {
546 assert(full.ast.bit_range_end != 0);
547 _ = try astrl.expr(full.ast.bit_range_start, block, ResultInfo.type_only);
548 _ = try astrl.expr(full.ast.bit_range_end, block, ResultInfo.type_only);
549 }
550 return false;
551 },
552
553 .container_decl,
554 .container_decl_trailing,
555 .container_decl_arg,
556 .container_decl_arg_trailing,
557 .container_decl_two,
558 .container_decl_two_trailing,
559 .tagged_union,
560 .tagged_union_trailing,
561 .tagged_union_enum_tag,
562 .tagged_union_enum_tag_trailing,
563 .tagged_union_two,
564 .tagged_union_two_trailing,
565 => {
566 var buf: [2]Ast.Node.Index = undefined;
567 try astrl.containerDecl(block, tree.fullContainerDecl(&buf, node).?);
568 return false;
569 },
570
571 .@"break" => {
572 if (node_datas[node].rhs == 0) {
573 // Breaks with void are not interesting
574 return false;
575 }
576
577 var opt_cur_block = block;
578 if (node_datas[node].lhs == 0) {
579 // No label - we're breaking from a loop.
580 while (opt_cur_block) |cur_block| : (opt_cur_block = cur_block.parent) {
581 if (cur_block.is_loop) break;
582 }
583 } else {
584 const break_label = try astrl.identString(node_datas[node].lhs);
585 while (opt_cur_block) |cur_block| : (opt_cur_block = cur_block.parent) {
586 const block_label = cur_block.label orelse continue;
587 if (std.mem.eql(u8, block_label, break_label)) break;
588 }
589 }
590
591 if (opt_cur_block) |target_block| {
592 const consumes_break_rl = try astrl.expr(node_datas[node].rhs, block, target_block.ri);
593 if (consumes_break_rl) target_block.consumes_res_ptr = true;
594 } else {
595 // No corresponding scope to break from - AstGen will emit an error.
596 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
597 }
598
599 return false;
600 },
601
602 .array_type => {
603 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
604 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
605 return false;
606 },
607 .array_type_sentinel => {
608 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel);
609 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
610 _ = try astrl.expr(extra.elem_type, block, ResultInfo.type_only);
611 _ = try astrl.expr(extra.sentinel, block, ResultInfo.type_only);
612 return false;
613 },
614 .array_access => {
615 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
616 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
617 return false;
618 },
619 .@"comptime" => {
620 // AstGen will emit an error if the scope is already comptime, so we can assume it is
621 // not. This means the result location is not forwarded.
622 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
623 return false;
624 },
625 .@"switch", .switch_comma => {
626 const operand_node = node_datas[node].lhs;
627 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SubRange);
628 const case_nodes = tree.extra_data[extra.start..extra.end];
629
630 _ = try astrl.expr(operand_node, block, ResultInfo.none);
631
632 var any_prong_consumed_rl = false;
633 for (case_nodes) |case_node| {
634 const case = tree.fullSwitchCase(case_node).?;
635 for (case.ast.values) |item_node| {
636 if (node_tags[item_node] == .switch_range) {
637 _ = try astrl.expr(node_datas[item_node].lhs, block, ResultInfo.none);
638 _ = try astrl.expr(node_datas[item_node].rhs, block, ResultInfo.none);
639 } else {
640 _ = try astrl.expr(item_node, block, ResultInfo.none);
641 }
642 }
643 if (try astrl.expr(case.ast.target_expr, block, ri)) {
644 any_prong_consumed_rl = true;
645 }
646 }
647 if (any_prong_consumed_rl) {
648 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
649 }
650 return any_prong_consumed_rl;
651 },
652 .@"suspend" => {
653 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
654 return false;
655 },
656 .@"resume" => {
657 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
658 return false;
659 },
660
661 .array_init_one,
662 .array_init_one_comma,
663 .array_init_dot_two,
664 .array_init_dot_two_comma,
665 .array_init_dot,
666 .array_init_dot_comma,
667 .array_init,
668 .array_init_comma,
669 => {
670 var buf: [2]Ast.Node.Index = undefined;
671 const full = tree.fullArrayInit(&buf, node).?;
672
673 if (full.ast.type_expr != 0) {
674 // Explicitly typed init does not participate in RLS
675 _ = try astrl.expr(full.ast.type_expr, block, ResultInfo.none);
676 for (full.ast.elements) |elem_init| {
677 _ = try astrl.expr(elem_init, block, ResultInfo.type_only);
678 }
679 return false;
680 }
681
682 if (ri.have_type) {
683 // Always forward type information
684 // If we have a result pointer, we use and forward it
685 for (full.ast.elements) |elem_init| {
686 _ = try astrl.expr(elem_init, block, ri);
687 }
688 return ri.have_ptr;
689 } else {
690 // Untyped init does not consume result location
691 for (full.ast.elements) |elem_init| {
692 _ = try astrl.expr(elem_init, block, ResultInfo.none);
693 }
694 return false;
695 }
696 },
697
698 .struct_init_one,
699 .struct_init_one_comma,
700 .struct_init_dot_two,
701 .struct_init_dot_two_comma,
702 .struct_init_dot,
703 .struct_init_dot_comma,
704 .struct_init,
705 .struct_init_comma,
706 => {
707 var buf: [2]Ast.Node.Index = undefined;
708 const full = tree.fullStructInit(&buf, node).?;
709
710 if (full.ast.type_expr != 0) {
711 // Explicitly typed init does not participate in RLS
712 _ = try astrl.expr(full.ast.type_expr, block, ResultInfo.none);
713 for (full.ast.fields) |field_init| {
714 _ = try astrl.expr(field_init, block, ResultInfo.type_only);
715 }
716 return false;
717 }
718
719 if (ri.have_type) {
720 // Always forward type information
721 // If we have a result pointer, we use and forward it
722 for (full.ast.fields) |field_init| {
723 _ = try astrl.expr(field_init, block, ri);
724 }
725 return ri.have_ptr;
726 } else {
727 // Untyped init does not consume result location
728 for (full.ast.fields) |field_init| {
729 _ = try astrl.expr(field_init, block, ResultInfo.none);
730 }
731 return false;
732 }
733 },
734
735 .fn_proto_simple,
736 .fn_proto_multi,
737 .fn_proto_one,
738 .fn_proto,
739 .fn_decl,
740 => {
741 var buf: [1]Ast.Node.Index = undefined;
742 const full = tree.fullFnProto(&buf, node).?;
743 const body_node = if (node_tags[node] == .fn_decl) node_datas[node].rhs else 0;
744 {
745 var it = full.iterate(tree);
746 while (it.next()) |param| {
747 if (param.anytype_ellipsis3 == null) {
748 _ = try astrl.expr(param.type_expr, block, ResultInfo.type_only);
749 }
750 }
751 }
752 if (full.ast.align_expr != 0) {
753 _ = try astrl.expr(full.ast.align_expr, block, ResultInfo.type_only);
754 }
755 if (full.ast.addrspace_expr != 0) {
756 _ = try astrl.expr(full.ast.addrspace_expr, block, ResultInfo.type_only);
757 }
758 if (full.ast.section_expr != 0) {
759 _ = try astrl.expr(full.ast.section_expr, block, ResultInfo.type_only);
760 }
761 if (full.ast.callconv_expr != 0) {
762 _ = try astrl.expr(full.ast.callconv_expr, block, ResultInfo.type_only);
763 }
764 _ = try astrl.expr(full.ast.return_type, block, ResultInfo.type_only);
765 if (body_node != 0) {
766 _ = try astrl.expr(body_node, block, ResultInfo.none);
767 }
768 return false;
769 },
770 }
771}
772
773fn identString(astrl: *AstRlAnnotate, token: Ast.TokenIndex) ![]const u8 {
774 const tree = astrl.tree;
775 const token_tags = tree.tokens.items(.tag);
776 assert(token_tags[token] == .identifier);
777 const ident_name = tree.tokenSlice(token);
778 if (!std.mem.startsWith(u8, ident_name, "@")) {
779 return ident_name;
780 }
781 return std.zig.string_literal.parseAlloc(astrl.arena, ident_name[1..]) catch |err| switch (err) {
782 error.OutOfMemory => error.OutOfMemory,
783 error.InvalidLiteral => "", // This pass can safely return garbage on invalid AST
784 };
785}
786
787fn blockExpr(astrl: *AstRlAnnotate, parent_block: ?*Block, ri: ResultInfo, node: Ast.Node.Index, statements: []const Ast.Node.Index) !bool {
788 const tree = astrl.tree;
789 const token_tags = tree.tokens.items(.tag);
790 const main_tokens = tree.nodes.items(.main_token);
791
792 const lbrace = main_tokens[node];
793 if (token_tags[lbrace - 1] == .colon and
794 token_tags[lbrace - 2] == .identifier)
795 {
796 // Labeled block
797 var new_block: Block = .{
798 .parent = parent_block,
799 .label = try astrl.identString(lbrace - 2),
800 .is_loop = false,
801 .ri = ri,
802 .consumes_res_ptr = false,
803 };
804 for (statements) |statement| {
805 _ = try astrl.expr(statement, &new_block, ResultInfo.none);
806 }
807 if (new_block.consumes_res_ptr) {
808 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
809 }
810 return new_block.consumes_res_ptr;
811 } else {
812 // Unlabeled block
813 for (statements) |statement| {
814 _ = try astrl.expr(statement, parent_block, ResultInfo.none);
815 }
816 return false;
817 }
818}
819
820fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.Node.Index, args: []const Ast.Node.Index) !bool {
821 _ = ri; // Currently, no builtin consumes its result location.
822
823 const tree = astrl.tree;
824 const main_tokens = tree.nodes.items(.main_token);
825 const builtin_token = main_tokens[node];
826 const builtin_name = tree.tokenSlice(builtin_token);
827 const info = BuiltinFn.list.get(builtin_name) orelse return false;
828 if (info.param_count) |expected| {
829 if (expected != args.len) return false;
830 }
831 switch (info.tag) {
832 .import => return false,
833 .compile_log, .TypeOf => {
834 for (args) |arg_node| {
835 _ = try astrl.expr(arg_node, block, ResultInfo.none);
836 }
837 return false;
838 },
839 .as => {
840 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
841 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
842 return false;
843 },
844 .bit_cast => {
845 _ = try astrl.expr(args[0], block, ResultInfo.none);
846 return false;
847 },
848 .union_init => {
849 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
850 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
851 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
852 return false;
853 },
854 .c_import => {
855 _ = try astrl.expr(args[0], block, ResultInfo.none);
856 return false;
857 },
858 .min, .max => {
859 for (args) |arg_node| {
860 _ = try astrl.expr(arg_node, block, ResultInfo.none);
861 }
862 return false;
863 },
864 .@"export" => {
865 _ = try astrl.expr(args[0], block, ResultInfo.none);
866 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
867 return false;
868 },
869 .@"extern" => {
870 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
871 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
872 return false;
873 },
874 // These builtins take no args and do not consume the result pointer.
875 .src,
876 .This,
877 .return_address,
878 .error_return_trace,
879 .frame,
880 .breakpoint,
881 .in_comptime,
882 .panic,
883 .trap,
884 .c_va_start,
885 => return false,
886 // TODO: this is a workaround for llvm/llvm-project#68409
887 // Zig tracking issue: #16876
888 .frame_address => return true,
889 // These builtins take a single argument with a known result type, but do not consume their
890 // result pointer.
891 .size_of,
892 .bit_size_of,
893 .align_of,
894 .compile_error,
895 .set_eval_branch_quota,
896 .int_from_bool,
897 .int_from_error,
898 .error_from_int,
899 .embed_file,
900 .error_name,
901 .set_runtime_safety,
902 .Type,
903 .c_undef,
904 .c_include,
905 .wasm_memory_size,
906 .splat,
907 .fence,
908 .set_float_mode,
909 .set_align_stack,
910 .set_cold,
911 .type_info,
912 .work_item_id,
913 .work_group_size,
914 .work_group_id,
915 => {
916 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
917 return false;
918 },
919 // These builtins take a single argument with no result information and do not consume their
920 // result pointer.
921 .int_from_ptr,
922 .int_from_enum,
923 .sqrt,
924 .sin,
925 .cos,
926 .tan,
927 .exp,
928 .exp2,
929 .log,
930 .log2,
931 .log10,
932 .abs,
933 .floor,
934 .ceil,
935 .trunc,
936 .round,
937 .tag_name,
938 .type_name,
939 .Frame,
940 .frame_size,
941 .int_from_float,
942 .float_from_int,
943 .ptr_from_int,
944 .enum_from_int,
945 .float_cast,
946 .int_cast,
947 .truncate,
948 .error_cast,
949 .ptr_cast,
950 .align_cast,
951 .addrspace_cast,
952 .const_cast,
953 .volatile_cast,
954 .clz,
955 .ctz,
956 .pop_count,
957 .byte_swap,
958 .bit_reverse,
959 => {
960 _ = try astrl.expr(args[0], block, ResultInfo.none);
961 return false;
962 },
963 .div_exact,
964 .div_floor,
965 .div_trunc,
966 .mod,
967 .rem,
968 => {
969 _ = try astrl.expr(args[0], block, ResultInfo.none);
970 _ = try astrl.expr(args[1], block, ResultInfo.none);
971 return false;
972 },
973 .shl_exact, .shr_exact => {
974 _ = try astrl.expr(args[0], block, ResultInfo.none);
975 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
976 return false;
977 },
978 .bit_offset_of,
979 .offset_of,
980 .field_parent_ptr,
981 .has_decl,
982 .has_field,
983 .field,
984 => {
985 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
986 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
987 return false;
988 },
989 .wasm_memory_grow => {
990 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
991 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
992 return false;
993 },
994 .c_define => {
995 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
996 _ = try astrl.expr(args[1], block, ResultInfo.none);
997 return false;
998 },
999 .reduce => {
1000 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1001 _ = try astrl.expr(args[1], block, ResultInfo.none);
1002 return false;
1003 },
1004 .add_with_overflow, .sub_with_overflow, .mul_with_overflow, .shl_with_overflow => {
1005 _ = try astrl.expr(args[0], block, ResultInfo.none);
1006 _ = try astrl.expr(args[1], block, ResultInfo.none);
1007 return false;
1008 },
1009 .atomic_load => {
1010 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1011 _ = try astrl.expr(args[1], block, ResultInfo.none);
1012 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1013 return false;
1014 },
1015 .atomic_rmw => {
1016 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1017 _ = try astrl.expr(args[1], block, ResultInfo.none);
1018 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1019 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1020 _ = try astrl.expr(args[4], block, ResultInfo.type_only);
1021 return false;
1022 },
1023 .atomic_store => {
1024 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1025 _ = try astrl.expr(args[1], block, ResultInfo.none);
1026 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1027 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1028 return false;
1029 },
1030 .mul_add => {
1031 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1032 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1033 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1034 return false;
1035 },
1036 .call => {
1037 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1038 _ = try astrl.expr(args[1], block, ResultInfo.none);
1039 _ = try astrl.expr(args[2], block, ResultInfo.none);
1040 return false;
1041 },
1042 .memcpy => {
1043 _ = try astrl.expr(args[0], block, ResultInfo.none);
1044 _ = try astrl.expr(args[1], block, ResultInfo.none);
1045 return false;
1046 },
1047 .memset => {
1048 _ = try astrl.expr(args[0], block, ResultInfo.none);
1049 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1050 return false;
1051 },
1052 .shuffle => {
1053 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1054 _ = try astrl.expr(args[1], block, ResultInfo.none);
1055 _ = try astrl.expr(args[2], block, ResultInfo.none);
1056 _ = try astrl.expr(args[3], block, ResultInfo.none);
1057 return false;
1058 },
1059 .select => {
1060 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1061 _ = try astrl.expr(args[1], block, ResultInfo.none);
1062 _ = try astrl.expr(args[2], block, ResultInfo.none);
1063 _ = try astrl.expr(args[3], block, ResultInfo.none);
1064 return false;
1065 },
1066 .async_call => {
1067 _ = try astrl.expr(args[0], block, ResultInfo.none);
1068 _ = try astrl.expr(args[1], block, ResultInfo.none);
1069 _ = try astrl.expr(args[2], block, ResultInfo.none);
1070 _ = try astrl.expr(args[3], block, ResultInfo.none);
1071 return false; // buffer passed as arg for frame data
1072 },
1073 .Vector => {
1074 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1075 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1076 return false;
1077 },
1078 .prefetch => {
1079 _ = try astrl.expr(args[0], block, ResultInfo.none);
1080 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1081 return false;
1082 },
1083 .c_va_arg => {
1084 _ = try astrl.expr(args[0], block, ResultInfo.none);
1085 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1086 return false;
1087 },
1088 .c_va_copy => {
1089 _ = try astrl.expr(args[0], block, ResultInfo.none);
1090 return false;
1091 },
1092 .c_va_end => {
1093 _ = try astrl.expr(args[0], block, ResultInfo.none);
1094 return false;
1095 },
1096 .cmpxchg_strong, .cmpxchg_weak => {
1097 _ = try astrl.expr(args[0], block, ResultInfo.none);
1098 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1099 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1100 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1101 _ = try astrl.expr(args[4], block, ResultInfo.type_only);
1102 return false;
1103 },
1104 }
1105}
src/AstGen.zig+1-1
...@@ -14,7 +14,7 @@ const isPrimitive = std.zig.primitives.isPrimitive;...@@ -14,7 +14,7 @@ const isPrimitive = std.zig.primitives.isPrimitive;
1414
15const Zir = @import("Zir.zig");15const Zir = @import("Zir.zig");
16const BuiltinFn = std.zig.BuiltinFn;16const BuiltinFn = std.zig.BuiltinFn;
17const AstRlAnnotate = @import("AstRlAnnotate.zig");17const AstRlAnnotate = std.zig.AstRlAnnotate;
1818
19gpa: Allocator,19gpa: Allocator,
20tree: *const Ast,20tree: *const Ast,
src/AstRlAnnotate.zig deleted-1105
...@@ -1,1105 +0,0 @@
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 = std.zig.BuiltinFn;
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_destructure => {
207 const lhs_count = tree.extra_data[node_datas[node].lhs];
208 const all_lhs = tree.extra_data[node_datas[node].lhs + 1 ..][0..lhs_count];
209 for (all_lhs) |lhs| {
210 _ = try astrl.expr(lhs, block, ResultInfo.none);
211 }
212 // We don't need to gather any meaningful data here, because destructures always use RLS
213 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
214 return false;
215 },
216 .assign => {
217 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
218 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.typed_ptr);
219 return false;
220 },
221 .assign_shl,
222 .assign_shl_sat,
223 .assign_shr,
224 .assign_bit_and,
225 .assign_bit_or,
226 .assign_bit_xor,
227 .assign_div,
228 .assign_sub,
229 .assign_sub_wrap,
230 .assign_sub_sat,
231 .assign_mod,
232 .assign_add,
233 .assign_add_wrap,
234 .assign_add_sat,
235 .assign_mul,
236 .assign_mul_wrap,
237 .assign_mul_sat,
238 => {
239 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
240 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
241 return false;
242 },
243 .shl, .shr => {
244 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
245 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
246 return false;
247 },
248 .add,
249 .add_wrap,
250 .add_sat,
251 .sub,
252 .sub_wrap,
253 .sub_sat,
254 .mul,
255 .mul_wrap,
256 .mul_sat,
257 .div,
258 .mod,
259 .shl_sat,
260 .bit_and,
261 .bit_or,
262 .bit_xor,
263 .bang_equal,
264 .equal_equal,
265 .greater_than,
266 .greater_or_equal,
267 .less_than,
268 .less_or_equal,
269 .array_cat,
270 => {
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 .array_mult => {
276 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
277 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
278 return false;
279 },
280 .error_union, .merge_error_sets => {
281 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
282 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
283 return false;
284 },
285 .bool_and,
286 .bool_or,
287 => {
288 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
289 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
290 return false;
291 },
292 .bool_not => {
293 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
294 return false;
295 },
296 .bit_not, .negation, .negation_wrap => {
297 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
298 return false;
299 },
300
301 // These nodes are leaves and never consume a result location.
302 .identifier,
303 .string_literal,
304 .multiline_string_literal,
305 .number_literal,
306 .unreachable_literal,
307 .asm_simple,
308 .@"asm",
309 .enum_literal,
310 .error_value,
311 .anyframe_literal,
312 .@"continue",
313 .char_literal,
314 .error_set_decl,
315 => return false,
316
317 .builtin_call_two, .builtin_call_two_comma => {
318 if (node_datas[node].lhs == 0) {
319 return astrl.builtinCall(block, ri, node, &.{});
320 } else if (node_datas[node].rhs == 0) {
321 return astrl.builtinCall(block, ri, node, &.{node_datas[node].lhs});
322 } else {
323 return astrl.builtinCall(block, ri, node, &.{ node_datas[node].lhs, node_datas[node].rhs });
324 }
325 },
326 .builtin_call, .builtin_call_comma => {
327 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
328 return astrl.builtinCall(block, ri, node, params);
329 },
330
331 .call_one,
332 .call_one_comma,
333 .async_call_one,
334 .async_call_one_comma,
335 .call,
336 .call_comma,
337 .async_call,
338 .async_call_comma,
339 => {
340 var buf: [1]Ast.Node.Index = undefined;
341 const full = tree.fullCall(&buf, node).?;
342 _ = try astrl.expr(full.ast.fn_expr, block, ResultInfo.none);
343 for (full.ast.params) |param_node| {
344 _ = try astrl.expr(param_node, block, ResultInfo.type_only);
345 }
346 return switch (node_tags[node]) {
347 .call_one,
348 .call_one_comma,
349 .call,
350 .call_comma,
351 => false, // TODO: once function calls are passed result locations this will change
352 .async_call_one,
353 .async_call_one_comma,
354 .async_call,
355 .async_call_comma,
356 => ri.have_ptr, // always use result ptr for frames
357 else => unreachable,
358 };
359 },
360
361 .@"return" => {
362 if (node_datas[node].lhs != 0) {
363 const ret_val_consumes_rl = try astrl.expr(node_datas[node].lhs, block, ResultInfo.typed_ptr);
364 if (ret_val_consumes_rl) {
365 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
366 }
367 }
368 return false;
369 },
370
371 .field_access => {
372 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
373 return false;
374 },
375
376 .if_simple, .@"if" => {
377 const full = tree.fullIf(node).?;
378 if (full.error_token != null or full.payload_token != null) {
379 _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.none);
380 } else {
381 _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.type_only); // bool
382 }
383
384 if (full.ast.else_expr == 0) {
385 _ = try astrl.expr(full.ast.then_expr, block, ResultInfo.none);
386 return false;
387 } else {
388 const then_uses_rl = try astrl.expr(full.ast.then_expr, block, ri);
389 const else_uses_rl = try astrl.expr(full.ast.else_expr, block, ri);
390 const uses_rl = then_uses_rl or else_uses_rl;
391 if (uses_rl) try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
392 return uses_rl;
393 }
394 },
395
396 .while_simple, .while_cont, .@"while" => {
397 const full = tree.fullWhile(node).?;
398 const label: ?[]const u8 = if (full.label_token) |label_token| label: {
399 break :label try astrl.identString(label_token);
400 } else null;
401 if (full.error_token != null or full.payload_token != null) {
402 _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.none);
403 } else {
404 _ = try astrl.expr(full.ast.cond_expr, block, ResultInfo.type_only); // bool
405 }
406 var new_block: Block = .{
407 .parent = block,
408 .label = label,
409 .is_loop = true,
410 .ri = ri,
411 .consumes_res_ptr = false,
412 };
413 if (full.ast.cont_expr != 0) {
414 _ = try astrl.expr(full.ast.cont_expr, &new_block, ResultInfo.none);
415 }
416 _ = try astrl.expr(full.ast.then_expr, &new_block, ResultInfo.none);
417 const else_consumes_rl = if (full.ast.else_expr != 0) else_rl: {
418 break :else_rl try astrl.expr(full.ast.else_expr, block, ri);
419 } else false;
420 if (new_block.consumes_res_ptr or else_consumes_rl) {
421 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
422 return true;
423 } else {
424 return false;
425 }
426 },
427
428 .for_simple, .@"for" => {
429 const full = tree.fullFor(node).?;
430 const label: ?[]const u8 = if (full.label_token) |label_token| label: {
431 break :label try astrl.identString(label_token);
432 } else null;
433 for (full.ast.inputs) |input| {
434 if (node_tags[input] == .for_range) {
435 _ = try astrl.expr(node_datas[input].lhs, block, ResultInfo.type_only);
436 if (node_datas[input].rhs != 0) {
437 _ = try astrl.expr(node_datas[input].rhs, block, ResultInfo.type_only);
438 }
439 } else {
440 _ = try astrl.expr(input, block, ResultInfo.none);
441 }
442 }
443 var new_block: Block = .{
444 .parent = block,
445 .label = label,
446 .is_loop = true,
447 .ri = ri,
448 .consumes_res_ptr = false,
449 };
450 _ = try astrl.expr(full.ast.then_expr, &new_block, ResultInfo.none);
451 const else_consumes_rl = if (full.ast.else_expr != 0) else_rl: {
452 break :else_rl try astrl.expr(full.ast.else_expr, block, ri);
453 } else false;
454 if (new_block.consumes_res_ptr or else_consumes_rl) {
455 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
456 return true;
457 } else {
458 return false;
459 }
460 },
461
462 .slice_open => {
463 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
464 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
465 return false;
466 },
467 .slice => {
468 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
469 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
470 _ = try astrl.expr(extra.start, block, ResultInfo.type_only);
471 _ = try astrl.expr(extra.end, block, ResultInfo.type_only);
472 return false;
473 },
474 .slice_sentinel => {
475 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
476 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
477 _ = try astrl.expr(extra.start, block, ResultInfo.type_only);
478 if (extra.end != 0) {
479 _ = try astrl.expr(extra.end, block, ResultInfo.type_only);
480 }
481 _ = try astrl.expr(extra.sentinel, block, ResultInfo.none);
482 return false;
483 },
484 .deref => {
485 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
486 return false;
487 },
488 .address_of => {
489 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
490 return false;
491 },
492 .optional_type => {
493 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
494 return false;
495 },
496 .grouped_expression,
497 .@"try",
498 .@"await",
499 .@"nosuspend",
500 .unwrap_optional,
501 => return astrl.expr(node_datas[node].lhs, block, ri),
502
503 .block_two, .block_two_semicolon => {
504 if (node_datas[node].lhs == 0) {
505 return astrl.blockExpr(block, ri, node, &.{});
506 } else if (node_datas[node].rhs == 0) {
507 return astrl.blockExpr(block, ri, node, &.{node_datas[node].lhs});
508 } else {
509 return astrl.blockExpr(block, ri, node, &.{ node_datas[node].lhs, node_datas[node].rhs });
510 }
511 },
512 .block, .block_semicolon => {
513 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
514 return astrl.blockExpr(block, ri, node, statements);
515 },
516 .anyframe_type => {
517 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
518 return false;
519 },
520 .@"catch", .@"orelse" => {
521 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
522 const rhs_consumes_rl = try astrl.expr(node_datas[node].rhs, block, ri);
523 if (rhs_consumes_rl) {
524 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
525 }
526 return rhs_consumes_rl;
527 },
528
529 .ptr_type_aligned,
530 .ptr_type_sentinel,
531 .ptr_type,
532 .ptr_type_bit_range,
533 => {
534 const full = tree.fullPtrType(node).?;
535 _ = try astrl.expr(full.ast.child_type, block, ResultInfo.type_only);
536 if (full.ast.sentinel != 0) {
537 _ = try astrl.expr(full.ast.sentinel, block, ResultInfo.type_only);
538 }
539 if (full.ast.addrspace_node != 0) {
540 _ = try astrl.expr(full.ast.addrspace_node, block, ResultInfo.type_only);
541 }
542 if (full.ast.align_node != 0) {
543 _ = try astrl.expr(full.ast.align_node, block, ResultInfo.type_only);
544 }
545 if (full.ast.bit_range_start != 0) {
546 assert(full.ast.bit_range_end != 0);
547 _ = try astrl.expr(full.ast.bit_range_start, block, ResultInfo.type_only);
548 _ = try astrl.expr(full.ast.bit_range_end, block, ResultInfo.type_only);
549 }
550 return false;
551 },
552
553 .container_decl,
554 .container_decl_trailing,
555 .container_decl_arg,
556 .container_decl_arg_trailing,
557 .container_decl_two,
558 .container_decl_two_trailing,
559 .tagged_union,
560 .tagged_union_trailing,
561 .tagged_union_enum_tag,
562 .tagged_union_enum_tag_trailing,
563 .tagged_union_two,
564 .tagged_union_two_trailing,
565 => {
566 var buf: [2]Ast.Node.Index = undefined;
567 try astrl.containerDecl(block, tree.fullContainerDecl(&buf, node).?);
568 return false;
569 },
570
571 .@"break" => {
572 if (node_datas[node].rhs == 0) {
573 // Breaks with void are not interesting
574 return false;
575 }
576
577 var opt_cur_block = block;
578 if (node_datas[node].lhs == 0) {
579 // No label - we're breaking from a loop.
580 while (opt_cur_block) |cur_block| : (opt_cur_block = cur_block.parent) {
581 if (cur_block.is_loop) break;
582 }
583 } else {
584 const break_label = try astrl.identString(node_datas[node].lhs);
585 while (opt_cur_block) |cur_block| : (opt_cur_block = cur_block.parent) {
586 const block_label = cur_block.label orelse continue;
587 if (std.mem.eql(u8, block_label, break_label)) break;
588 }
589 }
590
591 if (opt_cur_block) |target_block| {
592 const consumes_break_rl = try astrl.expr(node_datas[node].rhs, block, target_block.ri);
593 if (consumes_break_rl) target_block.consumes_res_ptr = true;
594 } else {
595 // No corresponding scope to break from - AstGen will emit an error.
596 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.none);
597 }
598
599 return false;
600 },
601
602 .array_type => {
603 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
604 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
605 return false;
606 },
607 .array_type_sentinel => {
608 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel);
609 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.type_only);
610 _ = try astrl.expr(extra.elem_type, block, ResultInfo.type_only);
611 _ = try astrl.expr(extra.sentinel, block, ResultInfo.type_only);
612 return false;
613 },
614 .array_access => {
615 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
616 _ = try astrl.expr(node_datas[node].rhs, block, ResultInfo.type_only);
617 return false;
618 },
619 .@"comptime" => {
620 // AstGen will emit an error if the scope is already comptime, so we can assume it is
621 // not. This means the result location is not forwarded.
622 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
623 return false;
624 },
625 .@"switch", .switch_comma => {
626 const operand_node = node_datas[node].lhs;
627 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SubRange);
628 const case_nodes = tree.extra_data[extra.start..extra.end];
629
630 _ = try astrl.expr(operand_node, block, ResultInfo.none);
631
632 var any_prong_consumed_rl = false;
633 for (case_nodes) |case_node| {
634 const case = tree.fullSwitchCase(case_node).?;
635 for (case.ast.values) |item_node| {
636 if (node_tags[item_node] == .switch_range) {
637 _ = try astrl.expr(node_datas[item_node].lhs, block, ResultInfo.none);
638 _ = try astrl.expr(node_datas[item_node].rhs, block, ResultInfo.none);
639 } else {
640 _ = try astrl.expr(item_node, block, ResultInfo.none);
641 }
642 }
643 if (try astrl.expr(case.ast.target_expr, block, ri)) {
644 any_prong_consumed_rl = true;
645 }
646 }
647 if (any_prong_consumed_rl) {
648 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
649 }
650 return any_prong_consumed_rl;
651 },
652 .@"suspend" => {
653 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
654 return false;
655 },
656 .@"resume" => {
657 _ = try astrl.expr(node_datas[node].lhs, block, ResultInfo.none);
658 return false;
659 },
660
661 .array_init_one,
662 .array_init_one_comma,
663 .array_init_dot_two,
664 .array_init_dot_two_comma,
665 .array_init_dot,
666 .array_init_dot_comma,
667 .array_init,
668 .array_init_comma,
669 => {
670 var buf: [2]Ast.Node.Index = undefined;
671 const full = tree.fullArrayInit(&buf, node).?;
672
673 if (full.ast.type_expr != 0) {
674 // Explicitly typed init does not participate in RLS
675 _ = try astrl.expr(full.ast.type_expr, block, ResultInfo.none);
676 for (full.ast.elements) |elem_init| {
677 _ = try astrl.expr(elem_init, block, ResultInfo.type_only);
678 }
679 return false;
680 }
681
682 if (ri.have_type) {
683 // Always forward type information
684 // If we have a result pointer, we use and forward it
685 for (full.ast.elements) |elem_init| {
686 _ = try astrl.expr(elem_init, block, ri);
687 }
688 return ri.have_ptr;
689 } else {
690 // Untyped init does not consume result location
691 for (full.ast.elements) |elem_init| {
692 _ = try astrl.expr(elem_init, block, ResultInfo.none);
693 }
694 return false;
695 }
696 },
697
698 .struct_init_one,
699 .struct_init_one_comma,
700 .struct_init_dot_two,
701 .struct_init_dot_two_comma,
702 .struct_init_dot,
703 .struct_init_dot_comma,
704 .struct_init,
705 .struct_init_comma,
706 => {
707 var buf: [2]Ast.Node.Index = undefined;
708 const full = tree.fullStructInit(&buf, node).?;
709
710 if (full.ast.type_expr != 0) {
711 // Explicitly typed init does not participate in RLS
712 _ = try astrl.expr(full.ast.type_expr, block, ResultInfo.none);
713 for (full.ast.fields) |field_init| {
714 _ = try astrl.expr(field_init, block, ResultInfo.type_only);
715 }
716 return false;
717 }
718
719 if (ri.have_type) {
720 // Always forward type information
721 // If we have a result pointer, we use and forward it
722 for (full.ast.fields) |field_init| {
723 _ = try astrl.expr(field_init, block, ri);
724 }
725 return ri.have_ptr;
726 } else {
727 // Untyped init does not consume result location
728 for (full.ast.fields) |field_init| {
729 _ = try astrl.expr(field_init, block, ResultInfo.none);
730 }
731 return false;
732 }
733 },
734
735 .fn_proto_simple,
736 .fn_proto_multi,
737 .fn_proto_one,
738 .fn_proto,
739 .fn_decl,
740 => {
741 var buf: [1]Ast.Node.Index = undefined;
742 const full = tree.fullFnProto(&buf, node).?;
743 const body_node = if (node_tags[node] == .fn_decl) node_datas[node].rhs else 0;
744 {
745 var it = full.iterate(tree);
746 while (it.next()) |param| {
747 if (param.anytype_ellipsis3 == null) {
748 _ = try astrl.expr(param.type_expr, block, ResultInfo.type_only);
749 }
750 }
751 }
752 if (full.ast.align_expr != 0) {
753 _ = try astrl.expr(full.ast.align_expr, block, ResultInfo.type_only);
754 }
755 if (full.ast.addrspace_expr != 0) {
756 _ = try astrl.expr(full.ast.addrspace_expr, block, ResultInfo.type_only);
757 }
758 if (full.ast.section_expr != 0) {
759 _ = try astrl.expr(full.ast.section_expr, block, ResultInfo.type_only);
760 }
761 if (full.ast.callconv_expr != 0) {
762 _ = try astrl.expr(full.ast.callconv_expr, block, ResultInfo.type_only);
763 }
764 _ = try astrl.expr(full.ast.return_type, block, ResultInfo.type_only);
765 if (body_node != 0) {
766 _ = try astrl.expr(body_node, block, ResultInfo.none);
767 }
768 return false;
769 },
770 }
771}
772
773fn identString(astrl: *AstRlAnnotate, token: Ast.TokenIndex) ![]const u8 {
774 const tree = astrl.tree;
775 const token_tags = tree.tokens.items(.tag);
776 assert(token_tags[token] == .identifier);
777 const ident_name = tree.tokenSlice(token);
778 if (!std.mem.startsWith(u8, ident_name, "@")) {
779 return ident_name;
780 }
781 return std.zig.string_literal.parseAlloc(astrl.arena, ident_name[1..]) catch |err| switch (err) {
782 error.OutOfMemory => error.OutOfMemory,
783 error.InvalidLiteral => "", // This pass can safely return garbage on invalid AST
784 };
785}
786
787fn blockExpr(astrl: *AstRlAnnotate, parent_block: ?*Block, ri: ResultInfo, node: Ast.Node.Index, statements: []const Ast.Node.Index) !bool {
788 const tree = astrl.tree;
789 const token_tags = tree.tokens.items(.tag);
790 const main_tokens = tree.nodes.items(.main_token);
791
792 const lbrace = main_tokens[node];
793 if (token_tags[lbrace - 1] == .colon and
794 token_tags[lbrace - 2] == .identifier)
795 {
796 // Labeled block
797 var new_block: Block = .{
798 .parent = parent_block,
799 .label = try astrl.identString(lbrace - 2),
800 .is_loop = false,
801 .ri = ri,
802 .consumes_res_ptr = false,
803 };
804 for (statements) |statement| {
805 _ = try astrl.expr(statement, &new_block, ResultInfo.none);
806 }
807 if (new_block.consumes_res_ptr) {
808 try astrl.nodes_need_rl.putNoClobber(astrl.gpa, node, {});
809 }
810 return new_block.consumes_res_ptr;
811 } else {
812 // Unlabeled block
813 for (statements) |statement| {
814 _ = try astrl.expr(statement, parent_block, ResultInfo.none);
815 }
816 return false;
817 }
818}
819
820fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.Node.Index, args: []const Ast.Node.Index) !bool {
821 _ = ri; // Currently, no builtin consumes its result location.
822
823 const tree = astrl.tree;
824 const main_tokens = tree.nodes.items(.main_token);
825 const builtin_token = main_tokens[node];
826 const builtin_name = tree.tokenSlice(builtin_token);
827 const info = BuiltinFn.list.get(builtin_name) orelse return false;
828 if (info.param_count) |expected| {
829 if (expected != args.len) return false;
830 }
831 switch (info.tag) {
832 .import => return false,
833 .compile_log, .TypeOf => {
834 for (args) |arg_node| {
835 _ = try astrl.expr(arg_node, block, ResultInfo.none);
836 }
837 return false;
838 },
839 .as => {
840 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
841 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
842 return false;
843 },
844 .bit_cast => {
845 _ = try astrl.expr(args[0], block, ResultInfo.none);
846 return false;
847 },
848 .union_init => {
849 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
850 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
851 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
852 return false;
853 },
854 .c_import => {
855 _ = try astrl.expr(args[0], block, ResultInfo.none);
856 return false;
857 },
858 .min, .max => {
859 for (args) |arg_node| {
860 _ = try astrl.expr(arg_node, block, ResultInfo.none);
861 }
862 return false;
863 },
864 .@"export" => {
865 _ = try astrl.expr(args[0], block, ResultInfo.none);
866 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
867 return false;
868 },
869 .@"extern" => {
870 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
871 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
872 return false;
873 },
874 // These builtins take no args and do not consume the result pointer.
875 .src,
876 .This,
877 .return_address,
878 .error_return_trace,
879 .frame,
880 .breakpoint,
881 .in_comptime,
882 .panic,
883 .trap,
884 .c_va_start,
885 => return false,
886 // TODO: this is a workaround for llvm/llvm-project#68409
887 // Zig tracking issue: #16876
888 .frame_address => return true,
889 // These builtins take a single argument with a known result type, but do not consume their
890 // result pointer.
891 .size_of,
892 .bit_size_of,
893 .align_of,
894 .compile_error,
895 .set_eval_branch_quota,
896 .int_from_bool,
897 .int_from_error,
898 .error_from_int,
899 .embed_file,
900 .error_name,
901 .set_runtime_safety,
902 .Type,
903 .c_undef,
904 .c_include,
905 .wasm_memory_size,
906 .splat,
907 .fence,
908 .set_float_mode,
909 .set_align_stack,
910 .set_cold,
911 .type_info,
912 .work_item_id,
913 .work_group_size,
914 .work_group_id,
915 => {
916 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
917 return false;
918 },
919 // These builtins take a single argument with no result information and do not consume their
920 // result pointer.
921 .int_from_ptr,
922 .int_from_enum,
923 .sqrt,
924 .sin,
925 .cos,
926 .tan,
927 .exp,
928 .exp2,
929 .log,
930 .log2,
931 .log10,
932 .abs,
933 .floor,
934 .ceil,
935 .trunc,
936 .round,
937 .tag_name,
938 .type_name,
939 .Frame,
940 .frame_size,
941 .int_from_float,
942 .float_from_int,
943 .ptr_from_int,
944 .enum_from_int,
945 .float_cast,
946 .int_cast,
947 .truncate,
948 .error_cast,
949 .ptr_cast,
950 .align_cast,
951 .addrspace_cast,
952 .const_cast,
953 .volatile_cast,
954 .clz,
955 .ctz,
956 .pop_count,
957 .byte_swap,
958 .bit_reverse,
959 => {
960 _ = try astrl.expr(args[0], block, ResultInfo.none);
961 return false;
962 },
963 .div_exact,
964 .div_floor,
965 .div_trunc,
966 .mod,
967 .rem,
968 => {
969 _ = try astrl.expr(args[0], block, ResultInfo.none);
970 _ = try astrl.expr(args[1], block, ResultInfo.none);
971 return false;
972 },
973 .shl_exact, .shr_exact => {
974 _ = try astrl.expr(args[0], block, ResultInfo.none);
975 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
976 return false;
977 },
978 .bit_offset_of,
979 .offset_of,
980 .field_parent_ptr,
981 .has_decl,
982 .has_field,
983 .field,
984 => {
985 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
986 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
987 return false;
988 },
989 .wasm_memory_grow => {
990 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
991 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
992 return false;
993 },
994 .c_define => {
995 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
996 _ = try astrl.expr(args[1], block, ResultInfo.none);
997 return false;
998 },
999 .reduce => {
1000 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1001 _ = try astrl.expr(args[1], block, ResultInfo.none);
1002 return false;
1003 },
1004 .add_with_overflow, .sub_with_overflow, .mul_with_overflow, .shl_with_overflow => {
1005 _ = try astrl.expr(args[0], block, ResultInfo.none);
1006 _ = try astrl.expr(args[1], block, ResultInfo.none);
1007 return false;
1008 },
1009 .atomic_load => {
1010 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1011 _ = try astrl.expr(args[1], block, ResultInfo.none);
1012 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1013 return false;
1014 },
1015 .atomic_rmw => {
1016 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1017 _ = try astrl.expr(args[1], block, ResultInfo.none);
1018 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1019 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1020 _ = try astrl.expr(args[4], block, ResultInfo.type_only);
1021 return false;
1022 },
1023 .atomic_store => {
1024 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1025 _ = try astrl.expr(args[1], block, ResultInfo.none);
1026 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1027 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1028 return false;
1029 },
1030 .mul_add => {
1031 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1032 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1033 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1034 return false;
1035 },
1036 .call => {
1037 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1038 _ = try astrl.expr(args[1], block, ResultInfo.none);
1039 _ = try astrl.expr(args[2], block, ResultInfo.none);
1040 return false;
1041 },
1042 .memcpy => {
1043 _ = try astrl.expr(args[0], block, ResultInfo.none);
1044 _ = try astrl.expr(args[1], block, ResultInfo.none);
1045 return false;
1046 },
1047 .memset => {
1048 _ = try astrl.expr(args[0], block, ResultInfo.none);
1049 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1050 return false;
1051 },
1052 .shuffle => {
1053 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1054 _ = try astrl.expr(args[1], block, ResultInfo.none);
1055 _ = try astrl.expr(args[2], block, ResultInfo.none);
1056 _ = try astrl.expr(args[3], block, ResultInfo.none);
1057 return false;
1058 },
1059 .select => {
1060 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1061 _ = try astrl.expr(args[1], block, ResultInfo.none);
1062 _ = try astrl.expr(args[2], block, ResultInfo.none);
1063 _ = try astrl.expr(args[3], block, ResultInfo.none);
1064 return false;
1065 },
1066 .async_call => {
1067 _ = try astrl.expr(args[0], block, ResultInfo.none);
1068 _ = try astrl.expr(args[1], block, ResultInfo.none);
1069 _ = try astrl.expr(args[2], block, ResultInfo.none);
1070 _ = try astrl.expr(args[3], block, ResultInfo.none);
1071 return false; // buffer passed as arg for frame data
1072 },
1073 .Vector => {
1074 _ = try astrl.expr(args[0], block, ResultInfo.type_only);
1075 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1076 return false;
1077 },
1078 .prefetch => {
1079 _ = try astrl.expr(args[0], block, ResultInfo.none);
1080 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1081 return false;
1082 },
1083 .c_va_arg => {
1084 _ = try astrl.expr(args[0], block, ResultInfo.none);
1085 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1086 return false;
1087 },
1088 .c_va_copy => {
1089 _ = try astrl.expr(args[0], block, ResultInfo.none);
1090 return false;
1091 },
1092 .c_va_end => {
1093 _ = try astrl.expr(args[0], block, ResultInfo.none);
1094 return false;
1095 },
1096 .cmpxchg_strong, .cmpxchg_weak => {
1097 _ = try astrl.expr(args[0], block, ResultInfo.none);
1098 _ = try astrl.expr(args[1], block, ResultInfo.type_only);
1099 _ = try astrl.expr(args[2], block, ResultInfo.type_only);
1100 _ = try astrl.expr(args[3], block, ResultInfo.type_only);
1101 _ = try astrl.expr(args[4], block, ResultInfo.type_only);
1102 return false;
1103 },
1104 }
1105}