authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-03-28 19:08:42+02:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-03-28 19:10:10+02:00
log402f87a213b9f3d5e19d5a1d412d8963957d8849
treefc82b22ed9387a937457594fd5c99a4589e71eb6
parent68f4eb0f67b6e5f7c20332e79c8e8decb07748ee
signature Commit is signed but in an unrecognized format.

stage2: rename WipZirCode => AstGen, astgen.zig => AstGen.zig


6 files changed, 4123 insertions(+), 4115 deletions(-)

CMakeLists.txt+1-1
......@@ -538,7 +538,7 @@ set(ZIG_STAGE2_SOURCES
538538 "${CMAKE_SOURCE_DIR}/src/ThreadPool.zig"
539539 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"
540540 "${CMAKE_SOURCE_DIR}/src/WaitGroup.zig"
541 "${CMAKE_SOURCE_DIR}/src/astgen.zig"
541 "${CMAKE_SOURCE_DIR}/src/AstGen.zig"
542542 "${CMAKE_SOURCE_DIR}/src/clang.zig"
543543 "${CMAKE_SOURCE_DIR}/src/clang_options.zig"
544544 "${CMAKE_SOURCE_DIR}/src/clang_options_data.zig"
src/AstGen.zig created+3979
......@@ -0,0 +1,3979 @@
1//! A Work-In-Progress `zir.Code`. This is a shared parent of all
2//! `GenZir` scopes. Once the `zir.Code` is produced, this struct
3//! is deinitialized.
4//! The `GenZir.finish` function converts this to a `zir.Code`.
5
6const AstGen = @This();
7
8const std = @import("std");
9const ast = std.zig.ast;
10const mem = std.mem;
11const Allocator = std.mem.Allocator;
12const assert = std.debug.assert;
13const ArrayListUnmanaged = std.ArrayListUnmanaged;
14
15const Value = @import("value.zig").Value;
16const Type = @import("type.zig").Type;
17const TypedValue = @import("TypedValue.zig");
18const zir = @import("zir.zig");
19const Module = @import("Module.zig");
20const trace = @import("tracy.zig").trace;
21const Scope = Module.Scope;
22const InnerError = Module.InnerError;
23const Decl = Module.Decl;
24const BuiltinFn = @import("BuiltinFn.zig");
25
26instructions: std.MultiArrayList(zir.Inst) = .{},
27string_bytes: ArrayListUnmanaged(u8) = .{},
28extra: ArrayListUnmanaged(u32) = .{},
29decl_map: std.StringArrayHashMapUnmanaged(void) = .{},
30decls: ArrayListUnmanaged(*Decl) = .{},
31/// The end of special indexes. `zir.Inst.Ref` subtracts against this number to convert
32/// to `zir.Inst.Index`. The default here is correct if there are 0 parameters.
33ref_start_index: u32 = zir.Inst.Ref.typed_value_map.len,
34mod: *Module,
35decl: *Decl,
36arena: *Allocator,
37
38/// Call `deinit` on the result.
39pub fn init(mod: *Module, decl: *Decl, arena: *Allocator) !AstGen {
40 var astgen: AstGen = .{
41 .mod = mod,
42 .decl = decl,
43 .arena = arena,
44 };
45 // Must be a block instruction at index 0 with the root body.
46 try astgen.instructions.append(mod.gpa, .{
47 .tag = .block,
48 .data = .{ .pl_node = .{
49 .src_node = 0,
50 .payload_index = undefined,
51 } },
52 });
53 return astgen;
54}
55
56pub fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 {
57 const fields = std.meta.fields(@TypeOf(extra));
58 try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len + fields.len);
59 return addExtraAssumeCapacity(astgen, extra);
60}
61
62pub fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
63 const fields = std.meta.fields(@TypeOf(extra));
64 const result = @intCast(u32, astgen.extra.items.len);
65 inline for (fields) |field| {
66 astgen.extra.appendAssumeCapacity(switch (field.field_type) {
67 u32 => @field(extra, field.name),
68 zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
69 else => @compileError("bad field type"),
70 });
71 }
72 return result;
73}
74
75pub fn appendRefs(astgen: *AstGen, refs: []const zir.Inst.Ref) !void {
76 const coerced = @bitCast([]const u32, refs);
77 return astgen.extra.appendSlice(astgen.mod.gpa, coerced);
78}
79
80pub fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const zir.Inst.Ref) void {
81 const coerced = @bitCast([]const u32, refs);
82 astgen.extra.appendSliceAssumeCapacity(coerced);
83}
84
85pub fn refIsNoReturn(astgen: AstGen, inst_ref: zir.Inst.Ref) bool {
86 if (inst_ref == .unreachable_value) return true;
87 if (astgen.refToIndex(inst_ref)) |inst_index| {
88 return astgen.instructions.items(.tag)[inst_index].isNoReturn();
89 }
90 return false;
91}
92
93pub fn indexToRef(astgen: AstGen, inst: zir.Inst.Index) zir.Inst.Ref {
94 return @intToEnum(zir.Inst.Ref, astgen.ref_start_index + inst);
95}
96
97pub fn refToIndex(astgen: AstGen, inst: zir.Inst.Ref) ?zir.Inst.Index {
98 const ref_int = @enumToInt(inst);
99 if (ref_int >= astgen.ref_start_index) {
100 return ref_int - astgen.ref_start_index;
101 } else {
102 return null;
103 }
104}
105
106pub fn deinit(astgen: *AstGen) void {
107 const gpa = astgen.mod.gpa;
108 astgen.instructions.deinit(gpa);
109 astgen.extra.deinit(gpa);
110 astgen.string_bytes.deinit(gpa);
111 astgen.decl_map.deinit(gpa);
112 astgen.decls.deinit(gpa);
113}
114
115pub const ResultLoc = union(enum) {
116 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
117 /// expression should be generated. The result instruction from the expression must
118 /// be ignored.
119 discard,
120 /// The expression has an inferred type, and it will be evaluated as an rvalue.
121 none,
122 /// The expression must generate a pointer rather than a value. For example, the left hand side
123 /// of an assignment uses this kind of result location.
124 ref,
125 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
126 ty: zir.Inst.Ref,
127 /// The expression must store its result into this typed pointer. The result instruction
128 /// from the expression must be ignored.
129 ptr: zir.Inst.Ref,
130 /// The expression must store its result into this allocation, which has an inferred type.
131 /// The result instruction from the expression must be ignored.
132 /// Always an instruction with tag `alloc_inferred`.
133 inferred_ptr: zir.Inst.Ref,
134 /// The expression must store its result into this pointer, which is a typed pointer that
135 /// has been bitcasted to whatever the expression's type is.
136 /// The result instruction from the expression must be ignored.
137 bitcasted_ptr: zir.Inst.Ref,
138 /// There is a pointer for the expression to store its result into, however, its type
139 /// is inferred based on peer type resolution for a `zir.Inst.Block`.
140 /// The result instruction from the expression must be ignored.
141 block_ptr: *Module.Scope.GenZir,
142
143 pub const Strategy = struct {
144 elide_store_to_block_ptr_instructions: bool,
145 tag: Tag,
146
147 pub const Tag = enum {
148 /// Both branches will use break_void; result location is used to communicate the
149 /// result instruction.
150 break_void,
151 /// Use break statements to pass the block result value, and call rvalue() at
152 /// the end depending on rl. Also elide the store_to_block_ptr instructions
153 /// depending on rl.
154 break_operand,
155 };
156 };
157};
158
159pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!zir.Inst.Ref {
160 return expr(mod, scope, .{ .ty = .type_type }, type_node);
161}
162
163fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
164 const tree = scope.tree();
165 const node_tags = tree.nodes.items(.tag);
166 const main_tokens = tree.nodes.items(.main_token);
167 switch (node_tags[node]) {
168 .root => unreachable,
169 .@"usingnamespace" => unreachable,
170 .test_decl => unreachable,
171 .global_var_decl => unreachable,
172 .local_var_decl => unreachable,
173 .simple_var_decl => unreachable,
174 .aligned_var_decl => unreachable,
175 .switch_case => unreachable,
176 .switch_case_one => unreachable,
177 .container_field_init => unreachable,
178 .container_field_align => unreachable,
179 .container_field => unreachable,
180 .asm_output => unreachable,
181 .asm_input => unreachable,
182
183 .assign,
184 .assign_bit_and,
185 .assign_bit_or,
186 .assign_bit_shift_left,
187 .assign_bit_shift_right,
188 .assign_bit_xor,
189 .assign_div,
190 .assign_sub,
191 .assign_sub_wrap,
192 .assign_mod,
193 .assign_add,
194 .assign_add_wrap,
195 .assign_mul,
196 .assign_mul_wrap,
197 .add,
198 .add_wrap,
199 .sub,
200 .sub_wrap,
201 .mul,
202 .mul_wrap,
203 .div,
204 .mod,
205 .bit_and,
206 .bit_or,
207 .bit_shift_left,
208 .bit_shift_right,
209 .bit_xor,
210 .bang_equal,
211 .equal_equal,
212 .greater_than,
213 .greater_or_equal,
214 .less_than,
215 .less_or_equal,
216 .array_cat,
217 .array_mult,
218 .bool_and,
219 .bool_or,
220 .@"asm",
221 .asm_simple,
222 .string_literal,
223 .integer_literal,
224 .call,
225 .call_comma,
226 .async_call,
227 .async_call_comma,
228 .call_one,
229 .call_one_comma,
230 .async_call_one,
231 .async_call_one_comma,
232 .unreachable_literal,
233 .@"return",
234 .@"if",
235 .if_simple,
236 .@"while",
237 .while_simple,
238 .while_cont,
239 .bool_not,
240 .address_of,
241 .float_literal,
242 .undefined_literal,
243 .true_literal,
244 .false_literal,
245 .null_literal,
246 .optional_type,
247 .block,
248 .block_semicolon,
249 .block_two,
250 .block_two_semicolon,
251 .@"break",
252 .ptr_type_aligned,
253 .ptr_type_sentinel,
254 .ptr_type,
255 .ptr_type_bit_range,
256 .array_type,
257 .array_type_sentinel,
258 .enum_literal,
259 .multiline_string_literal,
260 .char_literal,
261 .@"defer",
262 .@"errdefer",
263 .@"catch",
264 .error_union,
265 .merge_error_sets,
266 .switch_range,
267 .@"await",
268 .bit_not,
269 .negation,
270 .negation_wrap,
271 .@"resume",
272 .@"try",
273 .slice,
274 .slice_open,
275 .slice_sentinel,
276 .array_init_one,
277 .array_init_one_comma,
278 .array_init_dot_two,
279 .array_init_dot_two_comma,
280 .array_init_dot,
281 .array_init_dot_comma,
282 .array_init,
283 .array_init_comma,
284 .struct_init_one,
285 .struct_init_one_comma,
286 .struct_init_dot_two,
287 .struct_init_dot_two_comma,
288 .struct_init_dot,
289 .struct_init_dot_comma,
290 .struct_init,
291 .struct_init_comma,
292 .@"switch",
293 .switch_comma,
294 .@"for",
295 .for_simple,
296 .@"suspend",
297 .@"continue",
298 .@"anytype",
299 .fn_proto_simple,
300 .fn_proto_multi,
301 .fn_proto_one,
302 .fn_proto,
303 .fn_decl,
304 .anyframe_type,
305 .anyframe_literal,
306 .error_set_decl,
307 .container_decl,
308 .container_decl_trailing,
309 .container_decl_two,
310 .container_decl_two_trailing,
311 .container_decl_arg,
312 .container_decl_arg_trailing,
313 .tagged_union,
314 .tagged_union_trailing,
315 .tagged_union_two,
316 .tagged_union_two_trailing,
317 .tagged_union_enum_tag,
318 .tagged_union_enum_tag_trailing,
319 .@"comptime",
320 .@"nosuspend",
321 .error_value,
322 => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
323
324 .builtin_call,
325 .builtin_call_comma,
326 .builtin_call_two,
327 .builtin_call_two_comma,
328 => {
329 const builtin_token = main_tokens[node];
330 const builtin_name = tree.tokenSlice(builtin_token);
331 // If the builtin is an invalid name, we don't cause an error here; instead
332 // let it pass, and the error will be "invalid builtin function" later.
333 if (BuiltinFn.list.get(builtin_name)) |info| {
334 if (!info.allows_lvalue) {
335 return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
336 }
337 }
338 },
339
340 // These can be assigned to.
341 .unwrap_optional,
342 .deref,
343 .field_access,
344 .array_access,
345 .identifier,
346 .grouped_expression,
347 .@"orelse",
348 => {},
349 }
350 return expr(mod, scope, .ref, node);
351}
352
353/// Turn Zig AST into untyped ZIR istructions.
354/// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the
355/// result instruction can be used to inspect whether it is isNoReturn() but that is it,
356/// it must otherwise not be used.
357pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref {
358 const tree = scope.tree();
359 const main_tokens = tree.nodes.items(.main_token);
360 const token_tags = tree.tokens.items(.tag);
361 const node_datas = tree.nodes.items(.data);
362 const node_tags = tree.nodes.items(.tag);
363
364 const gz = scope.getGenZir();
365
366 switch (node_tags[node]) {
367 .root => unreachable, // Top-level declaration.
368 .@"usingnamespace" => unreachable, // Top-level declaration.
369 .test_decl => unreachable, // Top-level declaration.
370 .container_field_init => unreachable, // Top-level declaration.
371 .container_field_align => unreachable, // Top-level declaration.
372 .container_field => unreachable, // Top-level declaration.
373 .fn_decl => unreachable, // Top-level declaration.
374
375 .global_var_decl => unreachable, // Handled in `blockExpr`.
376 .local_var_decl => unreachable, // Handled in `blockExpr`.
377 .simple_var_decl => unreachable, // Handled in `blockExpr`.
378 .aligned_var_decl => unreachable, // Handled in `blockExpr`.
379
380 .switch_case => unreachable, // Handled in `switchExpr`.
381 .switch_case_one => unreachable, // Handled in `switchExpr`.
382 .switch_range => unreachable, // Handled in `switchExpr`.
383
384 .asm_output => unreachable, // Handled in `asmExpr`.
385 .asm_input => unreachable, // Handled in `asmExpr`.
386
387 .assign => {
388 try assign(mod, scope, node);
389 return rvalue(mod, scope, rl, .void_value, node);
390 },
391 .assign_bit_and => {
392 try assignOp(mod, scope, node, .bit_and);
393 return rvalue(mod, scope, rl, .void_value, node);
394 },
395 .assign_bit_or => {
396 try assignOp(mod, scope, node, .bit_or);
397 return rvalue(mod, scope, rl, .void_value, node);
398 },
399 .assign_bit_shift_left => {
400 try assignOp(mod, scope, node, .shl);
401 return rvalue(mod, scope, rl, .void_value, node);
402 },
403 .assign_bit_shift_right => {
404 try assignOp(mod, scope, node, .shr);
405 return rvalue(mod, scope, rl, .void_value, node);
406 },
407 .assign_bit_xor => {
408 try assignOp(mod, scope, node, .xor);
409 return rvalue(mod, scope, rl, .void_value, node);
410 },
411 .assign_div => {
412 try assignOp(mod, scope, node, .div);
413 return rvalue(mod, scope, rl, .void_value, node);
414 },
415 .assign_sub => {
416 try assignOp(mod, scope, node, .sub);
417 return rvalue(mod, scope, rl, .void_value, node);
418 },
419 .assign_sub_wrap => {
420 try assignOp(mod, scope, node, .subwrap);
421 return rvalue(mod, scope, rl, .void_value, node);
422 },
423 .assign_mod => {
424 try assignOp(mod, scope, node, .mod_rem);
425 return rvalue(mod, scope, rl, .void_value, node);
426 },
427 .assign_add => {
428 try assignOp(mod, scope, node, .add);
429 return rvalue(mod, scope, rl, .void_value, node);
430 },
431 .assign_add_wrap => {
432 try assignOp(mod, scope, node, .addwrap);
433 return rvalue(mod, scope, rl, .void_value, node);
434 },
435 .assign_mul => {
436 try assignOp(mod, scope, node, .mul);
437 return rvalue(mod, scope, rl, .void_value, node);
438 },
439 .assign_mul_wrap => {
440 try assignOp(mod, scope, node, .mulwrap);
441 return rvalue(mod, scope, rl, .void_value, node);
442 },
443
444 .add => return simpleBinOp(mod, scope, rl, node, .add),
445 .add_wrap => return simpleBinOp(mod, scope, rl, node, .addwrap),
446 .sub => return simpleBinOp(mod, scope, rl, node, .sub),
447 .sub_wrap => return simpleBinOp(mod, scope, rl, node, .subwrap),
448 .mul => return simpleBinOp(mod, scope, rl, node, .mul),
449 .mul_wrap => return simpleBinOp(mod, scope, rl, node, .mulwrap),
450 .div => return simpleBinOp(mod, scope, rl, node, .div),
451 .mod => return simpleBinOp(mod, scope, rl, node, .mod_rem),
452 .bit_and => return simpleBinOp(mod, scope, rl, node, .bit_and),
453 .bit_or => return simpleBinOp(mod, scope, rl, node, .bit_or),
454 .bit_shift_left => return simpleBinOp(mod, scope, rl, node, .shl),
455 .bit_shift_right => return simpleBinOp(mod, scope, rl, node, .shr),
456 .bit_xor => return simpleBinOp(mod, scope, rl, node, .xor),
457
458 .bang_equal => return simpleBinOp(mod, scope, rl, node, .cmp_neq),
459 .equal_equal => return simpleBinOp(mod, scope, rl, node, .cmp_eq),
460 .greater_than => return simpleBinOp(mod, scope, rl, node, .cmp_gt),
461 .greater_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_gte),
462 .less_than => return simpleBinOp(mod, scope, rl, node, .cmp_lt),
463 .less_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_lte),
464
465 .array_cat => return simpleBinOp(mod, scope, rl, node, .array_cat),
466 .array_mult => return simpleBinOp(mod, scope, rl, node, .array_mul),
467
468 .error_union => return simpleBinOp(mod, scope, rl, node, .error_union_type),
469 .merge_error_sets => return simpleBinOp(mod, scope, rl, node, .merge_error_sets),
470
471 .bool_and => return boolBinOp(mod, scope, rl, node, .bool_br_and),
472 .bool_or => return boolBinOp(mod, scope, rl, node, .bool_br_or),
473
474 .bool_not => return boolNot(mod, scope, rl, node),
475 .bit_not => return bitNot(mod, scope, rl, node),
476
477 .negation => return negation(mod, scope, rl, node, .negate),
478 .negation_wrap => return negation(mod, scope, rl, node, .negate_wrap),
479
480 .identifier => return identifier(mod, scope, rl, node),
481
482 .asm_simple => return asmExpr(mod, scope, rl, node, tree.asmSimple(node)),
483 .@"asm" => return asmExpr(mod, scope, rl, node, tree.asmFull(node)),
484
485 .string_literal => return stringLiteral(mod, scope, rl, node),
486 .multiline_string_literal => return multilineStringLiteral(mod, scope, rl, node),
487
488 .integer_literal => return integerLiteral(mod, scope, rl, node),
489
490 .builtin_call_two, .builtin_call_two_comma => {
491 if (node_datas[node].lhs == 0) {
492 const params = [_]ast.Node.Index{};
493 return builtinCall(mod, scope, rl, node, &params);
494 } else if (node_datas[node].rhs == 0) {
495 const params = [_]ast.Node.Index{node_datas[node].lhs};
496 return builtinCall(mod, scope, rl, node, &params);
497 } else {
498 const params = [_]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
499 return builtinCall(mod, scope, rl, node, &params);
500 }
501 },
502 .builtin_call, .builtin_call_comma => {
503 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
504 return builtinCall(mod, scope, rl, node, params);
505 },
506
507 .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
508 var params: [1]ast.Node.Index = undefined;
509 return callExpr(mod, scope, rl, node, tree.callOne(&params, node));
510 },
511 .call, .call_comma, .async_call, .async_call_comma => {
512 return callExpr(mod, scope, rl, node, tree.callFull(node));
513 },
514
515 .unreachable_literal => {
516 _ = try gz.addAsIndex(.{
517 .tag = .@"unreachable",
518 .data = .{ .@"unreachable" = .{
519 .safety = true,
520 .src_node = gz.astgen.decl.nodeIndexToRelative(node),
521 } },
522 });
523 return zir.Inst.Ref.unreachable_value;
524 },
525 .@"return" => return ret(mod, scope, node),
526 .field_access => return fieldAccess(mod, scope, rl, node),
527 .float_literal => return floatLiteral(mod, scope, rl, node),
528
529 .if_simple => return ifExpr(mod, scope, rl, node, tree.ifSimple(node)),
530 .@"if" => return ifExpr(mod, scope, rl, node, tree.ifFull(node)),
531
532 .while_simple => return whileExpr(mod, scope, rl, node, tree.whileSimple(node)),
533 .while_cont => return whileExpr(mod, scope, rl, node, tree.whileCont(node)),
534 .@"while" => return whileExpr(mod, scope, rl, node, tree.whileFull(node)),
535
536 .for_simple => return forExpr(mod, scope, rl, node, tree.forSimple(node)),
537 .@"for" => return forExpr(mod, scope, rl, node, tree.forFull(node)),
538
539 .slice_open => {
540 const lhs = try expr(mod, scope, .ref, node_datas[node].lhs);
541 const start = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].rhs);
542 const result = try gz.addPlNode(.slice_start, node, zir.Inst.SliceStart{
543 .lhs = lhs,
544 .start = start,
545 });
546 return rvalue(mod, scope, rl, result, node);
547 },
548 .slice => {
549 const lhs = try expr(mod, scope, .ref, node_datas[node].lhs);
550 const extra = tree.extraData(node_datas[node].rhs, ast.Node.Slice);
551 const start = try expr(mod, scope, .{ .ty = .usize_type }, extra.start);
552 const end = try expr(mod, scope, .{ .ty = .usize_type }, extra.end);
553 const result = try gz.addPlNode(.slice_end, node, zir.Inst.SliceEnd{
554 .lhs = lhs,
555 .start = start,
556 .end = end,
557 });
558 return rvalue(mod, scope, rl, result, node);
559 },
560 .slice_sentinel => {
561 const lhs = try expr(mod, scope, .ref, node_datas[node].lhs);
562 const extra = tree.extraData(node_datas[node].rhs, ast.Node.SliceSentinel);
563 const start = try expr(mod, scope, .{ .ty = .usize_type }, extra.start);
564 const end = try expr(mod, scope, .{ .ty = .usize_type }, extra.end);
565 const sentinel = try expr(mod, scope, .{ .ty = .usize_type }, extra.sentinel);
566 const result = try gz.addPlNode(.slice_sentinel, node, zir.Inst.SliceSentinel{
567 .lhs = lhs,
568 .start = start,
569 .end = end,
570 .sentinel = sentinel,
571 });
572 return rvalue(mod, scope, rl, result, node);
573 },
574
575 .deref => {
576 const lhs = try expr(mod, scope, .none, node_datas[node].lhs);
577 const result = try gz.addUnNode(.load, lhs, node);
578 return rvalue(mod, scope, rl, result, node);
579 },
580 .address_of => {
581 const result = try expr(mod, scope, .ref, node_datas[node].lhs);
582 return rvalue(mod, scope, rl, result, node);
583 },
584 .undefined_literal => return rvalue(mod, scope, rl, .undef, node),
585 .true_literal => return rvalue(mod, scope, rl, .bool_true, node),
586 .false_literal => return rvalue(mod, scope, rl, .bool_false, node),
587 .null_literal => return rvalue(mod, scope, rl, .null_value, node),
588 .optional_type => {
589 const operand = try typeExpr(mod, scope, node_datas[node].lhs);
590 const result = try gz.addUnNode(.optional_type, operand, node);
591 return rvalue(mod, scope, rl, result, node);
592 },
593 .unwrap_optional => switch (rl) {
594 .ref => return gz.addUnNode(
595 .optional_payload_safe_ptr,
596 try expr(mod, scope, .ref, node_datas[node].lhs),
597 node,
598 ),
599 else => return rvalue(mod, scope, rl, try gz.addUnNode(
600 .optional_payload_safe,
601 try expr(mod, scope, .none, node_datas[node].lhs),
602 node,
603 ), node),
604 },
605 .block_two, .block_two_semicolon => {
606 const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
607 if (node_datas[node].lhs == 0) {
608 return blockExpr(mod, scope, rl, node, statements[0..0]);
609 } else if (node_datas[node].rhs == 0) {
610 return blockExpr(mod, scope, rl, node, statements[0..1]);
611 } else {
612 return blockExpr(mod, scope, rl, node, statements[0..2]);
613 }
614 },
615 .block, .block_semicolon => {
616 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
617 return blockExpr(mod, scope, rl, node, statements);
618 },
619 .enum_literal => return simpleStrTok(mod, scope, rl, main_tokens[node], node, .enum_literal),
620 .error_value => return simpleStrTok(mod, scope, rl, node_datas[node].rhs, node, .error_value),
621 .anyframe_literal => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
622 .anyframe_type => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
623 .@"catch" => {
624 const catch_token = main_tokens[node];
625 const payload_token: ?ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)
626 catch_token + 2
627 else
628 null;
629 switch (rl) {
630 .ref => return orelseCatchExpr(
631 mod,
632 scope,
633 rl,
634 node,
635 node_datas[node].lhs,
636 .is_err_ptr,
637 .err_union_payload_unsafe_ptr,
638 .err_union_code_ptr,
639 node_datas[node].rhs,
640 payload_token,
641 ),
642 else => return orelseCatchExpr(
643 mod,
644 scope,
645 rl,
646 node,
647 node_datas[node].lhs,
648 .is_err,
649 .err_union_payload_unsafe,
650 .err_union_code,
651 node_datas[node].rhs,
652 payload_token,
653 ),
654 }
655 },
656 .@"orelse" => switch (rl) {
657 .ref => return orelseCatchExpr(
658 mod,
659 scope,
660 rl,
661 node,
662 node_datas[node].lhs,
663 .is_null_ptr,
664 .optional_payload_unsafe_ptr,
665 undefined,
666 node_datas[node].rhs,
667 null,
668 ),
669 else => return orelseCatchExpr(
670 mod,
671 scope,
672 rl,
673 node,
674 node_datas[node].lhs,
675 .is_null,
676 .optional_payload_unsafe,
677 undefined,
678 node_datas[node].rhs,
679 null,
680 ),
681 },
682
683 .ptr_type_aligned => return ptrType(mod, scope, rl, node, tree.ptrTypeAligned(node)),
684 .ptr_type_sentinel => return ptrType(mod, scope, rl, node, tree.ptrTypeSentinel(node)),
685 .ptr_type => return ptrType(mod, scope, rl, node, tree.ptrType(node)),
686 .ptr_type_bit_range => return ptrType(mod, scope, rl, node, tree.ptrTypeBitRange(node)),
687
688 .container_decl,
689 .container_decl_trailing,
690 => return containerDecl(mod, scope, rl, tree.containerDecl(node)),
691 .container_decl_two, .container_decl_two_trailing => {
692 var buffer: [2]ast.Node.Index = undefined;
693 return containerDecl(mod, scope, rl, tree.containerDeclTwo(&buffer, node));
694 },
695 .container_decl_arg,
696 .container_decl_arg_trailing,
697 => return containerDecl(mod, scope, rl, tree.containerDeclArg(node)),
698
699 .tagged_union,
700 .tagged_union_trailing,
701 => return containerDecl(mod, scope, rl, tree.taggedUnion(node)),
702 .tagged_union_two, .tagged_union_two_trailing => {
703 var buffer: [2]ast.Node.Index = undefined;
704 return containerDecl(mod, scope, rl, tree.taggedUnionTwo(&buffer, node));
705 },
706 .tagged_union_enum_tag,
707 .tagged_union_enum_tag_trailing,
708 => return containerDecl(mod, scope, rl, tree.taggedUnionEnumTag(node)),
709
710 .@"break" => return breakExpr(mod, scope, node),
711 .@"continue" => return continueExpr(mod, scope, node),
712 .grouped_expression => return expr(mod, scope, rl, node_datas[node].lhs),
713 .array_type => return arrayType(mod, scope, rl, node),
714 .array_type_sentinel => return arrayTypeSentinel(mod, scope, rl, node),
715 .char_literal => return charLiteral(mod, scope, rl, node),
716 .error_set_decl => return errorSetDecl(mod, scope, rl, node),
717 .array_access => return arrayAccess(mod, scope, rl, node),
718 .@"comptime" => return comptimeExpr(mod, scope, rl, node_datas[node].lhs),
719 .@"switch", .switch_comma => return switchExpr(mod, scope, rl, node),
720
721 .@"nosuspend" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
722 .@"suspend" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
723 .@"await" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
724 .@"resume" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
725
726 .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}),
727 .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}),
728 .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
729
730 .array_init_one,
731 .array_init_one_comma,
732 .array_init_dot_two,
733 .array_init_dot_two_comma,
734 .array_init_dot,
735 .array_init_dot_comma,
736 .array_init,
737 .array_init_comma,
738 => return mod.failNode(scope, node, "TODO implement astgen.expr for array literals", .{}),
739
740 .struct_init_one,
741 .struct_init_one_comma,
742 .struct_init_dot_two,
743 .struct_init_dot_two_comma,
744 .struct_init_dot,
745 .struct_init_dot_comma,
746 .struct_init,
747 .struct_init_comma,
748 => return mod.failNode(scope, node, "TODO implement astgen.expr for struct literals", .{}),
749
750 .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}),
751 .fn_proto_simple,
752 .fn_proto_multi,
753 .fn_proto_one,
754 .fn_proto,
755 => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}),
756 }
757}
758
759pub fn comptimeExpr(
760 mod: *Module,
761 parent_scope: *Scope,
762 rl: ResultLoc,
763 node: ast.Node.Index,
764) InnerError!zir.Inst.Ref {
765 const gz = parent_scope.getGenZir();
766
767 const prev_force_comptime = gz.force_comptime;
768 gz.force_comptime = true;
769 const result = try expr(mod, parent_scope, rl, node);
770 gz.force_comptime = prev_force_comptime;
771 return result;
772}
773
774fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
775 const parent_gz = parent_scope.getGenZir();
776 const tree = parent_gz.tree();
777 const node_datas = tree.nodes.items(.data);
778 const break_label = node_datas[node].lhs;
779 const rhs = node_datas[node].rhs;
780
781 // Look for the label in the scope.
782 var scope = parent_scope;
783 while (true) {
784 switch (scope.tag) {
785 .gen_zir => {
786 const block_gz = scope.cast(Scope.GenZir).?;
787
788 const block_inst = blk: {
789 if (break_label != 0) {
790 if (block_gz.label) |*label| {
791 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
792 label.used = true;
793 break :blk label.block_inst;
794 }
795 }
796 } else if (block_gz.break_block != 0) {
797 break :blk block_gz.break_block;
798 }
799 scope = block_gz.parent;
800 continue;
801 };
802
803 if (rhs == 0) {
804 _ = try parent_gz.addBreak(.@"break", block_inst, .void_value);
805 return zir.Inst.Ref.unreachable_value;
806 }
807 block_gz.break_count += 1;
808 const prev_rvalue_rl_count = block_gz.rvalue_rl_count;
809 const operand = try expr(mod, parent_scope, block_gz.break_result_loc, rhs);
810 const have_store_to_block = block_gz.rvalue_rl_count != prev_rvalue_rl_count;
811
812 const br = try parent_gz.addBreak(.@"break", block_inst, operand);
813
814 if (block_gz.break_result_loc == .block_ptr) {
815 try block_gz.labeled_breaks.append(mod.gpa, br);
816
817 if (have_store_to_block) {
818 const zir_tags = parent_gz.astgen.instructions.items(.tag);
819 const zir_datas = parent_gz.astgen.instructions.items(.data);
820 const store_inst = @intCast(u32, zir_tags.len - 2);
821 assert(zir_tags[store_inst] == .store_to_block_ptr);
822 assert(zir_datas[store_inst].bin.lhs == block_gz.rl_ptr);
823 try block_gz.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst);
824 }
825 }
826 return zir.Inst.Ref.unreachable_value;
827 },
828 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
829 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
830 else => if (break_label != 0) {
831 const label_name = try mod.identifierTokenString(parent_scope, break_label);
832 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
833 } else {
834 return mod.failNode(parent_scope, node, "break expression outside loop", .{});
835 },
836 }
837 }
838}
839
840fn continueExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
841 const parent_gz = parent_scope.getGenZir();
842 const tree = parent_gz.tree();
843 const node_datas = tree.nodes.items(.data);
844 const break_label = node_datas[node].lhs;
845
846 // Look for the label in the scope.
847 var scope = parent_scope;
848 while (true) {
849 switch (scope.tag) {
850 .gen_zir => {
851 const gen_zir = scope.cast(Scope.GenZir).?;
852 const continue_block = gen_zir.continue_block;
853 if (continue_block == 0) {
854 scope = gen_zir.parent;
855 continue;
856 }
857 if (break_label != 0) blk: {
858 if (gen_zir.label) |*label| {
859 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
860 label.used = true;
861 break :blk;
862 }
863 }
864 // found continue but either it has a different label, or no label
865 scope = gen_zir.parent;
866 continue;
867 }
868
869 // TODO emit a break_inline if the loop being continued is inline
870 _ = try parent_gz.addBreak(.@"break", continue_block, .void_value);
871 return zir.Inst.Ref.unreachable_value;
872 },
873 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
874 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
875 else => if (break_label != 0) {
876 const label_name = try mod.identifierTokenString(parent_scope, break_label);
877 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
878 } else {
879 return mod.failNode(parent_scope, node, "continue expression outside loop", .{});
880 },
881 }
882 }
883}
884
885pub fn blockExpr(
886 mod: *Module,
887 scope: *Scope,
888 rl: ResultLoc,
889 block_node: ast.Node.Index,
890 statements: []const ast.Node.Index,
891) InnerError!zir.Inst.Ref {
892 const tracy = trace(@src());
893 defer tracy.end();
894
895 const tree = scope.tree();
896 const main_tokens = tree.nodes.items(.main_token);
897 const token_tags = tree.tokens.items(.tag);
898
899 const lbrace = main_tokens[block_node];
900 if (token_tags[lbrace - 1] == .colon and
901 token_tags[lbrace - 2] == .identifier)
902 {
903 return labeledBlockExpr(mod, scope, rl, block_node, statements, .block);
904 }
905
906 try blockExprStmts(mod, scope, block_node, statements);
907 return rvalue(mod, scope, rl, .void_value, block_node);
908}
909
910fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void {
911 // Look for the label in the scope.
912 var scope = parent_scope;
913 while (true) {
914 switch (scope.tag) {
915 .gen_zir => {
916 const gen_zir = scope.cast(Scope.GenZir).?;
917 if (gen_zir.label) |prev_label| {
918 if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) {
919 const tree = parent_scope.tree();
920 const main_tokens = tree.nodes.items(.main_token);
921
922 const label_name = try mod.identifierTokenString(parent_scope, label);
923 const msg = msg: {
924 const msg = try mod.errMsg(
925 parent_scope,
926 gen_zir.tokSrcLoc(label),
927 "redefinition of label '{s}'",
928 .{label_name},
929 );
930 errdefer msg.destroy(mod.gpa);
931 try mod.errNote(
932 parent_scope,
933 gen_zir.tokSrcLoc(prev_label.token),
934 msg,
935 "previous definition is here",
936 .{},
937 );
938 break :msg msg;
939 };
940 return mod.failWithOwnedErrorMsg(parent_scope, msg);
941 }
942 }
943 scope = gen_zir.parent;
944 },
945 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
946 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
947 else => return,
948 }
949 }
950}
951
952fn labeledBlockExpr(
953 mod: *Module,
954 parent_scope: *Scope,
955 rl: ResultLoc,
956 block_node: ast.Node.Index,
957 statements: []const ast.Node.Index,
958 zir_tag: zir.Inst.Tag,
959) InnerError!zir.Inst.Ref {
960 const tracy = trace(@src());
961 defer tracy.end();
962
963 assert(zir_tag == .block);
964
965 const tree = parent_scope.tree();
966 const main_tokens = tree.nodes.items(.main_token);
967 const token_tags = tree.tokens.items(.tag);
968
969 const lbrace = main_tokens[block_node];
970 const label_token = lbrace - 2;
971 assert(token_tags[label_token] == .identifier);
972
973 try checkLabelRedefinition(mod, parent_scope, label_token);
974
975 // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
976 // so that break statements can reference it.
977 const gz = parent_scope.getGenZir();
978 const block_inst = try gz.addBlock(zir_tag, block_node);
979 try gz.instructions.append(mod.gpa, block_inst);
980
981 var block_scope: Scope.GenZir = .{
982 .parent = parent_scope,
983 .astgen = gz.astgen,
984 .force_comptime = gz.force_comptime,
985 .instructions = .{},
986 // TODO @as here is working around a stage1 miscompilation bug :(
987 .label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{
988 .token = label_token,
989 .block_inst = block_inst,
990 }),
991 };
992 setBlockResultLoc(&block_scope, rl);
993 defer block_scope.instructions.deinit(mod.gpa);
994 defer block_scope.labeled_breaks.deinit(mod.gpa);
995 defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa);
996
997 try blockExprStmts(mod, &block_scope.base, block_node, statements);
998
999 if (!block_scope.label.?.used) {
1000 return mod.failTok(parent_scope, label_token, "unused block label", .{});
1001 }
1002
1003 const zir_tags = gz.astgen.instructions.items(.tag);
1004 const zir_datas = gz.astgen.instructions.items(.data);
1005
1006 const strat = rlStrategy(rl, &block_scope);
1007 switch (strat.tag) {
1008 .break_void => {
1009 // The code took advantage of the result location as a pointer.
1010 // Turn the break instruction operands into void.
1011 for (block_scope.labeled_breaks.items) |br| {
1012 zir_datas[br].@"break".operand = .void_value;
1013 }
1014 try block_scope.setBlockBody(block_inst);
1015
1016 return gz.astgen.indexToRef(block_inst);
1017 },
1018 .break_operand => {
1019 // All break operands are values that did not use the result location pointer.
1020 if (strat.elide_store_to_block_ptr_instructions) {
1021 for (block_scope.labeled_store_to_block_ptr_list.items) |inst| {
1022 zir_tags[inst] = .elided;
1023 zir_datas[inst] = undefined;
1024 }
1025 // TODO technically not needed since we changed the tag to elided but
1026 // would be better still to elide the ones that are in this list.
1027 }
1028 try block_scope.setBlockBody(block_inst);
1029 const block_ref = gz.astgen.indexToRef(block_inst);
1030 switch (rl) {
1031 .ref => return block_ref,
1032 else => return rvalue(mod, parent_scope, rl, block_ref, block_node),
1033 }
1034 },
1035 }
1036}
1037
1038fn blockExprStmts(
1039 mod: *Module,
1040 parent_scope: *Scope,
1041 node: ast.Node.Index,
1042 statements: []const ast.Node.Index,
1043) !void {
1044 const tree = parent_scope.tree();
1045 const main_tokens = tree.nodes.items(.main_token);
1046 const node_tags = tree.nodes.items(.tag);
1047
1048 var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
1049 defer block_arena.deinit();
1050
1051 const gz = parent_scope.getGenZir();
1052
1053 var scope = parent_scope;
1054 for (statements) |statement| {
1055 if (!gz.force_comptime) {
1056 _ = try gz.addNode(.dbg_stmt_node, statement);
1057 }
1058 switch (node_tags[statement]) {
1059 .global_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.globalVarDecl(statement)),
1060 .local_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.localVarDecl(statement)),
1061 .simple_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.simpleVarDecl(statement)),
1062 .aligned_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.alignedVarDecl(statement)),
1063
1064 .assign => try assign(mod, scope, statement),
1065 .assign_bit_and => try assignOp(mod, scope, statement, .bit_and),
1066 .assign_bit_or => try assignOp(mod, scope, statement, .bit_or),
1067 .assign_bit_shift_left => try assignOp(mod, scope, statement, .shl),
1068 .assign_bit_shift_right => try assignOp(mod, scope, statement, .shr),
1069 .assign_bit_xor => try assignOp(mod, scope, statement, .xor),
1070 .assign_div => try assignOp(mod, scope, statement, .div),
1071 .assign_sub => try assignOp(mod, scope, statement, .sub),
1072 .assign_sub_wrap => try assignOp(mod, scope, statement, .subwrap),
1073 .assign_mod => try assignOp(mod, scope, statement, .mod_rem),
1074 .assign_add => try assignOp(mod, scope, statement, .add),
1075 .assign_add_wrap => try assignOp(mod, scope, statement, .addwrap),
1076 .assign_mul => try assignOp(mod, scope, statement, .mul),
1077 .assign_mul_wrap => try assignOp(mod, scope, statement, .mulwrap),
1078
1079 else => {
1080 // We need to emit an error if the result is not `noreturn` or `void`, but
1081 // we want to avoid adding the ZIR instruction if possible for performance.
1082 const maybe_unused_result = try expr(mod, scope, .none, statement);
1083 const elide_check = if (gz.astgen.refToIndex(maybe_unused_result)) |inst| b: {
1084 // Note that this array becomes invalid after appending more items to it
1085 // in the above while loop.
1086 const zir_tags = gz.astgen.instructions.items(.tag);
1087 switch (zir_tags[inst]) {
1088 .@"const" => {
1089 const tv = gz.astgen.instructions.items(.data)[inst].@"const";
1090 break :b switch (tv.ty.zigTypeTag()) {
1091 .NoReturn, .Void => true,
1092 else => false,
1093 };
1094 },
1095 // For some instructions, swap in a slightly different ZIR tag
1096 // so we can avoid a separate ensure_result_used instruction.
1097 .call_none_chkused => unreachable,
1098 .call_none => {
1099 zir_tags[inst] = .call_none_chkused;
1100 break :b true;
1101 },
1102 .call_chkused => unreachable,
1103 .call => {
1104 zir_tags[inst] = .call_chkused;
1105 break :b true;
1106 },
1107
1108 // ZIR instructions that might be a type other than `noreturn` or `void`.
1109 .add,
1110 .addwrap,
1111 .alloc,
1112 .alloc_mut,
1113 .alloc_inferred,
1114 .alloc_inferred_mut,
1115 .array_cat,
1116 .array_mul,
1117 .array_type,
1118 .array_type_sentinel,
1119 .indexable_ptr_len,
1120 .as,
1121 .as_node,
1122 .@"asm",
1123 .asm_volatile,
1124 .bit_and,
1125 .bitcast,
1126 .bitcast_ref,
1127 .bitcast_result_ptr,
1128 .bit_or,
1129 .block,
1130 .block_inline,
1131 .loop,
1132 .bool_br_and,
1133 .bool_br_or,
1134 .bool_not,
1135 .bool_and,
1136 .bool_or,
1137 .call_compile_time,
1138 .cmp_lt,
1139 .cmp_lte,
1140 .cmp_eq,
1141 .cmp_gte,
1142 .cmp_gt,
1143 .cmp_neq,
1144 .coerce_result_ptr,
1145 .decl_ref,
1146 .decl_val,
1147 .load,
1148 .div,
1149 .elem_ptr,
1150 .elem_val,
1151 .elem_ptr_node,
1152 .elem_val_node,
1153 .floatcast,
1154 .field_ptr,
1155 .field_val,
1156 .field_ptr_named,
1157 .field_val_named,
1158 .fn_type,
1159 .fn_type_var_args,
1160 .fn_type_cc,
1161 .fn_type_cc_var_args,
1162 .int,
1163 .intcast,
1164 .int_type,
1165 .is_non_null,
1166 .is_null,
1167 .is_non_null_ptr,
1168 .is_null_ptr,
1169 .is_err,
1170 .is_err_ptr,
1171 .mod_rem,
1172 .mul,
1173 .mulwrap,
1174 .param_type,
1175 .ptrtoint,
1176 .ref,
1177 .ret_ptr,
1178 .ret_type,
1179 .shl,
1180 .shr,
1181 .str,
1182 .sub,
1183 .subwrap,
1184 .negate,
1185 .negate_wrap,
1186 .typeof,
1187 .xor,
1188 .optional_type,
1189 .optional_type_from_ptr_elem,
1190 .optional_payload_safe,
1191 .optional_payload_unsafe,
1192 .optional_payload_safe_ptr,
1193 .optional_payload_unsafe_ptr,
1194 .err_union_payload_safe,
1195 .err_union_payload_unsafe,
1196 .err_union_payload_safe_ptr,
1197 .err_union_payload_unsafe_ptr,
1198 .err_union_code,
1199 .err_union_code_ptr,
1200 .ptr_type,
1201 .ptr_type_simple,
1202 .enum_literal,
1203 .enum_literal_small,
1204 .merge_error_sets,
1205 .error_union_type,
1206 .bit_not,
1207 .error_set,
1208 .error_value,
1209 .slice_start,
1210 .slice_end,
1211 .slice_sentinel,
1212 .import,
1213 .typeof_peer,
1214 => break :b false,
1215
1216 // ZIR instructions that are always either `noreturn` or `void`.
1217 .breakpoint,
1218 .dbg_stmt_node,
1219 .ensure_result_used,
1220 .ensure_result_non_error,
1221 .set_eval_branch_quota,
1222 .compile_log,
1223 .ensure_err_payload_void,
1224 .@"break",
1225 .break_inline,
1226 .condbr,
1227 .condbr_inline,
1228 .compile_error,
1229 .ret_node,
1230 .ret_tok,
1231 .ret_coerce,
1232 .@"unreachable",
1233 .elided,
1234 .store,
1235 .store_node,
1236 .store_to_block_ptr,
1237 .store_to_inferred_ptr,
1238 .resolve_inferred_alloc,
1239 .repeat,
1240 .repeat_inline,
1241 => break :b true,
1242 }
1243 } else switch (maybe_unused_result) {
1244 .none => unreachable,
1245
1246 .void_value,
1247 .unreachable_value,
1248 => true,
1249
1250 else => false,
1251 };
1252 if (!elide_check) {
1253 _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
1254 }
1255 },
1256 }
1257 }
1258}
1259
1260fn varDecl(
1261 mod: *Module,
1262 scope: *Scope,
1263 node: ast.Node.Index,
1264 block_arena: *Allocator,
1265 var_decl: ast.full.VarDecl,
1266) InnerError!*Scope {
1267 if (var_decl.comptime_token) |comptime_token| {
1268 return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
1269 }
1270 if (var_decl.ast.align_node != 0) {
1271 return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{});
1272 }
1273 const gz = scope.getGenZir();
1274 const astgen = gz.astgen;
1275 const tree = scope.tree();
1276 const token_tags = tree.tokens.items(.tag);
1277
1278 const name_token = var_decl.ast.mut_token + 1;
1279 const name_src = gz.tokSrcLoc(name_token);
1280 const ident_name = try mod.identifierTokenString(scope, name_token);
1281
1282 // Local variables shadowing detection, including function parameters.
1283 {
1284 var s = scope;
1285 while (true) switch (s.tag) {
1286 .local_val => {
1287 const local_val = s.cast(Scope.LocalVal).?;
1288 if (mem.eql(u8, local_val.name, ident_name)) {
1289 const msg = msg: {
1290 const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
1291 ident_name,
1292 });
1293 errdefer msg.destroy(mod.gpa);
1294 try mod.errNote(scope, local_val.src, msg, "previous definition is here", .{});
1295 break :msg msg;
1296 };
1297 return mod.failWithOwnedErrorMsg(scope, msg);
1298 }
1299 s = local_val.parent;
1300 },
1301 .local_ptr => {
1302 const local_ptr = s.cast(Scope.LocalPtr).?;
1303 if (mem.eql(u8, local_ptr.name, ident_name)) {
1304 const msg = msg: {
1305 const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
1306 ident_name,
1307 });
1308 errdefer msg.destroy(mod.gpa);
1309 try mod.errNote(scope, local_ptr.src, msg, "previous definition is here", .{});
1310 break :msg msg;
1311 };
1312 return mod.failWithOwnedErrorMsg(scope, msg);
1313 }
1314 s = local_ptr.parent;
1315 },
1316 .gen_zir => s = s.cast(Scope.GenZir).?.parent,
1317 else => break,
1318 };
1319 }
1320
1321 // Namespace vars shadowing detection
1322 if (mod.lookupDeclName(scope, ident_name)) |_| {
1323 // TODO add note for other definition
1324 return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});
1325 }
1326 if (var_decl.ast.init_node == 0) {
1327 return mod.fail(scope, name_src, "variables must be initialized", .{});
1328 }
1329
1330 switch (token_tags[var_decl.ast.mut_token]) {
1331 .keyword_const => {
1332 // Depending on the type of AST the initialization expression is, we may need an lvalue
1333 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
1334 // the variable, no memory location needed.
1335 if (!nodeMayNeedMemoryLocation(scope, var_decl.ast.init_node)) {
1336 const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{
1337 .ty = try typeExpr(mod, scope, var_decl.ast.type_node),
1338 } else .none;
1339 const init_inst = try expr(mod, scope, result_loc, var_decl.ast.init_node);
1340 const sub_scope = try block_arena.create(Scope.LocalVal);
1341 sub_scope.* = .{
1342 .parent = scope,
1343 .gen_zir = gz,
1344 .name = ident_name,
1345 .inst = init_inst,
1346 .src = name_src,
1347 };
1348 return &sub_scope.base;
1349 }
1350
1351 // Detect whether the initialization expression actually uses the
1352 // result location pointer.
1353 var init_scope: Scope.GenZir = .{
1354 .parent = scope,
1355 .force_comptime = gz.force_comptime,
1356 .astgen = astgen,
1357 };
1358 defer init_scope.instructions.deinit(mod.gpa);
1359
1360 var resolve_inferred_alloc: zir.Inst.Ref = .none;
1361 var opt_type_inst: zir.Inst.Ref = .none;
1362 if (var_decl.ast.type_node != 0) {
1363 const type_inst = try typeExpr(mod, &init_scope.base, var_decl.ast.type_node);
1364 opt_type_inst = type_inst;
1365 init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node);
1366 } else {
1367 const alloc = try init_scope.addUnNode(.alloc_inferred, undefined, node);
1368 resolve_inferred_alloc = alloc;
1369 init_scope.rl_ptr = alloc;
1370 }
1371 const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };
1372 const init_inst = try expr(mod, &init_scope.base, init_result_loc, var_decl.ast.init_node);
1373 const zir_tags = astgen.instructions.items(.tag);
1374 const zir_datas = astgen.instructions.items(.data);
1375
1376 const parent_zir = &gz.instructions;
1377 if (init_scope.rvalue_rl_count == 1) {
1378 // Result location pointer not used. We don't need an alloc for this
1379 // const local, and type inference becomes trivial.
1380 // Move the init_scope instructions into the parent scope, eliding
1381 // the alloc instruction and the store_to_block_ptr instruction.
1382 const expected_len = parent_zir.items.len + init_scope.instructions.items.len - 2;
1383 try parent_zir.ensureCapacity(mod.gpa, expected_len);
1384 for (init_scope.instructions.items) |src_inst| {
1385 if (astgen.indexToRef(src_inst) == init_scope.rl_ptr) continue;
1386 if (zir_tags[src_inst] == .store_to_block_ptr) {
1387 if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue;
1388 }
1389 parent_zir.appendAssumeCapacity(src_inst);
1390 }
1391 assert(parent_zir.items.len == expected_len);
1392 const casted_init = if (opt_type_inst != .none)
1393 try gz.addPlNode(.as_node, var_decl.ast.type_node, zir.Inst.As{
1394 .dest_type = opt_type_inst,
1395 .operand = init_inst,
1396 })
1397 else
1398 init_inst;
1399
1400 const sub_scope = try block_arena.create(Scope.LocalVal);
1401 sub_scope.* = .{
1402 .parent = scope,
1403 .gen_zir = gz,
1404 .name = ident_name,
1405 .inst = casted_init,
1406 .src = name_src,
1407 };
1408 return &sub_scope.base;
1409 }
1410 // The initialization expression took advantage of the result location
1411 // of the const local. In this case we will create an alloc and a LocalPtr for it.
1412 // Move the init_scope instructions into the parent scope, swapping
1413 // store_to_block_ptr for store_to_inferred_ptr.
1414 const expected_len = parent_zir.items.len + init_scope.instructions.items.len;
1415 try parent_zir.ensureCapacity(mod.gpa, expected_len);
1416 for (init_scope.instructions.items) |src_inst| {
1417 if (zir_tags[src_inst] == .store_to_block_ptr) {
1418 if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) {
1419 zir_tags[src_inst] = .store_to_inferred_ptr;
1420 }
1421 }
1422 parent_zir.appendAssumeCapacity(src_inst);
1423 }
1424 assert(parent_zir.items.len == expected_len);
1425 if (resolve_inferred_alloc != .none) {
1426 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
1427 }
1428 const sub_scope = try block_arena.create(Scope.LocalPtr);
1429 sub_scope.* = .{
1430 .parent = scope,
1431 .gen_zir = gz,
1432 .name = ident_name,
1433 .ptr = init_scope.rl_ptr,
1434 .src = name_src,
1435 };
1436 return &sub_scope.base;
1437 },
1438 .keyword_var => {
1439 var resolve_inferred_alloc: zir.Inst.Ref = .none;
1440 const var_data: struct {
1441 result_loc: ResultLoc,
1442 alloc: zir.Inst.Ref,
1443 } = if (var_decl.ast.type_node != 0) a: {
1444 const type_inst = try typeExpr(mod, scope, var_decl.ast.type_node);
1445
1446 const alloc = try gz.addUnNode(.alloc_mut, type_inst, node);
1447 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
1448 } else a: {
1449 const alloc = try gz.addUnNode(.alloc_inferred_mut, undefined, node);
1450 resolve_inferred_alloc = alloc;
1451 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } };
1452 };
1453 const init_inst = try expr(mod, scope, var_data.result_loc, var_decl.ast.init_node);
1454 if (resolve_inferred_alloc != .none) {
1455 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
1456 }
1457 const sub_scope = try block_arena.create(Scope.LocalPtr);
1458 sub_scope.* = .{
1459 .parent = scope,
1460 .gen_zir = gz,
1461 .name = ident_name,
1462 .ptr = var_data.alloc,
1463 .src = name_src,
1464 };
1465 return &sub_scope.base;
1466 },
1467 else => unreachable,
1468 }
1469}
1470
1471fn assign(mod: *Module, scope: *Scope, infix_node: ast.Node.Index) InnerError!void {
1472 const tree = scope.tree();
1473 const node_datas = tree.nodes.items(.data);
1474 const main_tokens = tree.nodes.items(.main_token);
1475 const node_tags = tree.nodes.items(.tag);
1476
1477 const lhs = node_datas[infix_node].lhs;
1478 const rhs = node_datas[infix_node].rhs;
1479 if (node_tags[lhs] == .identifier) {
1480 // This intentionally does not support `@"_"` syntax.
1481 const ident_name = tree.tokenSlice(main_tokens[lhs]);
1482 if (mem.eql(u8, ident_name, "_")) {
1483 _ = try expr(mod, scope, .discard, rhs);
1484 return;
1485 }
1486 }
1487 const lvalue = try lvalExpr(mod, scope, lhs);
1488 _ = try expr(mod, scope, .{ .ptr = lvalue }, rhs);
1489}
1490
1491fn assignOp(
1492 mod: *Module,
1493 scope: *Scope,
1494 infix_node: ast.Node.Index,
1495 op_inst_tag: zir.Inst.Tag,
1496) InnerError!void {
1497 const tree = scope.tree();
1498 const node_datas = tree.nodes.items(.data);
1499 const gz = scope.getGenZir();
1500
1501 const lhs_ptr = try lvalExpr(mod, scope, node_datas[infix_node].lhs);
1502 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
1503 const lhs_type = try gz.addUnTok(.typeof, lhs, infix_node);
1504 const rhs = try expr(mod, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);
1505
1506 const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{
1507 .lhs = lhs,
1508 .rhs = rhs,
1509 });
1510 _ = try gz.addBin(.store, lhs_ptr, result);
1511}
1512
1513fn boolNot(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref {
1514 const tree = scope.tree();
1515 const node_datas = tree.nodes.items(.data);
1516
1517 const operand = try expr(mod, scope, .{ .ty = .bool_type }, node_datas[node].lhs);
1518 const gz = scope.getGenZir();
1519 const result = try gz.addUnNode(.bool_not, operand, node);
1520 return rvalue(mod, scope, rl, result, node);
1521}
1522
1523fn bitNot(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref {
1524 const tree = scope.tree();
1525 const node_datas = tree.nodes.items(.data);
1526
1527 const gz = scope.getGenZir();
1528 const operand = try expr(mod, scope, .none, node_datas[node].lhs);
1529 const result = try gz.addUnNode(.bit_not, operand, node);
1530 return rvalue(mod, scope, rl, result, node);
1531}
1532
1533fn negation(
1534 mod: *Module,
1535 scope: *Scope,
1536 rl: ResultLoc,
1537 node: ast.Node.Index,
1538 tag: zir.Inst.Tag,
1539) InnerError!zir.Inst.Ref {
1540 const tree = scope.tree();
1541 const node_datas = tree.nodes.items(.data);
1542
1543 const gz = scope.getGenZir();
1544 const operand = try expr(mod, scope, .none, node_datas[node].lhs);
1545 const result = try gz.addUnNode(tag, operand, node);
1546 return rvalue(mod, scope, rl, result, node);
1547}
1548
1549fn ptrType(
1550 mod: *Module,
1551 scope: *Scope,
1552 rl: ResultLoc,
1553 node: ast.Node.Index,
1554 ptr_info: ast.full.PtrType,
1555) InnerError!zir.Inst.Ref {
1556 const tree = scope.tree();
1557 const gz = scope.getGenZir();
1558
1559 const elem_type = try typeExpr(mod, scope, ptr_info.ast.child_type);
1560
1561 const simple = ptr_info.ast.align_node == 0 and
1562 ptr_info.ast.sentinel == 0 and
1563 ptr_info.ast.bit_range_start == 0;
1564
1565 if (simple) {
1566 const result = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
1567 .ptr_type_simple = .{
1568 .is_allowzero = ptr_info.allowzero_token != null,
1569 .is_mutable = ptr_info.const_token == null,
1570 .is_volatile = ptr_info.volatile_token != null,
1571 .size = ptr_info.size,
1572 .elem_type = elem_type,
1573 },
1574 } });
1575 return rvalue(mod, scope, rl, result, node);
1576 }
1577
1578 var sentinel_ref: zir.Inst.Ref = .none;
1579 var align_ref: zir.Inst.Ref = .none;
1580 var bit_start_ref: zir.Inst.Ref = .none;
1581 var bit_end_ref: zir.Inst.Ref = .none;
1582 var trailing_count: u32 = 0;
1583
1584 if (ptr_info.ast.sentinel != 0) {
1585 sentinel_ref = try expr(mod, scope, .{ .ty = elem_type }, ptr_info.ast.sentinel);
1586 trailing_count += 1;
1587 }
1588 if (ptr_info.ast.align_node != 0) {
1589 align_ref = try expr(mod, scope, .none, ptr_info.ast.align_node);
1590 trailing_count += 1;
1591 }
1592 if (ptr_info.ast.bit_range_start != 0) {
1593 assert(ptr_info.ast.bit_range_end != 0);
1594 bit_start_ref = try expr(mod, scope, .none, ptr_info.ast.bit_range_start);
1595 bit_end_ref = try expr(mod, scope, .none, ptr_info.ast.bit_range_end);
1596 trailing_count += 2;
1597 }
1598
1599 const gpa = gz.astgen.mod.gpa;
1600 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1601 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1602 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
1603 @typeInfo(zir.Inst.PtrType).Struct.fields.len + trailing_count);
1604
1605 const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.PtrType{ .elem_type = elem_type });
1606 if (sentinel_ref != .none) {
1607 gz.astgen.extra.appendAssumeCapacity(@enumToInt(sentinel_ref));
1608 }
1609 if (align_ref != .none) {
1610 gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref));
1611 }
1612 if (bit_start_ref != .none) {
1613 gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref));
1614 gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref));
1615 }
1616
1617 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1618 const result = gz.astgen.indexToRef(new_index);
1619 gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
1620 .ptr_type = .{
1621 .flags = .{
1622 .is_allowzero = ptr_info.allowzero_token != null,
1623 .is_mutable = ptr_info.const_token == null,
1624 .is_volatile = ptr_info.volatile_token != null,
1625 .has_sentinel = sentinel_ref != .none,
1626 .has_align = align_ref != .none,
1627 .has_bit_range = bit_start_ref != .none,
1628 },
1629 .size = ptr_info.size,
1630 .payload_index = payload_index,
1631 },
1632 } });
1633 gz.instructions.appendAssumeCapacity(new_index);
1634
1635 return rvalue(mod, scope, rl, result, node);
1636}
1637
1638fn arrayType(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref {
1639 const tree = scope.tree();
1640 const node_datas = tree.nodes.items(.data);
1641 const gz = scope.getGenZir();
1642
1643 // TODO check for [_]T
1644 const len = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].lhs);
1645 const elem_type = try typeExpr(mod, scope, node_datas[node].rhs);
1646
1647 const result = try gz.addBin(.array_type, len, elem_type);
1648 return rvalue(mod, scope, rl, result, node);
1649}
1650
1651fn arrayTypeSentinel(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref {
1652 const tree = scope.tree();
1653 const node_datas = tree.nodes.items(.data);
1654 const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel);
1655 const gz = scope.getGenZir();
1656
1657 // TODO check for [_]T
1658 const len = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].lhs);
1659 const elem_type = try typeExpr(mod, scope, extra.elem_type);
1660 const sentinel = try expr(mod, scope, .{ .ty = elem_type }, extra.sentinel);
1661
1662 const result = try gz.addArrayTypeSentinel(len, elem_type, sentinel);
1663 return rvalue(mod, scope, rl, result, node);
1664}
1665
1666fn containerDecl(
1667 mod: *Module,
1668 scope: *Scope,
1669 rl: ResultLoc,
1670 container_decl: ast.full.ContainerDecl,
1671) InnerError!zir.Inst.Ref {
1672 return mod.failTok(scope, container_decl.ast.main_token, "TODO implement container decls", .{});
1673}
1674
1675fn errorSetDecl(
1676 mod: *Module,
1677 scope: *Scope,
1678 rl: ResultLoc,
1679 node: ast.Node.Index,
1680) InnerError!zir.Inst.Ref {
1681 if (true) @panic("TODO update for zir-memory-layout branch");
1682 const gz = scope.getGenZir();
1683 const tree = gz.tree();
1684 const main_tokens = tree.nodes.items(.main_token);
1685 const token_tags = tree.tokens.items(.tag);
1686
1687 // Count how many fields there are.
1688 const error_token = main_tokens[node];
1689 const count: usize = count: {
1690 var tok_i = error_token + 2;
1691 var count: usize = 0;
1692 while (true) : (tok_i += 1) {
1693 switch (token_tags[tok_i]) {
1694 .doc_comment, .comma => {},
1695 .identifier => count += 1,
1696 .r_brace => break :count count,
1697 else => unreachable,
1698 }
1699 } else unreachable; // TODO should not need else unreachable here
1700 };
1701
1702 const fields = try scope.arena().alloc([]const u8, count);
1703 {
1704 var tok_i = error_token + 2;
1705 var field_i: usize = 0;
1706 while (true) : (tok_i += 1) {
1707 switch (token_tags[tok_i]) {
1708 .doc_comment, .comma => {},
1709 .identifier => {
1710 fields[field_i] = try mod.identifierTokenString(scope, tok_i);
1711 field_i += 1;
1712 },
1713 .r_brace => break,
1714 else => unreachable,
1715 }
1716 }
1717 }
1718 const result = try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{});
1719 return rvalue(mod, scope, rl, result);
1720}
1721
1722fn orelseCatchExpr(
1723 mod: *Module,
1724 scope: *Scope,
1725 rl: ResultLoc,
1726 node: ast.Node.Index,
1727 lhs: ast.Node.Index,
1728 cond_op: zir.Inst.Tag,
1729 unwrap_op: zir.Inst.Tag,
1730 unwrap_code_op: zir.Inst.Tag,
1731 rhs: ast.Node.Index,
1732 payload_token: ?ast.TokenIndex,
1733) InnerError!zir.Inst.Ref {
1734 const parent_gz = scope.getGenZir();
1735 const tree = parent_gz.tree();
1736
1737 var block_scope: Scope.GenZir = .{
1738 .parent = scope,
1739 .astgen = parent_gz.astgen,
1740 .force_comptime = parent_gz.force_comptime,
1741 .instructions = .{},
1742 };
1743 setBlockResultLoc(&block_scope, rl);
1744 defer block_scope.instructions.deinit(mod.gpa);
1745
1746 // This could be a pointer or value depending on the `operand_rl` parameter.
1747 // We cannot use `block_scope.break_result_loc` because that has the bare
1748 // type, whereas this expression has the optional type. Later we make
1749 // up for this fact by calling rvalue on the else branch.
1750 block_scope.break_count += 1;
1751
1752 // TODO handle catch
1753 const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
1754 .ref => .ref,
1755 .discard, .none, .block_ptr, .inferred_ptr, .bitcasted_ptr => .none,
1756 .ty => |elem_ty| blk: {
1757 const wrapped_ty = try block_scope.addUnNode(.optional_type, elem_ty, node);
1758 break :blk .{ .ty = wrapped_ty };
1759 },
1760 .ptr => |ptr_ty| blk: {
1761 const wrapped_ty = try block_scope.addUnNode(.optional_type_from_ptr_elem, ptr_ty, node);
1762 break :blk .{ .ty = wrapped_ty };
1763 },
1764 };
1765 const operand = try expr(mod, &block_scope.base, operand_rl, lhs);
1766 const cond = try block_scope.addUnNode(cond_op, operand, node);
1767 const condbr = try block_scope.addCondBr(.condbr, node);
1768
1769 const block = try parent_gz.addBlock(.block, node);
1770 try parent_gz.instructions.append(mod.gpa, block);
1771 try block_scope.setBlockBody(block);
1772
1773 var then_scope: Scope.GenZir = .{
1774 .parent = scope,
1775 .astgen = parent_gz.astgen,
1776 .force_comptime = block_scope.force_comptime,
1777 .instructions = .{},
1778 };
1779 defer then_scope.instructions.deinit(mod.gpa);
1780
1781 var err_val_scope: Scope.LocalVal = undefined;
1782 const then_sub_scope = blk: {
1783 const payload = payload_token orelse break :blk &then_scope.base;
1784 if (mem.eql(u8, tree.tokenSlice(payload), "_")) {
1785 return mod.failTok(&then_scope.base, payload, "discard of error capture; omit it instead", .{});
1786 }
1787 const err_name = try mod.identifierTokenString(scope, payload);
1788 err_val_scope = .{
1789 .parent = &then_scope.base,
1790 .gen_zir = &then_scope,
1791 .name = err_name,
1792 .inst = try then_scope.addUnNode(unwrap_code_op, operand, node),
1793 .src = parent_gz.tokSrcLoc(payload),
1794 };
1795 break :blk &err_val_scope.base;
1796 };
1797
1798 block_scope.break_count += 1;
1799 const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, rhs);
1800 // We hold off on the break instructions as well as copying the then/else
1801 // instructions into place until we know whether to keep store_to_block_ptr
1802 // instructions or not.
1803
1804 var else_scope: Scope.GenZir = .{
1805 .parent = scope,
1806 .astgen = parent_gz.astgen,
1807 .force_comptime = block_scope.force_comptime,
1808 .instructions = .{},
1809 };
1810 defer else_scope.instructions.deinit(mod.gpa);
1811
1812 // This could be a pointer or value depending on `unwrap_op`.
1813 const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node);
1814 const else_result = switch (rl) {
1815 .ref => unwrapped_payload,
1816 else => try rvalue(mod, &else_scope.base, block_scope.break_result_loc, unwrapped_payload, node),
1817 };
1818
1819 return finishThenElseBlock(
1820 mod,
1821 scope,
1822 rl,
1823 node,
1824 &block_scope,
1825 &then_scope,
1826 &else_scope,
1827 condbr,
1828 cond,
1829 node,
1830 node,
1831 then_result,
1832 else_result,
1833 block,
1834 block,
1835 .@"break",
1836 );
1837}
1838
1839fn finishThenElseBlock(
1840 mod: *Module,
1841 parent_scope: *Scope,
1842 rl: ResultLoc,
1843 node: ast.Node.Index,
1844 block_scope: *Scope.GenZir,
1845 then_scope: *Scope.GenZir,
1846 else_scope: *Scope.GenZir,
1847 condbr: zir.Inst.Index,
1848 cond: zir.Inst.Ref,
1849 then_src: ast.Node.Index,
1850 else_src: ast.Node.Index,
1851 then_result: zir.Inst.Ref,
1852 else_result: zir.Inst.Ref,
1853 main_block: zir.Inst.Index,
1854 then_break_block: zir.Inst.Index,
1855 break_tag: zir.Inst.Tag,
1856) InnerError!zir.Inst.Ref {
1857 // We now have enough information to decide whether the result instruction should
1858 // be communicated via result location pointer or break instructions.
1859 const strat = rlStrategy(rl, block_scope);
1860 const astgen = block_scope.astgen;
1861 switch (strat.tag) {
1862 .break_void => {
1863 if (!astgen.refIsNoReturn(then_result)) {
1864 _ = try then_scope.addBreak(break_tag, then_break_block, .void_value);
1865 }
1866 const elide_else = if (else_result != .none) astgen.refIsNoReturn(else_result) else false;
1867 if (!elide_else) {
1868 _ = try else_scope.addBreak(break_tag, main_block, .void_value);
1869 }
1870 assert(!strat.elide_store_to_block_ptr_instructions);
1871 try setCondBrPayload(condbr, cond, then_scope, else_scope);
1872 return astgen.indexToRef(main_block);
1873 },
1874 .break_operand => {
1875 if (!astgen.refIsNoReturn(then_result)) {
1876 _ = try then_scope.addBreak(break_tag, then_break_block, then_result);
1877 }
1878 if (else_result != .none) {
1879 if (!astgen.refIsNoReturn(else_result)) {
1880 _ = try else_scope.addBreak(break_tag, main_block, else_result);
1881 }
1882 } else {
1883 _ = try else_scope.addBreak(break_tag, main_block, .void_value);
1884 }
1885 if (strat.elide_store_to_block_ptr_instructions) {
1886 try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope);
1887 } else {
1888 try setCondBrPayload(condbr, cond, then_scope, else_scope);
1889 }
1890 const block_ref = astgen.indexToRef(main_block);
1891 switch (rl) {
1892 .ref => return block_ref,
1893 else => return rvalue(mod, parent_scope, rl, block_ref, node),
1894 }
1895 },
1896 }
1897}
1898
1899/// Return whether the identifier names of two tokens are equal. Resolves @""
1900/// tokens without allocating.
1901/// OK in theory it could do it without allocating. This implementation
1902/// allocates when the @"" form is used.
1903fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
1904 const ident_name_1 = try mod.identifierTokenString(scope, token1);
1905 const ident_name_2 = try mod.identifierTokenString(scope, token2);
1906 return mem.eql(u8, ident_name_1, ident_name_2);
1907}
1908
1909pub fn fieldAccess(
1910 mod: *Module,
1911 scope: *Scope,
1912 rl: ResultLoc,
1913 node: ast.Node.Index,
1914) InnerError!zir.Inst.Ref {
1915 const gz = scope.getGenZir();
1916 const tree = gz.tree();
1917 const main_tokens = tree.nodes.items(.main_token);
1918 const node_datas = tree.nodes.items(.data);
1919 const object_node = node_datas[node].lhs;
1920 const dot_token = main_tokens[node];
1921 const field_ident = dot_token + 1;
1922 const string_bytes = &gz.astgen.string_bytes;
1923 const str_index = @intCast(u32, string_bytes.items.len);
1924 try mod.appendIdentStr(scope, field_ident, string_bytes);
1925 try string_bytes.append(mod.gpa, 0);
1926 switch (rl) {
1927 .ref => return gz.addPlNode(.field_ptr, node, zir.Inst.Field{
1928 .lhs = try expr(mod, scope, .ref, object_node),
1929 .field_name_start = str_index,
1930 }),
1931 else => return rvalue(mod, scope, rl, try gz.addPlNode(.field_val, node, zir.Inst.Field{
1932 .lhs = try expr(mod, scope, .none, object_node),
1933 .field_name_start = str_index,
1934 }), node),
1935 }
1936}
1937
1938fn arrayAccess(
1939 mod: *Module,
1940 scope: *Scope,
1941 rl: ResultLoc,
1942 node: ast.Node.Index,
1943) InnerError!zir.Inst.Ref {
1944 const gz = scope.getGenZir();
1945 const tree = gz.tree();
1946 const main_tokens = tree.nodes.items(.main_token);
1947 const node_datas = tree.nodes.items(.data);
1948 switch (rl) {
1949 .ref => return gz.addBin(
1950 .elem_ptr,
1951 try expr(mod, scope, .ref, node_datas[node].lhs),
1952 try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
1953 ),
1954 else => return rvalue(mod, scope, rl, try gz.addBin(
1955 .elem_val,
1956 try expr(mod, scope, .none, node_datas[node].lhs),
1957 try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
1958 ), node),
1959 }
1960}
1961
1962fn simpleBinOp(
1963 mod: *Module,
1964 scope: *Scope,
1965 rl: ResultLoc,
1966 node: ast.Node.Index,
1967 op_inst_tag: zir.Inst.Tag,
1968) InnerError!zir.Inst.Ref {
1969 const gz = scope.getGenZir();
1970 const tree = gz.tree();
1971 const node_datas = tree.nodes.items(.data);
1972
1973 const result = try gz.addPlNode(op_inst_tag, node, zir.Inst.Bin{
1974 .lhs = try expr(mod, scope, .none, node_datas[node].lhs),
1975 .rhs = try expr(mod, scope, .none, node_datas[node].rhs),
1976 });
1977 return rvalue(mod, scope, rl, result, node);
1978}
1979
1980fn simpleStrTok(
1981 mod: *Module,
1982 scope: *Scope,
1983 rl: ResultLoc,
1984 ident_token: ast.TokenIndex,
1985 node: ast.Node.Index,
1986 op_inst_tag: zir.Inst.Tag,
1987) InnerError!zir.Inst.Ref {
1988 const gz = scope.getGenZir();
1989 const string_bytes = &gz.astgen.string_bytes;
1990 const str_index = @intCast(u32, string_bytes.items.len);
1991 try mod.appendIdentStr(scope, ident_token, string_bytes);
1992 try string_bytes.append(mod.gpa, 0);
1993 const result = try gz.addStrTok(op_inst_tag, str_index, ident_token);
1994 return rvalue(mod, scope, rl, result, node);
1995}
1996
1997fn boolBinOp(
1998 mod: *Module,
1999 scope: *Scope,
2000 rl: ResultLoc,
2001 node: ast.Node.Index,
2002 zir_tag: zir.Inst.Tag,
2003) InnerError!zir.Inst.Ref {
2004 const gz = scope.getGenZir();
2005 const node_datas = gz.tree().nodes.items(.data);
2006
2007 const lhs = try expr(mod, scope, .{ .ty = .bool_type }, node_datas[node].lhs);
2008 const bool_br = try gz.addBoolBr(zir_tag, lhs);
2009
2010 var rhs_scope: Scope.GenZir = .{
2011 .parent = scope,
2012 .astgen = gz.astgen,
2013 .force_comptime = gz.force_comptime,
2014 };
2015 defer rhs_scope.instructions.deinit(mod.gpa);
2016 const rhs = try expr(mod, &rhs_scope.base, .{ .ty = .bool_type }, node_datas[node].rhs);
2017 _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
2018 try rhs_scope.setBoolBrBody(bool_br);
2019
2020 const block_ref = gz.astgen.indexToRef(bool_br);
2021 return rvalue(mod, scope, rl, block_ref, node);
2022}
2023
2024fn ifExpr(
2025 mod: *Module,
2026 scope: *Scope,
2027 rl: ResultLoc,
2028 node: ast.Node.Index,
2029 if_full: ast.full.If,
2030) InnerError!zir.Inst.Ref {
2031 const parent_gz = scope.getGenZir();
2032 var block_scope: Scope.GenZir = .{
2033 .parent = scope,
2034 .astgen = parent_gz.astgen,
2035 .force_comptime = parent_gz.force_comptime,
2036 .instructions = .{},
2037 };
2038 setBlockResultLoc(&block_scope, rl);
2039 defer block_scope.instructions.deinit(mod.gpa);
2040
2041 const cond = c: {
2042 // TODO https://github.com/ziglang/zig/issues/7929
2043 if (if_full.error_token) |error_token| {
2044 return mod.failTok(scope, error_token, "TODO implement if error union", .{});
2045 } else if (if_full.payload_token) |payload_token| {
2046 return mod.failTok(scope, payload_token, "TODO implement if optional", .{});
2047 } else {
2048 break :c try expr(mod, &block_scope.base, .{ .ty = .bool_type }, if_full.ast.cond_expr);
2049 }
2050 };
2051
2052 const condbr = try block_scope.addCondBr(.condbr, node);
2053
2054 const block = try parent_gz.addBlock(.block, node);
2055 try parent_gz.instructions.append(mod.gpa, block);
2056 try block_scope.setBlockBody(block);
2057
2058 var then_scope: Scope.GenZir = .{
2059 .parent = scope,
2060 .astgen = parent_gz.astgen,
2061 .force_comptime = block_scope.force_comptime,
2062 .instructions = .{},
2063 };
2064 defer then_scope.instructions.deinit(mod.gpa);
2065
2066 // declare payload to the then_scope
2067 const then_sub_scope = &then_scope.base;
2068
2069 block_scope.break_count += 1;
2070 const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr);
2071 // We hold off on the break instructions as well as copying the then/else
2072 // instructions into place until we know whether to keep store_to_block_ptr
2073 // instructions or not.
2074
2075 var else_scope: Scope.GenZir = .{
2076 .parent = scope,
2077 .astgen = parent_gz.astgen,
2078 .force_comptime = block_scope.force_comptime,
2079 .instructions = .{},
2080 };
2081 defer else_scope.instructions.deinit(mod.gpa);
2082
2083 const else_node = if_full.ast.else_expr;
2084 const else_info: struct {
2085 src: ast.Node.Index,
2086 result: zir.Inst.Ref,
2087 } = if (else_node != 0) blk: {
2088 block_scope.break_count += 1;
2089 const sub_scope = &else_scope.base;
2090 break :blk .{
2091 .src = else_node,
2092 .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node),
2093 };
2094 } else .{
2095 .src = if_full.ast.then_expr,
2096 .result = .none,
2097 };
2098
2099 return finishThenElseBlock(
2100 mod,
2101 scope,
2102 rl,
2103 node,
2104 &block_scope,
2105 &then_scope,
2106 &else_scope,
2107 condbr,
2108 cond,
2109 if_full.ast.then_expr,
2110 else_info.src,
2111 then_result,
2112 else_info.result,
2113 block,
2114 block,
2115 .@"break",
2116 );
2117}
2118
2119fn setCondBrPayload(
2120 condbr: zir.Inst.Index,
2121 cond: zir.Inst.Ref,
2122 then_scope: *Scope.GenZir,
2123 else_scope: *Scope.GenZir,
2124) !void {
2125 const astgen = then_scope.astgen;
2126
2127 try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len +
2128 @typeInfo(zir.Inst.CondBr).Struct.fields.len +
2129 then_scope.instructions.items.len + else_scope.instructions.items.len);
2130
2131 const zir_datas = astgen.instructions.items(.data);
2132 zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.CondBr{
2133 .condition = cond,
2134 .then_body_len = @intCast(u32, then_scope.instructions.items.len),
2135 .else_body_len = @intCast(u32, else_scope.instructions.items.len),
2136 });
2137 astgen.extra.appendSliceAssumeCapacity(then_scope.instructions.items);
2138 astgen.extra.appendSliceAssumeCapacity(else_scope.instructions.items);
2139}
2140
2141/// If `elide_block_store_ptr` is set, expects to find exactly 1 .store_to_block_ptr instruction.
2142fn setCondBrPayloadElideBlockStorePtr(
2143 condbr: zir.Inst.Index,
2144 cond: zir.Inst.Ref,
2145 then_scope: *Scope.GenZir,
2146 else_scope: *Scope.GenZir,
2147) !void {
2148 const astgen = then_scope.astgen;
2149
2150 try astgen.extra.ensureCapacity(astgen.mod.gpa, astgen.extra.items.len +
2151 @typeInfo(zir.Inst.CondBr).Struct.fields.len +
2152 then_scope.instructions.items.len + else_scope.instructions.items.len - 2);
2153
2154 const zir_datas = astgen.instructions.items(.data);
2155 zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(zir.Inst.CondBr{
2156 .condition = cond,
2157 .then_body_len = @intCast(u32, then_scope.instructions.items.len - 1),
2158 .else_body_len = @intCast(u32, else_scope.instructions.items.len - 1),
2159 });
2160
2161 const zir_tags = astgen.instructions.items(.tag);
2162 for ([_]*Scope.GenZir{ then_scope, else_scope }) |scope| {
2163 for (scope.instructions.items) |src_inst| {
2164 if (zir_tags[src_inst] != .store_to_block_ptr) {
2165 astgen.extra.appendAssumeCapacity(src_inst);
2166 }
2167 }
2168 }
2169}
2170
2171fn whileExpr(
2172 mod: *Module,
2173 scope: *Scope,
2174 rl: ResultLoc,
2175 node: ast.Node.Index,
2176 while_full: ast.full.While,
2177) InnerError!zir.Inst.Ref {
2178 if (while_full.label_token) |label_token| {
2179 try checkLabelRedefinition(mod, scope, label_token);
2180 }
2181 const parent_gz = scope.getGenZir();
2182 const is_inline = parent_gz.force_comptime or while_full.inline_token != null;
2183 const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
2184 const loop_block = try parent_gz.addBlock(loop_tag, node);
2185 try parent_gz.instructions.append(mod.gpa, loop_block);
2186
2187 var loop_scope: Scope.GenZir = .{
2188 .parent = scope,
2189 .astgen = parent_gz.astgen,
2190 .force_comptime = parent_gz.force_comptime,
2191 .instructions = .{},
2192 };
2193 setBlockResultLoc(&loop_scope, rl);
2194 defer loop_scope.instructions.deinit(mod.gpa);
2195
2196 var continue_scope: Scope.GenZir = .{
2197 .parent = &loop_scope.base,
2198 .astgen = parent_gz.astgen,
2199 .force_comptime = loop_scope.force_comptime,
2200 .instructions = .{},
2201 };
2202 defer continue_scope.instructions.deinit(mod.gpa);
2203
2204 const cond = c: {
2205 // TODO https://github.com/ziglang/zig/issues/7929
2206 if (while_full.error_token) |error_token| {
2207 return mod.failTok(scope, error_token, "TODO implement while error union", .{});
2208 } else if (while_full.payload_token) |payload_token| {
2209 return mod.failTok(scope, payload_token, "TODO implement while optional", .{});
2210 } else {
2211 const bool_type_rl: ResultLoc = .{ .ty = .bool_type };
2212 break :c try expr(mod, &continue_scope.base, bool_type_rl, while_full.ast.cond_expr);
2213 }
2214 };
2215
2216 const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
2217 const condbr = try continue_scope.addCondBr(condbr_tag, node);
2218 const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
2219 const cond_block = try loop_scope.addBlock(block_tag, node);
2220 try loop_scope.instructions.append(mod.gpa, cond_block);
2221 try continue_scope.setBlockBody(cond_block);
2222
2223 // TODO avoid emitting the continue expr when there
2224 // are no jumps to it. This happens when the last statement of a while body is noreturn
2225 // and there are no `continue` statements.
2226 if (while_full.ast.cont_expr != 0) {
2227 _ = try expr(mod, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr);
2228 }
2229 const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
2230 _ = try loop_scope.addNode(repeat_tag, node);
2231
2232 try loop_scope.setBlockBody(loop_block);
2233 loop_scope.break_block = loop_block;
2234 loop_scope.continue_block = cond_block;
2235 if (while_full.label_token) |label_token| {
2236 loop_scope.label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{
2237 .token = label_token,
2238 .block_inst = loop_block,
2239 });
2240 }
2241
2242 var then_scope: Scope.GenZir = .{
2243 .parent = &continue_scope.base,
2244 .astgen = parent_gz.astgen,
2245 .force_comptime = continue_scope.force_comptime,
2246 .instructions = .{},
2247 };
2248 defer then_scope.instructions.deinit(mod.gpa);
2249
2250 const then_sub_scope = &then_scope.base;
2251
2252 loop_scope.break_count += 1;
2253 const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
2254
2255 var else_scope: Scope.GenZir = .{
2256 .parent = &continue_scope.base,
2257 .astgen = parent_gz.astgen,
2258 .force_comptime = continue_scope.force_comptime,
2259 .instructions = .{},
2260 };
2261 defer else_scope.instructions.deinit(mod.gpa);
2262
2263 const else_node = while_full.ast.else_expr;
2264 const else_info: struct {
2265 src: ast.Node.Index,
2266 result: zir.Inst.Ref,
2267 } = if (else_node != 0) blk: {
2268 loop_scope.break_count += 1;
2269 const sub_scope = &else_scope.base;
2270 break :blk .{
2271 .src = else_node,
2272 .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
2273 };
2274 } else .{
2275 .src = while_full.ast.then_expr,
2276 .result = .none,
2277 };
2278
2279 if (loop_scope.label) |some| {
2280 if (!some.used) {
2281 return mod.failTok(scope, some.token, "unused while loop label", .{});
2282 }
2283 }
2284 const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
2285 return finishThenElseBlock(
2286 mod,
2287 scope,
2288 rl,
2289 node,
2290 &loop_scope,
2291 &then_scope,
2292 &else_scope,
2293 condbr,
2294 cond,
2295 while_full.ast.then_expr,
2296 else_info.src,
2297 then_result,
2298 else_info.result,
2299 loop_block,
2300 cond_block,
2301 break_tag,
2302 );
2303}
2304
2305fn forExpr(
2306 mod: *Module,
2307 scope: *Scope,
2308 rl: ResultLoc,
2309 node: ast.Node.Index,
2310 for_full: ast.full.While,
2311) InnerError!zir.Inst.Ref {
2312 if (for_full.label_token) |label_token| {
2313 try checkLabelRedefinition(mod, scope, label_token);
2314 }
2315 // Set up variables and constants.
2316 const parent_gz = scope.getGenZir();
2317 const is_inline = parent_gz.force_comptime or for_full.inline_token != null;
2318 const tree = parent_gz.tree();
2319 const token_tags = tree.tokens.items(.tag);
2320
2321 const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr);
2322 const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
2323
2324 const index_ptr = blk: {
2325 const index_ptr = try parent_gz.addUnNode(.alloc, .usize_type, node);
2326 // initialize to zero
2327 _ = try parent_gz.addBin(.store, index_ptr, .zero_usize);
2328 break :blk index_ptr;
2329 };
2330
2331 const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
2332 const loop_block = try parent_gz.addBlock(loop_tag, node);
2333 try parent_gz.instructions.append(mod.gpa, loop_block);
2334
2335 var loop_scope: Scope.GenZir = .{
2336 .parent = scope,
2337 .astgen = parent_gz.astgen,
2338 .force_comptime = parent_gz.force_comptime,
2339 .instructions = .{},
2340 };
2341 setBlockResultLoc(&loop_scope, rl);
2342 defer loop_scope.instructions.deinit(mod.gpa);
2343
2344 var cond_scope: Scope.GenZir = .{
2345 .parent = &loop_scope.base,
2346 .astgen = parent_gz.astgen,
2347 .force_comptime = loop_scope.force_comptime,
2348 .instructions = .{},
2349 };
2350 defer cond_scope.instructions.deinit(mod.gpa);
2351
2352 // check condition i < array_expr.len
2353 const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
2354 const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, zir.Inst.Bin{
2355 .lhs = index,
2356 .rhs = len,
2357 });
2358
2359 const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
2360 const condbr = try cond_scope.addCondBr(condbr_tag, node);
2361 const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
2362 const cond_block = try loop_scope.addBlock(block_tag, node);
2363 try loop_scope.instructions.append(mod.gpa, cond_block);
2364 try cond_scope.setBlockBody(cond_block);
2365
2366 // Increment the index variable.
2367 const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
2368 const index_plus_one = try loop_scope.addPlNode(.add, node, zir.Inst.Bin{
2369 .lhs = index_2,
2370 .rhs = .one_usize,
2371 });
2372 _ = try loop_scope.addBin(.store, index_ptr, index_plus_one);
2373 const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
2374 _ = try loop_scope.addNode(repeat_tag, node);
2375
2376 try loop_scope.setBlockBody(loop_block);
2377 loop_scope.break_block = loop_block;
2378 loop_scope.continue_block = cond_block;
2379 if (for_full.label_token) |label_token| {
2380 loop_scope.label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{
2381 .token = label_token,
2382 .block_inst = loop_block,
2383 });
2384 }
2385
2386 var then_scope: Scope.GenZir = .{
2387 .parent = &cond_scope.base,
2388 .astgen = parent_gz.astgen,
2389 .force_comptime = cond_scope.force_comptime,
2390 .instructions = .{},
2391 };
2392 defer then_scope.instructions.deinit(mod.gpa);
2393
2394 var index_scope: Scope.LocalPtr = undefined;
2395 const then_sub_scope = blk: {
2396 const payload_token = for_full.payload_token.?;
2397 const ident = if (token_tags[payload_token] == .asterisk)
2398 payload_token + 1
2399 else
2400 payload_token;
2401 const is_ptr = ident != payload_token;
2402 const value_name = tree.tokenSlice(ident);
2403 if (!mem.eql(u8, value_name, "_")) {
2404 return mod.failNode(&then_scope.base, ident, "TODO implement for loop value payload", .{});
2405 } else if (is_ptr) {
2406 return mod.failTok(&then_scope.base, payload_token, "pointer modifier invalid on discard", .{});
2407 }
2408
2409 const index_token = if (token_tags[ident + 1] == .comma)
2410 ident + 2
2411 else
2412 break :blk &then_scope.base;
2413 if (mem.eql(u8, tree.tokenSlice(index_token), "_")) {
2414 return mod.failTok(&then_scope.base, index_token, "discard of index capture; omit it instead", .{});
2415 }
2416 const index_name = try mod.identifierTokenString(&then_scope.base, index_token);
2417 index_scope = .{
2418 .parent = &then_scope.base,
2419 .gen_zir = &then_scope,
2420 .name = index_name,
2421 .ptr = index_ptr,
2422 .src = parent_gz.tokSrcLoc(index_token),
2423 };
2424 break :blk &index_scope.base;
2425 };
2426
2427 loop_scope.break_count += 1;
2428 const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
2429
2430 var else_scope: Scope.GenZir = .{
2431 .parent = &cond_scope.base,
2432 .astgen = parent_gz.astgen,
2433 .force_comptime = cond_scope.force_comptime,
2434 .instructions = .{},
2435 };
2436 defer else_scope.instructions.deinit(mod.gpa);
2437
2438 const else_node = for_full.ast.else_expr;
2439 const else_info: struct {
2440 src: ast.Node.Index,
2441 result: zir.Inst.Ref,
2442 } = if (else_node != 0) blk: {
2443 loop_scope.break_count += 1;
2444 const sub_scope = &else_scope.base;
2445 break :blk .{
2446 .src = else_node,
2447 .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
2448 };
2449 } else .{
2450 .src = for_full.ast.then_expr,
2451 .result = .none,
2452 };
2453
2454 if (loop_scope.label) |some| {
2455 if (!some.used) {
2456 return mod.failTok(scope, some.token, "unused for loop label", .{});
2457 }
2458 }
2459 const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
2460 return finishThenElseBlock(
2461 mod,
2462 scope,
2463 rl,
2464 node,
2465 &loop_scope,
2466 &then_scope,
2467 &else_scope,
2468 condbr,
2469 cond,
2470 for_full.ast.then_expr,
2471 else_info.src,
2472 then_result,
2473 else_info.result,
2474 loop_block,
2475 cond_block,
2476 break_tag,
2477 );
2478}
2479
2480fn getRangeNode(
2481 node_tags: []const ast.Node.Tag,
2482 node_datas: []const ast.Node.Data,
2483 start_node: ast.Node.Index,
2484) ?ast.Node.Index {
2485 var node = start_node;
2486 while (true) {
2487 switch (node_tags[node]) {
2488 .switch_range => return node,
2489 .grouped_expression => node = node_datas[node].lhs,
2490 else => return null,
2491 }
2492 }
2493}
2494
2495fn switchExpr(
2496 mod: *Module,
2497 scope: *Scope,
2498 rl: ResultLoc,
2499 switch_node: ast.Node.Index,
2500) InnerError!zir.Inst.Ref {
2501 if (true) @panic("TODO update for zir-memory-layout");
2502 const parent_gz = scope.getGenZir();
2503 const tree = parent_gz.tree();
2504 const node_datas = tree.nodes.items(.data);
2505 const main_tokens = tree.nodes.items(.main_token);
2506 const token_tags = tree.tokens.items(.tag);
2507 const node_tags = tree.nodes.items(.tag);
2508
2509 const switch_token = main_tokens[switch_node];
2510 const target_node = node_datas[switch_node].lhs;
2511 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
2512 const case_nodes = tree.extra_data[extra.start..extra.end];
2513
2514 const switch_src = token_starts[switch_token];
2515
2516 var block_scope: Scope.GenZir = .{
2517 .parent = scope,
2518 .decl = scope.ownerDecl().?,
2519 .arena = scope.arena(),
2520 .force_comptime = parent_gz.force_comptime,
2521 .instructions = .{},
2522 };
2523 setBlockResultLoc(&block_scope, rl);
2524 defer block_scope.instructions.deinit(mod.gpa);
2525
2526 var items = std.ArrayList(zir.Inst.Ref).init(mod.gpa);
2527 defer items.deinit();
2528
2529 // First we gather all the switch items and check else/'_' prongs.
2530 var else_src: ?usize = null;
2531 var underscore_src: ?usize = null;
2532 var first_range: ?*zir.Inst = null;
2533 var simple_case_count: usize = 0;
2534 var any_payload_is_ref = false;
2535 for (case_nodes) |case_node| {
2536 const case = switch (node_tags[case_node]) {
2537 .switch_case_one => tree.switchCaseOne(case_node),
2538 .switch_case => tree.switchCase(case_node),
2539 else => unreachable,
2540 };
2541 if (case.payload_token) |payload_token| {
2542 if (token_tags[payload_token] == .asterisk) {
2543 any_payload_is_ref = true;
2544 }
2545 }
2546 // Check for else/_ prong, those are handled last.
2547 if (case.ast.values.len == 0) {
2548 const case_src = token_starts[case.ast.arrow_token - 1];
2549 if (else_src) |src| {
2550 const msg = msg: {
2551 const msg = try mod.errMsg(
2552 scope,
2553 case_src,
2554 "multiple else prongs in switch expression",
2555 .{},
2556 );
2557 errdefer msg.destroy(mod.gpa);
2558 try mod.errNote(scope, src, msg, "previous else prong is here", .{});
2559 break :msg msg;
2560 };
2561 return mod.failWithOwnedErrorMsg(scope, msg);
2562 }
2563 else_src = case_src;
2564 continue;
2565 } else if (case.ast.values.len == 1 and
2566 node_tags[case.ast.values[0]] == .identifier and
2567 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2568 {
2569 const case_src = token_starts[case.ast.arrow_token - 1];
2570 if (underscore_src) |src| {
2571 const msg = msg: {
2572 const msg = try mod.errMsg(
2573 scope,
2574 case_src,
2575 "multiple '_' prongs in switch expression",
2576 .{},
2577 );
2578 errdefer msg.destroy(mod.gpa);
2579 try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
2580 break :msg msg;
2581 };
2582 return mod.failWithOwnedErrorMsg(scope, msg);
2583 }
2584 underscore_src = case_src;
2585 continue;
2586 }
2587
2588 if (else_src) |some_else| {
2589 if (underscore_src) |some_underscore| {
2590 const msg = msg: {
2591 const msg = try mod.errMsg(
2592 scope,
2593 switch_src,
2594 "else and '_' prong in switch expression",
2595 .{},
2596 );
2597 errdefer msg.destroy(mod.gpa);
2598 try mod.errNote(scope, some_else, msg, "else prong is here", .{});
2599 try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
2600 break :msg msg;
2601 };
2602 return mod.failWithOwnedErrorMsg(scope, msg);
2603 }
2604 }
2605
2606 if (case.ast.values.len == 1 and
2607 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2608 {
2609 simple_case_count += 1;
2610 }
2611
2612 // Generate all the switch items as comptime expressions.
2613 for (case.ast.values) |item| {
2614 if (getRangeNode(node_tags, node_datas, item)) |range| {
2615 const start = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].lhs);
2616 const end = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].rhs);
2617 const range_src = token_starts[main_tokens[range]];
2618 const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end);
2619 try items.append(range_inst);
2620 } else {
2621 const item_inst = try comptimeExpr(mod, &block_scope.base, .none, item);
2622 try items.append(item_inst);
2623 }
2624 }
2625 }
2626
2627 var special_prong: zir.Inst.SwitchBr.SpecialProng = .none;
2628 if (else_src != null) special_prong = .@"else";
2629 if (underscore_src != null) special_prong = .underscore;
2630 var cases = try block_scope.arena.alloc(zir.Inst.SwitchBr.Case, simple_case_count);
2631
2632 const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{
2633 .rl = .ref,
2634 .tag = .switchbr_ref,
2635 } else .{
2636 .rl = .none,
2637 .tag = .switchbr,
2638 };
2639 const target = try expr(mod, &block_scope.base, rl_and_tag.rl, target_node);
2640 const switch_inst = try addZirInstT(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, rl_and_tag.tag, .{
2641 .target = target,
2642 .cases = cases,
2643 .items = try block_scope.arena.dupe(zir.Inst.Ref, items.items),
2644 .else_body = undefined, // populated below
2645 .range = first_range,
2646 .special_prong = special_prong,
2647 });
2648 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
2649 .instructions = try block_scope.arena.dupe(zir.Inst.Ref, block_scope.instructions.items),
2650 });
2651
2652 var case_scope: Scope.GenZir = .{
2653 .parent = scope,
2654 .decl = block_scope.decl,
2655 .arena = block_scope.arena,
2656 .force_comptime = block_scope.force_comptime,
2657 .instructions = .{},
2658 };
2659 defer case_scope.instructions.deinit(mod.gpa);
2660
2661 var else_scope: Scope.GenZir = .{
2662 .parent = scope,
2663 .decl = case_scope.decl,
2664 .arena = case_scope.arena,
2665 .force_comptime = case_scope.force_comptime,
2666 .instructions = .{},
2667 };
2668 defer else_scope.instructions.deinit(mod.gpa);
2669
2670 // Now generate all but the special cases.
2671 var special_case: ?ast.full.SwitchCase = null;
2672 var items_index: usize = 0;
2673 var case_index: usize = 0;
2674 for (case_nodes) |case_node| {
2675 const case = switch (node_tags[case_node]) {
2676 .switch_case_one => tree.switchCaseOne(case_node),
2677 .switch_case => tree.switchCase(case_node),
2678 else => unreachable,
2679 };
2680 const case_src = token_starts[main_tokens[case_node]];
2681 case_scope.instructions.shrinkRetainingCapacity(0);
2682
2683 // Check for else/_ prong, those are handled last.
2684 if (case.ast.values.len == 0) {
2685 special_case = case;
2686 continue;
2687 } else if (case.ast.values.len == 1 and
2688 node_tags[case.ast.values[0]] == .identifier and
2689 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2690 {
2691 special_case = case;
2692 continue;
2693 }
2694
2695 // If this is a simple one item prong then it is handled by the switchbr.
2696 if (case.ast.values.len == 1 and
2697 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2698 {
2699 const item = items.items[items_index];
2700 items_index += 1;
2701 try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target);
2702
2703 cases[case_index] = .{
2704 .item = item,
2705 .body = .{ .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items) },
2706 };
2707 case_index += 1;
2708 continue;
2709 }
2710
2711 // Check if the target matches any of the items.
2712 // 1, 2, 3..6 will result in
2713 // target == 1 or target == 2 or (target >= 3 and target <= 6)
2714 // TODO handle multiple items as switch prongs rather than along with ranges.
2715 var any_ok: ?*zir.Inst = null;
2716 for (case.ast.values) |item| {
2717 if (getRangeNode(node_tags, node_datas, item)) |range| {
2718 const range_src = token_starts[main_tokens[range]];
2719 const range_inst = items.items[items_index].castTag(.switch_range).?;
2720 items_index += 1;
2721
2722 // target >= start and target <= end
2723 const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, range_inst.positionals.lhs);
2724 const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, range_inst.positionals.rhs);
2725 const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_and, range_start_ok, range_end_ok);
2726
2727 if (any_ok) |some| {
2728 any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_or, some, range_ok);
2729 } else {
2730 any_ok = range_ok;
2731 }
2732 continue;
2733 }
2734
2735 const item_inst = items.items[items_index];
2736 items_index += 1;
2737 const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst);
2738
2739 if (any_ok) |some| {
2740 any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .bool_or, some, cpm_ok);
2741 } else {
2742 any_ok = cpm_ok;
2743 }
2744 }
2745
2746 const condbr = try addZIRInstSpecial(mod, &case_scope.base, case_src, zir.Inst.CondBr, .{
2747 .condition = any_ok.?,
2748 .then_body = undefined, // populated below
2749 .else_body = undefined, // populated below
2750 }, .{});
2751 const cond_block = try addZIRInstBlock(mod, &else_scope.base, case_src, .block, .{
2752 .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items),
2753 });
2754
2755 // reset cond_scope for then_body
2756 case_scope.instructions.items.len = 0;
2757 try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target);
2758 condbr.positionals.then_body = .{
2759 .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items),
2760 };
2761
2762 // reset cond_scope for else_body
2763 case_scope.instructions.items.len = 0;
2764 _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{
2765 .block = cond_block,
2766 }, .{});
2767 condbr.positionals.else_body = .{
2768 .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items),
2769 };
2770 }
2771
2772 // Finally generate else block or a break.
2773 if (special_case) |case| {
2774 try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target);
2775 } else {
2776 // Not handling all possible cases is a compile error.
2777 _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe);
2778 }
2779 switch_inst.positionals.else_body = .{
2780 .instructions = try block_scope.arena.dupe(zir.Inst.Ref, else_scope.instructions.items),
2781 };
2782
2783 return &block.base;
2784}
2785
2786fn switchCaseExpr(
2787 mod: *Module,
2788 scope: *Scope,
2789 rl: ResultLoc,
2790 block: *zir.Inst.Block,
2791 case: ast.full.SwitchCase,
2792 target: zir.Inst.Ref,
2793) !void {
2794 const tree = scope.tree();
2795 const node_datas = tree.nodes.items(.data);
2796 const main_tokens = tree.nodes.items(.main_token);
2797 const token_tags = tree.tokens.items(.tag);
2798
2799 const case_src = token_starts[case.ast.arrow_token];
2800 const sub_scope = blk: {
2801 const payload_token = case.payload_token orelse break :blk scope;
2802 const ident = if (token_tags[payload_token] == .asterisk)
2803 payload_token + 1
2804 else
2805 payload_token;
2806 const is_ptr = ident != payload_token;
2807 const value_name = tree.tokenSlice(ident);
2808 if (mem.eql(u8, value_name, "_")) {
2809 if (is_ptr) {
2810 return mod.failTok(scope, payload_token, "pointer modifier invalid on discard", .{});
2811 }
2812 break :blk scope;
2813 }
2814 return mod.failTok(scope, ident, "TODO implement switch value payload", .{});
2815 };
2816
2817 const case_body = try expr(mod, sub_scope, rl, case.ast.target_expr);
2818 if (!case_body.tag.isNoReturn()) {
2819 _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{
2820 .block = block,
2821 .operand = case_body,
2822 }, .{});
2823 }
2824}
2825
2826fn ret(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
2827 const tree = scope.tree();
2828 const node_datas = tree.nodes.items(.data);
2829 const main_tokens = tree.nodes.items(.main_token);
2830
2831 const operand_node = node_datas[node].lhs;
2832 const gz = scope.getGenZir();
2833 const operand: zir.Inst.Ref = if (operand_node != 0) operand: {
2834 const rl: ResultLoc = if (nodeMayNeedMemoryLocation(scope, operand_node)) .{
2835 .ptr = try gz.addNode(.ret_ptr, node),
2836 } else .{
2837 .ty = try gz.addNode(.ret_type, node),
2838 };
2839 break :operand try expr(mod, scope, rl, operand_node);
2840 } else .void_value;
2841 _ = try gz.addUnNode(.ret_node, operand, node);
2842 return zir.Inst.Ref.unreachable_value;
2843}
2844
2845fn identifier(
2846 mod: *Module,
2847 scope: *Scope,
2848 rl: ResultLoc,
2849 ident: ast.Node.Index,
2850) InnerError!zir.Inst.Ref {
2851 const tracy = trace(@src());
2852 defer tracy.end();
2853
2854 const tree = scope.tree();
2855 const main_tokens = tree.nodes.items(.main_token);
2856
2857 const gz = scope.getGenZir();
2858
2859 const ident_token = main_tokens[ident];
2860 const ident_name = try mod.identifierTokenString(scope, ident_token);
2861 if (mem.eql(u8, ident_name, "_")) {
2862 return mod.failNode(scope, ident, "TODO implement '_' identifier", .{});
2863 }
2864
2865 if (simple_types.get(ident_name)) |zir_const_ref| {
2866 return rvalue(mod, scope, rl, zir_const_ref, ident);
2867 }
2868
2869 if (ident_name.len >= 2) integer: {
2870 const first_c = ident_name[0];
2871 if (first_c == 'i' or first_c == 'u') {
2872 const signedness: std.builtin.Signedness = switch (first_c == 'i') {
2873 true => .signed,
2874 false => .unsigned,
2875 };
2876 const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
2877 error.Overflow => return mod.failNode(
2878 scope,
2879 ident,
2880 "primitive integer type '{s}' exceeds maximum bit width of 65535",
2881 .{ident_name},
2882 ),
2883 error.InvalidCharacter => break :integer,
2884 };
2885 const result = try gz.add(.{
2886 .tag = .int_type,
2887 .data = .{ .int_type = .{
2888 .src_node = gz.astgen.decl.nodeIndexToRelative(ident),
2889 .signedness = signedness,
2890 .bit_count = bit_count,
2891 } },
2892 });
2893 return rvalue(mod, scope, rl, result, ident);
2894 }
2895 }
2896
2897 // Local variables, including function parameters.
2898 {
2899 var s = scope;
2900 while (true) switch (s.tag) {
2901 .local_val => {
2902 const local_val = s.cast(Scope.LocalVal).?;
2903 if (mem.eql(u8, local_val.name, ident_name)) {
2904 return rvalue(mod, scope, rl, local_val.inst, ident);
2905 }
2906 s = local_val.parent;
2907 },
2908 .local_ptr => {
2909 const local_ptr = s.cast(Scope.LocalPtr).?;
2910 if (mem.eql(u8, local_ptr.name, ident_name)) {
2911 if (rl == .ref) return local_ptr.ptr;
2912 const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident);
2913 return rvalue(mod, scope, rl, loaded, ident);
2914 }
2915 s = local_ptr.parent;
2916 },
2917 .gen_zir => s = s.cast(Scope.GenZir).?.parent,
2918 else => break,
2919 };
2920 }
2921
2922 const gop = try gz.astgen.decl_map.getOrPut(mod.gpa, ident_name);
2923 if (!gop.found_existing) {
2924 const decl = mod.lookupDeclName(scope, ident_name) orelse
2925 return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name});
2926 try gz.astgen.decls.append(mod.gpa, decl);
2927 }
2928 const decl_index = @intCast(u32, gop.index);
2929 switch (rl) {
2930 .ref => return gz.addDecl(.decl_ref, decl_index, ident),
2931 else => return rvalue(mod, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident),
2932 }
2933}
2934
2935fn stringLiteral(
2936 mod: *Module,
2937 scope: *Scope,
2938 rl: ResultLoc,
2939 node: ast.Node.Index,
2940) InnerError!zir.Inst.Ref {
2941 const tree = scope.tree();
2942 const main_tokens = tree.nodes.items(.main_token);
2943 const gz = scope.getGenZir();
2944 const string_bytes = &gz.astgen.string_bytes;
2945 const str_index = string_bytes.items.len;
2946 const str_lit_token = main_tokens[node];
2947 const token_bytes = tree.tokenSlice(str_lit_token);
2948 try mod.parseStrLit(scope, str_lit_token, string_bytes, token_bytes, 0);
2949 const str_len = string_bytes.items.len - str_index;
2950 const result = try gz.add(.{
2951 .tag = .str,
2952 .data = .{ .str = .{
2953 .start = @intCast(u32, str_index),
2954 .len = @intCast(u32, str_len),
2955 } },
2956 });
2957 return rvalue(mod, scope, rl, result, node);
2958}
2959
2960fn multilineStringLiteral(
2961 mod: *Module,
2962 scope: *Scope,
2963 rl: ResultLoc,
2964 node: ast.Node.Index,
2965) InnerError!zir.Inst.Ref {
2966 const gz = scope.getGenZir();
2967 const tree = gz.tree();
2968 const node_datas = tree.nodes.items(.data);
2969 const main_tokens = tree.nodes.items(.main_token);
2970
2971 const start = node_datas[node].lhs;
2972 const end = node_datas[node].rhs;
2973 const string_bytes = &gz.astgen.string_bytes;
2974 const str_index = string_bytes.items.len;
2975
2976 // First line: do not append a newline.
2977 var tok_i = start;
2978 {
2979 const slice = tree.tokenSlice(tok_i);
2980 const line_bytes = slice[2 .. slice.len - 1];
2981 try string_bytes.appendSlice(mod.gpa, line_bytes);
2982 tok_i += 1;
2983 }
2984 // Following lines: each line prepends a newline.
2985 while (tok_i <= end) : (tok_i += 1) {
2986 const slice = tree.tokenSlice(tok_i);
2987 const line_bytes = slice[2 .. slice.len - 1];
2988 try string_bytes.ensureCapacity(mod.gpa, string_bytes.items.len + line_bytes.len + 1);
2989 string_bytes.appendAssumeCapacity('\n');
2990 string_bytes.appendSliceAssumeCapacity(line_bytes);
2991 }
2992 const result = try gz.add(.{
2993 .tag = .str,
2994 .data = .{ .str = .{
2995 .start = @intCast(u32, str_index),
2996 .len = @intCast(u32, string_bytes.items.len - str_index),
2997 } },
2998 });
2999 return rvalue(mod, scope, rl, result, node);
3000}
3001
3002fn charLiteral(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref {
3003 const gz = scope.getGenZir();
3004 const tree = gz.tree();
3005 const main_tokens = tree.nodes.items(.main_token);
3006 const main_token = main_tokens[node];
3007 const slice = tree.tokenSlice(main_token);
3008
3009 var bad_index: usize = undefined;
3010 const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {
3011 error.InvalidCharacter => {
3012 const bad_byte = slice[bad_index];
3013 const token_starts = tree.tokens.items(.start);
3014 const src_off = @intCast(u32, token_starts[main_token] + bad_index);
3015 return mod.failOff(scope, src_off, "invalid character: '{c}'\n", .{bad_byte});
3016 },
3017 };
3018 const result = try gz.addInt(value);
3019 return rvalue(mod, scope, rl, result, node);
3020}
3021
3022fn integerLiteral(
3023 mod: *Module,
3024 scope: *Scope,
3025 rl: ResultLoc,
3026 node: ast.Node.Index,
3027) InnerError!zir.Inst.Ref {
3028 const tree = scope.tree();
3029 const main_tokens = tree.nodes.items(.main_token);
3030 const int_token = main_tokens[node];
3031 const prefixed_bytes = tree.tokenSlice(int_token);
3032 const gz = scope.getGenZir();
3033 if (std.fmt.parseInt(u64, prefixed_bytes, 0)) |small_int| {
3034 const result: zir.Inst.Ref = switch (small_int) {
3035 0 => .zero,
3036 1 => .one,
3037 else => try gz.addInt(small_int),
3038 };
3039 return rvalue(mod, scope, rl, result, node);
3040 } else |err| {
3041 return mod.failNode(scope, node, "TODO implement int literals that don't fit in a u64", .{});
3042 }
3043}
3044
3045fn floatLiteral(
3046 mod: *Module,
3047 scope: *Scope,
3048 rl: ResultLoc,
3049 node: ast.Node.Index,
3050) InnerError!zir.Inst.Ref {
3051 const arena = scope.arena();
3052 const tree = scope.tree();
3053 const main_tokens = tree.nodes.items(.main_token);
3054 const gz = scope.getGenZir();
3055
3056 const main_token = main_tokens[node];
3057 const bytes = tree.tokenSlice(main_token);
3058 if (bytes.len > 2 and bytes[1] == 'x') {
3059 assert(bytes[0] == '0'); // validated by tokenizer
3060 return mod.failTok(scope, main_token, "TODO implement hex floats", .{});
3061 }
3062 const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
3063 error.InvalidCharacter => unreachable, // validated by tokenizer
3064 };
3065 const typed_value = try arena.create(TypedValue);
3066 typed_value.* = .{
3067 .ty = Type.initTag(.comptime_float),
3068 .val = try Value.Tag.float_128.create(arena, float_number),
3069 };
3070 const result = try gz.add(.{
3071 .tag = .@"const",
3072 .data = .{ .@"const" = typed_value },
3073 });
3074 return rvalue(mod, scope, rl, result, node);
3075}
3076
3077fn asmExpr(
3078 mod: *Module,
3079 scope: *Scope,
3080 rl: ResultLoc,
3081 node: ast.Node.Index,
3082 full: ast.full.Asm,
3083) InnerError!zir.Inst.Ref {
3084 const arena = scope.arena();
3085 const tree = scope.tree();
3086 const main_tokens = tree.nodes.items(.main_token);
3087 const node_datas = tree.nodes.items(.data);
3088 const gz = scope.getGenZir();
3089
3090 const asm_source = try expr(mod, scope, .{ .ty = .const_slice_u8_type }, full.ast.template);
3091
3092 if (full.outputs.len != 0) {
3093 return mod.failTok(scope, full.ast.asm_token, "TODO implement asm with an output", .{});
3094 }
3095
3096 const constraints = try arena.alloc(u32, full.inputs.len);
3097 const args = try arena.alloc(zir.Inst.Ref, full.inputs.len);
3098
3099 for (full.inputs) |input, i| {
3100 const constraint_token = main_tokens[input] + 2;
3101 const string_bytes = &gz.astgen.string_bytes;
3102 constraints[i] = @intCast(u32, string_bytes.items.len);
3103 const token_bytes = tree.tokenSlice(constraint_token);
3104 try mod.parseStrLit(scope, constraint_token, string_bytes, token_bytes, 0);
3105 try string_bytes.append(mod.gpa, 0);
3106
3107 args[i] = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[input].lhs);
3108 }
3109
3110 const tag: zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm";
3111 const result = try gz.addPlNode(tag, node, zir.Inst.Asm{
3112 .asm_source = asm_source,
3113 .return_type = .void_type,
3114 .output = .none,
3115 .args_len = @intCast(u32, full.inputs.len),
3116 .clobbers_len = 0, // TODO implement asm clobbers
3117 });
3118
3119 try gz.astgen.extra.ensureCapacity(mod.gpa, gz.astgen.extra.items.len +
3120 args.len + constraints.len);
3121 gz.astgen.appendRefsAssumeCapacity(args);
3122 gz.astgen.extra.appendSliceAssumeCapacity(constraints);
3123
3124 return rvalue(mod, scope, rl, result, node);
3125}
3126
3127fn as(
3128 mod: *Module,
3129 scope: *Scope,
3130 rl: ResultLoc,
3131 builtin_token: ast.TokenIndex,
3132 node: ast.Node.Index,
3133 lhs: ast.Node.Index,
3134 rhs: ast.Node.Index,
3135) InnerError!zir.Inst.Ref {
3136 const dest_type = try typeExpr(mod, scope, lhs);
3137 switch (rl) {
3138 .none, .discard, .ref, .ty => {
3139 const result = try expr(mod, scope, .{ .ty = dest_type }, rhs);
3140 return rvalue(mod, scope, rl, result, node);
3141 },
3142
3143 .ptr => |result_ptr| {
3144 return asRlPtr(mod, scope, rl, result_ptr, rhs, dest_type);
3145 },
3146 .block_ptr => |block_scope| {
3147 return asRlPtr(mod, scope, rl, block_scope.rl_ptr, rhs, dest_type);
3148 },
3149
3150 .bitcasted_ptr => |bitcasted_ptr| {
3151 // TODO here we should be able to resolve the inference; we now have a type for the result.
3152 return mod.failTok(scope, builtin_token, "TODO implement @as with result location @bitCast", .{});
3153 },
3154 .inferred_ptr => |result_alloc| {
3155 // TODO here we should be able to resolve the inference; we now have a type for the result.
3156 return mod.failTok(scope, builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
3157 },
3158 }
3159}
3160
3161fn asRlPtr(
3162 mod: *Module,
3163 scope: *Scope,
3164 rl: ResultLoc,
3165 result_ptr: zir.Inst.Ref,
3166 operand_node: ast.Node.Index,
3167 dest_type: zir.Inst.Ref,
3168) InnerError!zir.Inst.Ref {
3169 // Detect whether this expr() call goes into rvalue() to store the result into the
3170 // result location. If it does, elide the coerce_result_ptr instruction
3171 // as well as the store instruction, instead passing the result as an rvalue.
3172 const parent_gz = scope.getGenZir();
3173 const astgen = parent_gz.astgen;
3174
3175 var as_scope: Scope.GenZir = .{
3176 .parent = scope,
3177 .astgen = astgen,
3178 .force_comptime = parent_gz.force_comptime,
3179 .instructions = .{},
3180 };
3181 defer as_scope.instructions.deinit(mod.gpa);
3182
3183 as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
3184 const result = try expr(mod, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node);
3185 const parent_zir = &parent_gz.instructions;
3186 if (as_scope.rvalue_rl_count == 1) {
3187 // Busted! This expression didn't actually need a pointer.
3188 const zir_tags = astgen.instructions.items(.tag);
3189 const zir_datas = astgen.instructions.items(.data);
3190 const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2;
3191 try parent_zir.ensureCapacity(mod.gpa, expected_len);
3192 for (as_scope.instructions.items) |src_inst| {
3193 if (astgen.indexToRef(src_inst) == as_scope.rl_ptr) continue;
3194 if (zir_tags[src_inst] == .store_to_block_ptr) {
3195 if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
3196 }
3197 parent_zir.appendAssumeCapacity(src_inst);
3198 }
3199 assert(parent_zir.items.len == expected_len);
3200 const casted_result = try parent_gz.addBin(.as, dest_type, result);
3201 return rvalue(mod, scope, rl, casted_result, operand_node);
3202 } else {
3203 try parent_zir.appendSlice(mod.gpa, as_scope.instructions.items);
3204 return result;
3205 }
3206}
3207
3208fn bitCast(
3209 mod: *Module,
3210 scope: *Scope,
3211 rl: ResultLoc,
3212 builtin_token: ast.TokenIndex,
3213 node: ast.Node.Index,
3214 lhs: ast.Node.Index,
3215 rhs: ast.Node.Index,
3216) InnerError!zir.Inst.Ref {
3217 if (true) @panic("TODO update for zir-memory-layout");
3218 const dest_type = try typeExpr(mod, scope, lhs);
3219 switch (rl) {
3220 .none => {
3221 const operand = try expr(mod, scope, .none, rhs);
3222 return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
3223 },
3224 .discard => {
3225 const operand = try expr(mod, scope, .none, rhs);
3226 const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
3227 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
3228 return result;
3229 },
3230 .ref => {
3231 const operand = try expr(mod, scope, .ref, rhs);
3232 const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
3233 return result;
3234 },
3235 .ty => |result_ty| {
3236 const result = try expr(mod, scope, .none, rhs);
3237 const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
3238 return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
3239 },
3240 .ptr => |result_ptr| {
3241 const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
3242 return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, rhs);
3243 },
3244 .bitcasted_ptr => |bitcasted_ptr| {
3245 return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
3246 },
3247 .block_ptr => |block_ptr| {
3248 return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
3249 },
3250 .inferred_ptr => |result_alloc| {
3251 // TODO here we should be able to resolve the inference; we now have a type for the result.
3252 return mod.failTok(scope, builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
3253 },
3254 }
3255}
3256
3257fn typeOf(
3258 mod: *Module,
3259 scope: *Scope,
3260 rl: ResultLoc,
3261 builtin_token: ast.TokenIndex,
3262 node: ast.Node.Index,
3263 params: []const ast.Node.Index,
3264) InnerError!zir.Inst.Ref {
3265 if (params.len < 1) {
3266 return mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{});
3267 }
3268 const gz = scope.getGenZir();
3269 if (params.len == 1) {
3270 return rvalue(
3271 mod,
3272 scope,
3273 rl,
3274 try gz.addUnTok(.typeof, try expr(mod, scope, .none, params[0]), node),
3275 node,
3276 );
3277 }
3278 const arena = scope.arena();
3279 var items = try arena.alloc(zir.Inst.Ref, params.len);
3280 for (params) |param, param_i| {
3281 items[param_i] = try expr(mod, scope, .none, param);
3282 }
3283
3284 const result = try gz.addPlNode(.typeof_peer, node, zir.Inst.MultiOp{
3285 .operands_len = @intCast(u32, params.len),
3286 });
3287 try gz.astgen.appendRefs(items);
3288
3289 return rvalue(mod, scope, rl, result, node);
3290}
3291
3292fn builtinCall(
3293 mod: *Module,
3294 scope: *Scope,
3295 rl: ResultLoc,
3296 node: ast.Node.Index,
3297 params: []const ast.Node.Index,
3298) InnerError!zir.Inst.Ref {
3299 const tree = scope.tree();
3300 const main_tokens = tree.nodes.items(.main_token);
3301
3302 const builtin_token = main_tokens[node];
3303 const builtin_name = tree.tokenSlice(builtin_token);
3304
3305 // We handle the different builtins manually because they have different semantics depending
3306 // on the function. For example, `@as` and others participate in result location semantics,
3307 // and `@cImport` creates a special scope that collects a .c source code text buffer.
3308 // Also, some builtins have a variable number of parameters.
3309
3310 const info = BuiltinFn.list.get(builtin_name) orelse {
3311 return mod.failTok(scope, builtin_token, "invalid builtin function: '{s}'", .{
3312 builtin_name,
3313 });
3314 };
3315 if (info.param_count) |expected| {
3316 if (expected != params.len) {
3317 const s = if (expected == 1) "" else "s";
3318 return mod.failTok(scope, builtin_token, "expected {d} parameter{s}, found {d}", .{
3319 expected, s, params.len,
3320 });
3321 }
3322 }
3323
3324 const gz = scope.getGenZir();
3325
3326 switch (info.tag) {
3327 .ptr_to_int => {
3328 const operand = try expr(mod, scope, .none, params[0]);
3329 const result = try gz.addUnNode(.ptrtoint, operand, node);
3330 return rvalue(mod, scope, rl, result, node);
3331 },
3332 .float_cast => {
3333 const dest_type = try typeExpr(mod, scope, params[0]);
3334 const rhs = try expr(mod, scope, .none, params[1]);
3335 const result = try gz.addPlNode(.floatcast, node, zir.Inst.Bin{
3336 .lhs = dest_type,
3337 .rhs = rhs,
3338 });
3339 return rvalue(mod, scope, rl, result, node);
3340 },
3341 .int_cast => {
3342 const dest_type = try typeExpr(mod, scope, params[0]);
3343 const rhs = try expr(mod, scope, .none, params[1]);
3344 const result = try gz.addPlNode(.intcast, node, zir.Inst.Bin{
3345 .lhs = dest_type,
3346 .rhs = rhs,
3347 });
3348 return rvalue(mod, scope, rl, result, node);
3349 },
3350 .breakpoint => {
3351 const result = try gz.add(.{
3352 .tag = .breakpoint,
3353 .data = .{ .node = gz.astgen.decl.nodeIndexToRelative(node) },
3354 });
3355 return rvalue(mod, scope, rl, result, node);
3356 },
3357 .import => {
3358 const target = try expr(mod, scope, .none, params[0]);
3359 const result = try gz.addUnNode(.import, target, node);
3360 return rvalue(mod, scope, rl, result, node);
3361 },
3362 .compile_error => {
3363 const target = try expr(mod, scope, .none, params[0]);
3364 const result = try gz.addUnNode(.compile_error, target, node);
3365 return rvalue(mod, scope, rl, result, node);
3366 },
3367 .set_eval_branch_quota => {
3368 const quota = try expr(mod, scope, .{ .ty = .u32_type }, params[0]);
3369 const result = try gz.addUnNode(.set_eval_branch_quota, quota, node);
3370 return rvalue(mod, scope, rl, result, node);
3371 },
3372 .compile_log => {
3373 const arg_refs = try mod.gpa.alloc(zir.Inst.Ref, params.len);
3374 defer mod.gpa.free(arg_refs);
3375
3376 for (params) |param, i| arg_refs[i] = try expr(mod, scope, .none, param);
3377
3378 const result = try gz.addPlNode(.compile_log, node, zir.Inst.MultiOp{
3379 .operands_len = @intCast(u32, params.len),
3380 });
3381 try gz.astgen.appendRefs(arg_refs);
3382 return rvalue(mod, scope, rl, result, node);
3383 },
3384 .field => {
3385 const field_name = try comptimeExpr(mod, scope, .{ .ty = .const_slice_u8_type }, params[1]);
3386 if (rl == .ref) {
3387 return try gz.addPlNode(.field_ptr_named, node, zir.Inst.FieldNamed{
3388 .lhs = try expr(mod, scope, .ref, params[0]),
3389 .field_name = field_name,
3390 });
3391 }
3392 const result = try gz.addPlNode(.field_val_named, node, zir.Inst.FieldNamed{
3393 .lhs = try expr(mod, scope, .none, params[0]),
3394 .field_name = field_name,
3395 });
3396 return rvalue(mod, scope, rl, result, node);
3397 },
3398 .as => return as(mod, scope, rl, builtin_token, node, params[0], params[1]),
3399 .bit_cast => return bitCast(mod, scope, rl, builtin_token, node, params[0], params[1]),
3400 .TypeOf => return typeOf(mod, scope, rl, builtin_token, node, params),
3401
3402 .add_with_overflow,
3403 .align_cast,
3404 .align_of,
3405 .atomic_load,
3406 .atomic_rmw,
3407 .atomic_store,
3408 .bit_offset_of,
3409 .bool_to_int,
3410 .bit_size_of,
3411 .mul_add,
3412 .byte_swap,
3413 .bit_reverse,
3414 .byte_offset_of,
3415 .call,
3416 .c_define,
3417 .c_import,
3418 .c_include,
3419 .clz,
3420 .cmpxchg_strong,
3421 .cmpxchg_weak,
3422 .ctz,
3423 .c_undef,
3424 .div_exact,
3425 .div_floor,
3426 .div_trunc,
3427 .embed_file,
3428 .enum_to_int,
3429 .error_name,
3430 .error_return_trace,
3431 .error_to_int,
3432 .err_set_cast,
3433 .@"export",
3434 .fence,
3435 .field_parent_ptr,
3436 .float_to_int,
3437 .has_decl,
3438 .has_field,
3439 .int_to_enum,
3440 .int_to_error,
3441 .int_to_float,
3442 .int_to_ptr,
3443 .memcpy,
3444 .memset,
3445 .wasm_memory_size,
3446 .wasm_memory_grow,
3447 .mod,
3448 .mul_with_overflow,
3449 .panic,
3450 .pop_count,
3451 .ptr_cast,
3452 .rem,
3453 .return_address,
3454 .set_align_stack,
3455 .set_cold,
3456 .set_float_mode,
3457 .set_runtime_safety,
3458 .shl_exact,
3459 .shl_with_overflow,
3460 .shr_exact,
3461 .shuffle,
3462 .size_of,
3463 .splat,
3464 .reduce,
3465 .src,
3466 .sqrt,
3467 .sin,
3468 .cos,
3469 .exp,
3470 .exp2,
3471 .log,
3472 .log2,
3473 .log10,
3474 .fabs,
3475 .floor,
3476 .ceil,
3477 .trunc,
3478 .round,
3479 .sub_with_overflow,
3480 .tag_name,
3481 .This,
3482 .truncate,
3483 .Type,
3484 .type_info,
3485 .type_name,
3486 .union_init,
3487 => return mod.failTok(scope, builtin_token, "TODO: implement builtin function {s}", .{
3488 builtin_name,
3489 }),
3490
3491 .async_call,
3492 .frame,
3493 .Frame,
3494 .frame_address,
3495 .frame_size,
3496 => return mod.failTok(scope, builtin_token, "async and related features are not yet supported", .{}),
3497 }
3498}
3499
3500fn callExpr(
3501 mod: *Module,
3502 scope: *Scope,
3503 rl: ResultLoc,
3504 node: ast.Node.Index,
3505 call: ast.full.Call,
3506) InnerError!zir.Inst.Ref {
3507 if (call.async_token) |async_token| {
3508 return mod.failTok(scope, async_token, "async and related features are not yet supported", .{});
3509 }
3510 const lhs = try expr(mod, scope, .none, call.ast.fn_expr);
3511
3512 const args = try mod.gpa.alloc(zir.Inst.Ref, call.ast.params.len);
3513 defer mod.gpa.free(args);
3514
3515 const gz = scope.getGenZir();
3516 for (call.ast.params) |param_node, i| {
3517 const param_type = try gz.add(.{
3518 .tag = .param_type,
3519 .data = .{ .param_type = .{
3520 .callee = lhs,
3521 .param_index = @intCast(u32, i),
3522 } },
3523 });
3524 args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);
3525 }
3526
3527 const modifier: std.builtin.CallOptions.Modifier = switch (call.async_token != null) {
3528 true => .async_kw,
3529 false => .auto,
3530 };
3531 const result: zir.Inst.Ref = res: {
3532 const tag: zir.Inst.Tag = switch (modifier) {
3533 .auto => switch (args.len == 0) {
3534 true => break :res try gz.addUnNode(.call_none, lhs, node),
3535 false => .call,
3536 },
3537 .async_kw => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
3538 .never_tail => unreachable,
3539 .never_inline => unreachable,
3540 .no_async => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
3541 .always_tail => unreachable,
3542 .always_inline => unreachable,
3543 .compile_time => .call_compile_time,
3544 };
3545 break :res try gz.addCall(tag, lhs, args, node);
3546 };
3547 return rvalue(mod, scope, rl, result, node); // TODO function call with result location
3548}
3549
3550pub const simple_types = std.ComptimeStringMap(zir.Inst.Ref, .{
3551 .{ "u8", .u8_type },
3552 .{ "i8", .i8_type },
3553 .{ "u16", .u16_type },
3554 .{ "i16", .i16_type },
3555 .{ "u32", .u32_type },
3556 .{ "i32", .i32_type },
3557 .{ "u64", .u64_type },
3558 .{ "i64", .i64_type },
3559 .{ "usize", .usize_type },
3560 .{ "isize", .isize_type },
3561 .{ "c_short", .c_short_type },
3562 .{ "c_ushort", .c_ushort_type },
3563 .{ "c_int", .c_int_type },
3564 .{ "c_uint", .c_uint_type },
3565 .{ "c_long", .c_long_type },
3566 .{ "c_ulong", .c_ulong_type },
3567 .{ "c_longlong", .c_longlong_type },
3568 .{ "c_ulonglong", .c_ulonglong_type },
3569 .{ "c_longdouble", .c_longdouble_type },
3570 .{ "f16", .f16_type },
3571 .{ "f32", .f32_type },
3572 .{ "f64", .f64_type },
3573 .{ "f128", .f128_type },
3574 .{ "c_void", .c_void_type },
3575 .{ "bool", .bool_type },
3576 .{ "void", .void_type },
3577 .{ "type", .type_type },
3578 .{ "anyerror", .anyerror_type },
3579 .{ "comptime_int", .comptime_int_type },
3580 .{ "comptime_float", .comptime_float_type },
3581 .{ "noreturn", .noreturn_type },
3582 .{ "null", .null_type },
3583 .{ "undefined", .undefined_type },
3584 .{ "undefined", .undef },
3585 .{ "null", .null_value },
3586 .{ "true", .bool_true },
3587 .{ "false", .bool_false },
3588});
3589
3590fn nodeMayNeedMemoryLocation(scope: *Scope, start_node: ast.Node.Index) bool {
3591 const tree = scope.tree();
3592 const node_tags = tree.nodes.items(.tag);
3593 const node_datas = tree.nodes.items(.data);
3594 const main_tokens = tree.nodes.items(.main_token);
3595 const token_tags = tree.tokens.items(.tag);
3596
3597 var node = start_node;
3598 while (true) {
3599 switch (node_tags[node]) {
3600 .root,
3601 .@"usingnamespace",
3602 .test_decl,
3603 .switch_case,
3604 .switch_case_one,
3605 .container_field_init,
3606 .container_field_align,
3607 .container_field,
3608 .asm_output,
3609 .asm_input,
3610 => unreachable,
3611
3612 .@"return",
3613 .@"break",
3614 .@"continue",
3615 .bit_not,
3616 .bool_not,
3617 .global_var_decl,
3618 .local_var_decl,
3619 .simple_var_decl,
3620 .aligned_var_decl,
3621 .@"defer",
3622 .@"errdefer",
3623 .address_of,
3624 .optional_type,
3625 .negation,
3626 .negation_wrap,
3627 .@"resume",
3628 .array_type,
3629 .array_type_sentinel,
3630 .ptr_type_aligned,
3631 .ptr_type_sentinel,
3632 .ptr_type,
3633 .ptr_type_bit_range,
3634 .@"suspend",
3635 .@"anytype",
3636 .fn_proto_simple,
3637 .fn_proto_multi,
3638 .fn_proto_one,
3639 .fn_proto,
3640 .fn_decl,
3641 .anyframe_type,
3642 .anyframe_literal,
3643 .integer_literal,
3644 .float_literal,
3645 .enum_literal,
3646 .string_literal,
3647 .multiline_string_literal,
3648 .char_literal,
3649 .true_literal,
3650 .false_literal,
3651 .null_literal,
3652 .undefined_literal,
3653 .unreachable_literal,
3654 .identifier,
3655 .error_set_decl,
3656 .container_decl,
3657 .container_decl_trailing,
3658 .container_decl_two,
3659 .container_decl_two_trailing,
3660 .container_decl_arg,
3661 .container_decl_arg_trailing,
3662 .tagged_union,
3663 .tagged_union_trailing,
3664 .tagged_union_two,
3665 .tagged_union_two_trailing,
3666 .tagged_union_enum_tag,
3667 .tagged_union_enum_tag_trailing,
3668 .@"asm",
3669 .asm_simple,
3670 .add,
3671 .add_wrap,
3672 .array_cat,
3673 .array_mult,
3674 .assign,
3675 .assign_bit_and,
3676 .assign_bit_or,
3677 .assign_bit_shift_left,
3678 .assign_bit_shift_right,
3679 .assign_bit_xor,
3680 .assign_div,
3681 .assign_sub,
3682 .assign_sub_wrap,
3683 .assign_mod,
3684 .assign_add,
3685 .assign_add_wrap,
3686 .assign_mul,
3687 .assign_mul_wrap,
3688 .bang_equal,
3689 .bit_and,
3690 .bit_or,
3691 .bit_shift_left,
3692 .bit_shift_right,
3693 .bit_xor,
3694 .bool_and,
3695 .bool_or,
3696 .div,
3697 .equal_equal,
3698 .error_union,
3699 .greater_or_equal,
3700 .greater_than,
3701 .less_or_equal,
3702 .less_than,
3703 .merge_error_sets,
3704 .mod,
3705 .mul,
3706 .mul_wrap,
3707 .switch_range,
3708 .field_access,
3709 .sub,
3710 .sub_wrap,
3711 .slice,
3712 .slice_open,
3713 .slice_sentinel,
3714 .deref,
3715 .array_access,
3716 .error_value,
3717 .while_simple, // This variant cannot have an else expression.
3718 .while_cont, // This variant cannot have an else expression.
3719 .for_simple, // This variant cannot have an else expression.
3720 .if_simple, // This variant cannot have an else expression.
3721 => return false,
3722
3723 // Forward the question to the LHS sub-expression.
3724 .grouped_expression,
3725 .@"try",
3726 .@"await",
3727 .@"comptime",
3728 .@"nosuspend",
3729 .unwrap_optional,
3730 => node = node_datas[node].lhs,
3731
3732 // Forward the question to the RHS sub-expression.
3733 .@"catch",
3734 .@"orelse",
3735 => node = node_datas[node].rhs,
3736
3737 // True because these are exactly the expressions we need memory locations for.
3738 .array_init_one,
3739 .array_init_one_comma,
3740 .array_init_dot_two,
3741 .array_init_dot_two_comma,
3742 .array_init_dot,
3743 .array_init_dot_comma,
3744 .array_init,
3745 .array_init_comma,
3746 .struct_init_one,
3747 .struct_init_one_comma,
3748 .struct_init_dot_two,
3749 .struct_init_dot_two_comma,
3750 .struct_init_dot,
3751 .struct_init_dot_comma,
3752 .struct_init,
3753 .struct_init_comma,
3754 => return true,
3755
3756 // True because depending on comptime conditions, sub-expressions
3757 // may be the kind that need memory locations.
3758 .@"while", // This variant always has an else expression.
3759 .@"if", // This variant always has an else expression.
3760 .@"for", // This variant always has an else expression.
3761 .@"switch",
3762 .switch_comma,
3763 .call_one,
3764 .call_one_comma,
3765 .async_call_one,
3766 .async_call_one_comma,
3767 .call,
3768 .call_comma,
3769 .async_call,
3770 .async_call_comma,
3771 => return true,
3772
3773 .block_two,
3774 .block_two_semicolon,
3775 .block,
3776 .block_semicolon,
3777 => {
3778 const lbrace = main_tokens[node];
3779 if (token_tags[lbrace - 1] == .colon) {
3780 // Labeled blocks may need a memory location to forward
3781 // to their break statements.
3782 return true;
3783 } else {
3784 return false;
3785 }
3786 },
3787
3788 .builtin_call,
3789 .builtin_call_comma,
3790 .builtin_call_two,
3791 .builtin_call_two_comma,
3792 => {
3793 const builtin_token = main_tokens[node];
3794 const builtin_name = tree.tokenSlice(builtin_token);
3795 // If the builtin is an invalid name, we don't cause an error here; instead
3796 // let it pass, and the error will be "invalid builtin function" later.
3797 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
3798 return builtin_info.needs_mem_loc;
3799 },
3800 }
3801 }
3802}
3803
3804/// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of
3805/// result locations must call this function on their result.
3806/// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
3807/// If the `ResultLoc` is `ty`, it will coerce the result to the type.
3808fn rvalue(
3809 mod: *Module,
3810 scope: *Scope,
3811 rl: ResultLoc,
3812 result: zir.Inst.Ref,
3813 src_node: ast.Node.Index,
3814) InnerError!zir.Inst.Ref {
3815 const gz = scope.getGenZir();
3816 switch (rl) {
3817 .none => return result,
3818 .discard => {
3819 // Emit a compile error for discarding error values.
3820 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
3821 return result;
3822 },
3823 .ref => {
3824 // We need a pointer but we have a value.
3825 const tree = scope.tree();
3826 const src_token = tree.firstToken(src_node);
3827 return gz.addUnTok(.ref, result, src_token);
3828 },
3829 .ty => |ty_inst| {
3830 // Quickly eliminate some common, unnecessary type coercion.
3831 const as_ty = @as(u64, @enumToInt(zir.Inst.Ref.type_type)) << 32;
3832 const as_comptime_int = @as(u64, @enumToInt(zir.Inst.Ref.comptime_int_type)) << 32;
3833 const as_bool = @as(u64, @enumToInt(zir.Inst.Ref.bool_type)) << 32;
3834 const as_usize = @as(u64, @enumToInt(zir.Inst.Ref.usize_type)) << 32;
3835 const as_void = @as(u64, @enumToInt(zir.Inst.Ref.void_type)) << 32;
3836 switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) {
3837 as_ty | @enumToInt(zir.Inst.Ref.u8_type),
3838 as_ty | @enumToInt(zir.Inst.Ref.i8_type),
3839 as_ty | @enumToInt(zir.Inst.Ref.u16_type),
3840 as_ty | @enumToInt(zir.Inst.Ref.i16_type),
3841 as_ty | @enumToInt(zir.Inst.Ref.u32_type),
3842 as_ty | @enumToInt(zir.Inst.Ref.i32_type),
3843 as_ty | @enumToInt(zir.Inst.Ref.u64_type),
3844 as_ty | @enumToInt(zir.Inst.Ref.i64_type),
3845 as_ty | @enumToInt(zir.Inst.Ref.usize_type),
3846 as_ty | @enumToInt(zir.Inst.Ref.isize_type),
3847 as_ty | @enumToInt(zir.Inst.Ref.c_short_type),
3848 as_ty | @enumToInt(zir.Inst.Ref.c_ushort_type),
3849 as_ty | @enumToInt(zir.Inst.Ref.c_int_type),
3850 as_ty | @enumToInt(zir.Inst.Ref.c_uint_type),
3851 as_ty | @enumToInt(zir.Inst.Ref.c_long_type),
3852 as_ty | @enumToInt(zir.Inst.Ref.c_ulong_type),
3853 as_ty | @enumToInt(zir.Inst.Ref.c_longlong_type),
3854 as_ty | @enumToInt(zir.Inst.Ref.c_ulonglong_type),
3855 as_ty | @enumToInt(zir.Inst.Ref.c_longdouble_type),
3856 as_ty | @enumToInt(zir.Inst.Ref.f16_type),
3857 as_ty | @enumToInt(zir.Inst.Ref.f32_type),
3858 as_ty | @enumToInt(zir.Inst.Ref.f64_type),
3859 as_ty | @enumToInt(zir.Inst.Ref.f128_type),
3860 as_ty | @enumToInt(zir.Inst.Ref.c_void_type),
3861 as_ty | @enumToInt(zir.Inst.Ref.bool_type),
3862 as_ty | @enumToInt(zir.Inst.Ref.void_type),
3863 as_ty | @enumToInt(zir.Inst.Ref.type_type),
3864 as_ty | @enumToInt(zir.Inst.Ref.anyerror_type),
3865 as_ty | @enumToInt(zir.Inst.Ref.comptime_int_type),
3866 as_ty | @enumToInt(zir.Inst.Ref.comptime_float_type),
3867 as_ty | @enumToInt(zir.Inst.Ref.noreturn_type),
3868 as_ty | @enumToInt(zir.Inst.Ref.null_type),
3869 as_ty | @enumToInt(zir.Inst.Ref.undefined_type),
3870 as_ty | @enumToInt(zir.Inst.Ref.fn_noreturn_no_args_type),
3871 as_ty | @enumToInt(zir.Inst.Ref.fn_void_no_args_type),
3872 as_ty | @enumToInt(zir.Inst.Ref.fn_naked_noreturn_no_args_type),
3873 as_ty | @enumToInt(zir.Inst.Ref.fn_ccc_void_no_args_type),
3874 as_ty | @enumToInt(zir.Inst.Ref.single_const_pointer_to_comptime_int_type),
3875 as_ty | @enumToInt(zir.Inst.Ref.const_slice_u8_type),
3876 as_ty | @enumToInt(zir.Inst.Ref.enum_literal_type),
3877 as_comptime_int | @enumToInt(zir.Inst.Ref.zero),
3878 as_comptime_int | @enumToInt(zir.Inst.Ref.one),
3879 as_bool | @enumToInt(zir.Inst.Ref.bool_true),
3880 as_bool | @enumToInt(zir.Inst.Ref.bool_false),
3881 as_usize | @enumToInt(zir.Inst.Ref.zero_usize),
3882 as_usize | @enumToInt(zir.Inst.Ref.one_usize),
3883 as_void | @enumToInt(zir.Inst.Ref.void_value),
3884 => return result, // type of result is already correct
3885
3886 // Need an explicit type coercion instruction.
3887 else => return gz.addPlNode(.as_node, src_node, zir.Inst.As{
3888 .dest_type = ty_inst,
3889 .operand = result,
3890 }),
3891 }
3892 },
3893 .ptr => |ptr_inst| {
3894 _ = try gz.addPlNode(.store_node, src_node, zir.Inst.Bin{
3895 .lhs = ptr_inst,
3896 .rhs = result,
3897 });
3898 return result;
3899 },
3900 .bitcasted_ptr => |bitcasted_ptr| {
3901 return mod.failNode(scope, src_node, "TODO implement rvalue .bitcasted_ptr", .{});
3902 },
3903 .inferred_ptr => |alloc| {
3904 _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
3905 return result;
3906 },
3907 .block_ptr => |block_scope| {
3908 block_scope.rvalue_rl_count += 1;
3909 _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result);
3910 return result;
3911 },
3912 }
3913}
3914
3915fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZir) ResultLoc.Strategy {
3916 var elide_store_to_block_ptr_instructions = false;
3917 switch (rl) {
3918 // In this branch there will not be any store_to_block_ptr instructions.
3919 .discard, .none, .ty, .ref => return .{
3920 .tag = .break_operand,
3921 .elide_store_to_block_ptr_instructions = false,
3922 },
3923 // The pointer got passed through to the sub-expressions, so we will use
3924 // break_void here.
3925 // In this branch there will not be any store_to_block_ptr instructions.
3926 .ptr => return .{
3927 .tag = .break_void,
3928 .elide_store_to_block_ptr_instructions = false,
3929 },
3930 .inferred_ptr, .bitcasted_ptr, .block_ptr => {
3931 if (block_scope.rvalue_rl_count == block_scope.break_count) {
3932 // Neither prong of the if consumed the result location, so we can
3933 // use break instructions to create an rvalue.
3934 return .{
3935 .tag = .break_operand,
3936 .elide_store_to_block_ptr_instructions = true,
3937 };
3938 } else {
3939 // Allow the store_to_block_ptr instructions to remain so that
3940 // semantic analysis can turn them into bitcasts.
3941 return .{
3942 .tag = .break_void,
3943 .elide_store_to_block_ptr_instructions = false,
3944 };
3945 }
3946 },
3947 }
3948}
3949
3950fn setBlockResultLoc(block_scope: *Scope.GenZir, parent_rl: ResultLoc) void {
3951 // Depending on whether the result location is a pointer or value, different
3952 // ZIR needs to be generated. In the former case we rely on storing to the
3953 // pointer to communicate the result, and use breakvoid; in the latter case
3954 // the block break instructions will have the result values.
3955 // One more complication: when the result location is a pointer, we detect
3956 // the scenario where the result location is not consumed. In this case
3957 // we emit ZIR for the block break instructions to have the result values,
3958 // and then rvalue() on that to pass the value to the result location.
3959 switch (parent_rl) {
3960 .discard, .none, .ty, .ptr, .ref => {
3961 block_scope.break_result_loc = parent_rl;
3962 },
3963
3964 .inferred_ptr => |ptr| {
3965 block_scope.rl_ptr = ptr;
3966 block_scope.break_result_loc = .{ .block_ptr = block_scope };
3967 },
3968
3969 .bitcasted_ptr => |ptr| {
3970 block_scope.rl_ptr = ptr;
3971 block_scope.break_result_loc = .{ .block_ptr = block_scope };
3972 },
3973
3974 .block_ptr => |parent_block_scope| {
3975 block_scope.rl_ptr = parent_block_scope.rl_ptr;
3976 block_scope.break_result_loc = .{ .block_ptr = block_scope };
3977 },
3978 }
3979}
src/Module.zig+140-230
......@@ -23,7 +23,7 @@ const link = @import("link.zig");
2323const ir = @import("ir.zig");
2424const zir = @import("zir.zig");
2525const trace = @import("tracy.zig").trace;
26const astgen = @import("astgen.zig");
26const AstGen = @import("AstGen.zig");
2727const Sema = @import("Sema.zig");
2828const target_util = @import("target.zig");
2929
......@@ -407,9 +407,9 @@ pub const Scope = struct {
407407 pub fn arena(scope: *Scope) *Allocator {
408408 switch (scope.tag) {
409409 .block => return scope.cast(Block).?.sema.arena,
410 .gen_zir => return scope.cast(GenZir).?.zir_code.arena,
411 .local_val => return scope.cast(LocalVal).?.gen_zir.zir_code.arena,
412 .local_ptr => return scope.cast(LocalPtr).?.gen_zir.zir_code.arena,
410 .gen_zir => return scope.cast(GenZir).?.astgen.arena,
411 .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.arena,
412 .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.arena,
413413 .file => unreachable,
414414 .container => unreachable,
415415 .decl_ref => unreachable,
......@@ -419,9 +419,9 @@ pub const Scope = struct {
419419 pub fn ownerDecl(scope: *Scope) ?*Decl {
420420 return switch (scope.tag) {
421421 .block => scope.cast(Block).?.sema.owner_decl,
422 .gen_zir => scope.cast(GenZir).?.zir_code.decl,
423 .local_val => scope.cast(LocalVal).?.gen_zir.zir_code.decl,
424 .local_ptr => scope.cast(LocalPtr).?.gen_zir.zir_code.decl,
422 .gen_zir => scope.cast(GenZir).?.astgen.decl,
423 .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl,
424 .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl,
425425 .file => null,
426426 .container => null,
427427 .decl_ref => scope.cast(DeclRef).?.decl,
......@@ -431,9 +431,9 @@ pub const Scope = struct {
431431 pub fn srcDecl(scope: *Scope) ?*Decl {
432432 return switch (scope.tag) {
433433 .block => scope.cast(Block).?.src_decl,
434 .gen_zir => scope.cast(GenZir).?.zir_code.decl,
435 .local_val => scope.cast(LocalVal).?.gen_zir.zir_code.decl,
436 .local_ptr => scope.cast(LocalPtr).?.gen_zir.zir_code.decl,
434 .gen_zir => scope.cast(GenZir).?.astgen.decl,
435 .local_val => scope.cast(LocalVal).?.gen_zir.astgen.decl,
436 .local_ptr => scope.cast(LocalPtr).?.gen_zir.astgen.decl,
437437 .file => null,
438438 .container => null,
439439 .decl_ref => scope.cast(DeclRef).?.decl,
......@@ -444,9 +444,9 @@ pub const Scope = struct {
444444 pub fn namespace(scope: *Scope) *Container {
445445 switch (scope.tag) {
446446 .block => return scope.cast(Block).?.sema.owner_decl.container,
447 .gen_zir => return scope.cast(GenZir).?.zir_code.decl.container,
448 .local_val => return scope.cast(LocalVal).?.gen_zir.zir_code.decl.container,
449 .local_ptr => return scope.cast(LocalPtr).?.gen_zir.zir_code.decl.container,
447 .gen_zir => return scope.cast(GenZir).?.astgen.decl.container,
448 .local_val => return scope.cast(LocalVal).?.gen_zir.astgen.decl.container,
449 .local_ptr => return scope.cast(LocalPtr).?.gen_zir.astgen.decl.container,
450450 .file => return &scope.cast(File).?.root_container,
451451 .container => return scope.cast(Container).?,
452452 .decl_ref => return scope.cast(DeclRef).?.decl.container,
......@@ -474,8 +474,8 @@ pub const Scope = struct {
474474 .file => return &scope.cast(File).?.tree,
475475 .block => return &scope.cast(Block).?.src_decl.container.file_scope.tree,
476476 .gen_zir => return scope.cast(GenZir).?.tree(),
477 .local_val => return &scope.cast(LocalVal).?.gen_zir.zir_code.decl.container.file_scope.tree,
478 .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.zir_code.decl.container.file_scope.tree,
477 .local_val => return &scope.cast(LocalVal).?.gen_zir.astgen.decl.container.file_scope.tree,
478 .local_ptr => return &scope.cast(LocalPtr).?.gen_zir.astgen.decl.container.file_scope.tree,
479479 .container => return &scope.cast(Container).?.file_scope.tree,
480480 .decl_ref => return &scope.cast(DeclRef).?.decl.container.file_scope.tree,
481481 }
......@@ -913,15 +913,15 @@ pub const Scope = struct {
913913 /// Parents can be: `GenZir`, `File`
914914 parent: *Scope,
915915 /// All `GenZir` scopes for the same ZIR share this.
916 zir_code: *WipZirCode,
916 astgen: *AstGen,
917917 /// Keeps track of the list of instructions in this scope only. Indexes
918 /// to instructions in `zir_code`.
918 /// to instructions in `astgen`.
919919 instructions: ArrayListUnmanaged(zir.Inst.Index) = .{},
920920 label: ?Label = null,
921921 break_block: zir.Inst.Index = 0,
922922 continue_block: zir.Inst.Index = 0,
923923 /// Only valid when setBlockResultLoc is called.
924 break_result_loc: astgen.ResultLoc = undefined,
924 break_result_loc: AstGen.ResultLoc = undefined,
925925 /// When a block has a pointer result location, here it is.
926926 rl_ptr: zir.Inst.Ref = .none,
927927 /// Keeps track of how many branches of a block did not actually
......@@ -948,49 +948,51 @@ pub const Scope = struct {
948948 };
949949
950950 /// Only valid to call on the top of the `GenZir` stack. Completes the
951 /// `WipZirCode` into a `zir.Code`. Leaves the `WipZirCode` in an
951 /// `AstGen` into a `zir.Code`. Leaves the `AstGen` in an
952952 /// initialized, but empty, state.
953953 pub fn finish(gz: *GenZir) !zir.Code {
954 const gpa = gz.zir_code.gpa;
954 const gpa = gz.astgen.mod.gpa;
955955 try gz.setBlockBody(0);
956956 return zir.Code{
957 .instructions = gz.zir_code.instructions.toOwnedSlice(),
958 .string_bytes = gz.zir_code.string_bytes.toOwnedSlice(gpa),
959 .extra = gz.zir_code.extra.toOwnedSlice(gpa),
960 .decls = gz.zir_code.decls.toOwnedSlice(gpa),
957 .instructions = gz.astgen.instructions.toOwnedSlice(),
958 .string_bytes = gz.astgen.string_bytes.toOwnedSlice(gpa),
959 .extra = gz.astgen.extra.toOwnedSlice(gpa),
960 .decls = gz.astgen.decls.toOwnedSlice(gpa),
961961 };
962962 }
963963
964964 pub fn tokSrcLoc(gz: GenZir, token_index: ast.TokenIndex) LazySrcLoc {
965 return gz.zir_code.decl.tokSrcLoc(token_index);
965 return gz.astgen.decl.tokSrcLoc(token_index);
966966 }
967967
968968 pub fn nodeSrcLoc(gz: GenZir, node_index: ast.Node.Index) LazySrcLoc {
969 return gz.zir_code.decl.nodeSrcLoc(node_index);
969 return gz.astgen.decl.nodeSrcLoc(node_index);
970970 }
971971
972972 pub fn tree(gz: *const GenZir) *const ast.Tree {
973 return &gz.zir_code.decl.container.file_scope.tree;
973 return &gz.astgen.decl.container.file_scope.tree;
974974 }
975975
976976 pub fn setBoolBrBody(gz: GenZir, inst: zir.Inst.Index) !void {
977 try gz.zir_code.extra.ensureCapacity(gz.zir_code.gpa, gz.zir_code.extra.items.len +
977 const gpa = gz.astgen.mod.gpa;
978 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
978979 @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len);
979 const zir_datas = gz.zir_code.instructions.items(.data);
980 zir_datas[inst].bool_br.payload_index = gz.zir_code.addExtraAssumeCapacity(
980 const zir_datas = gz.astgen.instructions.items(.data);
981 zir_datas[inst].bool_br.payload_index = gz.astgen.addExtraAssumeCapacity(
981982 zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) },
982983 );
983 gz.zir_code.extra.appendSliceAssumeCapacity(gz.instructions.items);
984 gz.astgen.extra.appendSliceAssumeCapacity(gz.instructions.items);
984985 }
985986
986987 pub fn setBlockBody(gz: GenZir, inst: zir.Inst.Index) !void {
987 try gz.zir_code.extra.ensureCapacity(gz.zir_code.gpa, gz.zir_code.extra.items.len +
988 const gpa = gz.astgen.mod.gpa;
989 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
988990 @typeInfo(zir.Inst.Block).Struct.fields.len + gz.instructions.items.len);
989 const zir_datas = gz.zir_code.instructions.items(.data);
990 zir_datas[inst].pl_node.payload_index = gz.zir_code.addExtraAssumeCapacity(
991 const zir_datas = gz.astgen.instructions.items(.data);
992 zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity(
991993 zir.Inst.Block{ .body_len = @intCast(u32, gz.instructions.items.len) },
992994 );
993 gz.zir_code.extra.appendSliceAssumeCapacity(gz.instructions.items);
995 gz.astgen.extra.appendSliceAssumeCapacity(gz.instructions.items);
994996 }
995997
996998 pub fn addFnTypeCc(gz: *GenZir, tag: zir.Inst.Tag, args: struct {
......@@ -1000,20 +1002,20 @@ pub const Scope = struct {
10001002 }) !zir.Inst.Ref {
10011003 assert(args.ret_ty != .none);
10021004 assert(args.cc != .none);
1003 const gpa = gz.zir_code.gpa;
1005 const gpa = gz.astgen.mod.gpa;
10041006 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1005 try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1);
1006 try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len +
1007 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1008 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
10071009 @typeInfo(zir.Inst.FnTypeCc).Struct.fields.len + args.param_types.len);
10081010
1009 const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.FnTypeCc{
1011 const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.FnTypeCc{
10101012 .cc = args.cc,
10111013 .param_types_len = @intCast(u32, args.param_types.len),
10121014 });
1013 gz.zir_code.appendRefsAssumeCapacity(args.param_types);
1015 gz.astgen.appendRefsAssumeCapacity(args.param_types);
10141016
1015 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
1016 gz.zir_code.instructions.appendAssumeCapacity(.{
1017 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1018 gz.astgen.instructions.appendAssumeCapacity(.{
10171019 .tag = tag,
10181020 .data = .{ .fn_type = .{
10191021 .return_type = args.ret_ty,
......@@ -1021,7 +1023,7 @@ pub const Scope = struct {
10211023 } },
10221024 });
10231025 gz.instructions.appendAssumeCapacity(new_index);
1024 return gz.zir_code.indexToRef(new_index);
1026 return gz.astgen.indexToRef(new_index);
10251027 }
10261028
10271029 pub fn addFnType(
......@@ -1031,19 +1033,19 @@ pub const Scope = struct {
10311033 param_types: []const zir.Inst.Ref,
10321034 ) !zir.Inst.Ref {
10331035 assert(ret_ty != .none);
1034 const gpa = gz.zir_code.gpa;
1036 const gpa = gz.astgen.mod.gpa;
10351037 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1036 try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1);
1037 try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len +
1038 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1039 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
10381040 @typeInfo(zir.Inst.FnType).Struct.fields.len + param_types.len);
10391041
1040 const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.FnType{
1042 const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.FnType{
10411043 .param_types_len = @intCast(u32, param_types.len),
10421044 });
1043 gz.zir_code.appendRefsAssumeCapacity(param_types);
1045 gz.astgen.appendRefsAssumeCapacity(param_types);
10441046
1045 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
1046 gz.zir_code.instructions.appendAssumeCapacity(.{
1047 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1048 gz.astgen.instructions.appendAssumeCapacity(.{
10471049 .tag = tag,
10481050 .data = .{ .fn_type = .{
10491051 .return_type = ret_ty,
......@@ -1051,7 +1053,7 @@ pub const Scope = struct {
10511053 } },
10521054 });
10531055 gz.instructions.appendAssumeCapacity(new_index);
1054 return gz.zir_code.indexToRef(new_index);
1056 return gz.astgen.indexToRef(new_index);
10551057 }
10561058
10571059 pub fn addCall(
......@@ -1064,28 +1066,28 @@ pub const Scope = struct {
10641066 ) !zir.Inst.Ref {
10651067 assert(callee != .none);
10661068 assert(src_node != 0);
1067 const gpa = gz.zir_code.gpa;
1069 const gpa = gz.astgen.mod.gpa;
10681070 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1069 try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1);
1070 try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len +
1071 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
1072 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +
10711073 @typeInfo(zir.Inst.Call).Struct.fields.len + args.len);
10721074
1073 const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.Call{
1075 const payload_index = gz.astgen.addExtraAssumeCapacity(zir.Inst.Call{
10741076 .callee = callee,
10751077 .args_len = @intCast(u32, args.len),
10761078 });
1077 gz.zir_code.appendRefsAssumeCapacity(args);
1079 gz.astgen.appendRefsAssumeCapacity(args);
10781080
1079 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
1080 gz.zir_code.instructions.appendAssumeCapacity(.{
1081 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1082 gz.astgen.instructions.appendAssumeCapacity(.{
10811083 .tag = tag,
10821084 .data = .{ .pl_node = .{
1083 .src_node = gz.zir_code.decl.nodeIndexToRelative(src_node),
1085 .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
10841086 .payload_index = payload_index,
10851087 } },
10861088 });
10871089 gz.instructions.appendAssumeCapacity(new_index);
1088 return gz.zir_code.indexToRef(new_index);
1090 return gz.astgen.indexToRef(new_index);
10891091 }
10901092
10911093 /// Note that this returns a `zir.Inst.Index` not a ref.
......@@ -1096,12 +1098,12 @@ pub const Scope = struct {
10961098 lhs: zir.Inst.Ref,
10971099 ) !zir.Inst.Index {
10981100 assert(lhs != .none);
1099 const gpa = gz.zir_code.gpa;
1101 const gpa = gz.astgen.mod.gpa;
11001102 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1101 try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1);
1103 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
11021104
1103 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
1104 gz.zir_code.instructions.appendAssumeCapacity(.{
1105 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1106 gz.astgen.instructions.appendAssumeCapacity(.{
11051107 .tag = tag,
11061108 .data = .{ .bool_br = .{
11071109 .lhs = lhs,
......@@ -1131,7 +1133,7 @@ pub const Scope = struct {
11311133 .tag = tag,
11321134 .data = .{ .un_node = .{
11331135 .operand = operand,
1134 .src_node = gz.zir_code.decl.nodeIndexToRelative(src_node),
1136 .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
11351137 } },
11361138 });
11371139 }
......@@ -1143,21 +1145,21 @@ pub const Scope = struct {
11431145 src_node: ast.Node.Index,
11441146 extra: anytype,
11451147 ) !zir.Inst.Ref {
1146 const gpa = gz.zir_code.gpa;
1148 const gpa = gz.astgen.mod.gpa;
11471149 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1148 try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1);
1150 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
11491151
1150 const payload_index = try gz.zir_code.addExtra(extra);
1151 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
1152 gz.zir_code.instructions.appendAssumeCapacity(.{
1152 const payload_index = try gz.astgen.addExtra(extra);
1153 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1154 gz.astgen.instructions.appendAssumeCapacity(.{
11531155 .tag = tag,
11541156 .data = .{ .pl_node = .{
1155 .src_node = gz.zir_code.decl.nodeIndexToRelative(src_node),
1157 .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
11561158 .payload_index = payload_index,
11571159 } },
11581160 });
11591161 gz.instructions.appendAssumeCapacity(new_index);
1160 return gz.zir_code.indexToRef(new_index);
1162 return gz.astgen.indexToRef(new_index);
11611163 }
11621164
11631165 pub fn addArrayTypeSentinel(
......@@ -1166,16 +1168,16 @@ pub const Scope = struct {
11661168 sentinel: zir.Inst.Ref,
11671169 elem_type: zir.Inst.Ref,
11681170 ) !zir.Inst.Ref {
1169 const gpa = gz.zir_code.gpa;
1171 const gpa = gz.astgen.mod.gpa;
11701172 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1171 try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1);
1173 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
11721174
1173 const payload_index = try gz.zir_code.addExtra(zir.Inst.ArrayTypeSentinel{
1175 const payload_index = try gz.astgen.addExtra(zir.Inst.ArrayTypeSentinel{
11741176 .sentinel = sentinel,
11751177 .elem_type = elem_type,
11761178 });
1177 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
1178 gz.zir_code.instructions.appendAssumeCapacity(.{
1179 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1180 gz.astgen.instructions.appendAssumeCapacity(.{
11791181 .tag = .array_type_sentinel,
11801182 .data = .{ .array_type_sentinel = .{
11811183 .len = len,
......@@ -1183,7 +1185,7 @@ pub const Scope = struct {
11831185 } },
11841186 });
11851187 gz.instructions.appendAssumeCapacity(new_index);
1186 return gz.zir_code.indexToRef(new_index);
1188 return gz.astgen.indexToRef(new_index);
11871189 }
11881190
11891191 pub fn addUnTok(
......@@ -1198,7 +1200,7 @@ pub const Scope = struct {
11981200 .tag = tag,
11991201 .data = .{ .un_tok = .{
12001202 .operand = operand,
1201 .src_tok = abs_tok_index - gz.zir_code.decl.srcToken(),
1203 .src_tok = abs_tok_index - gz.astgen.decl.srcToken(),
12021204 } },
12031205 });
12041206 }
......@@ -1214,7 +1216,7 @@ pub const Scope = struct {
12141216 .tag = tag,
12151217 .data = .{ .str_tok = .{
12161218 .start = str_index,
1217 .src_tok = abs_tok_index - gz.zir_code.decl.srcToken(),
1219 .src_tok = abs_tok_index - gz.astgen.decl.srcToken(),
12181220 } },
12191221 });
12201222 }
......@@ -1260,7 +1262,7 @@ pub const Scope = struct {
12601262 return gz.add(.{
12611263 .tag = tag,
12621264 .data = .{ .pl_node = .{
1263 .src_node = gz.zir_code.decl.nodeIndexToRelative(src_node),
1265 .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
12641266 .payload_index = decl_index,
12651267 } },
12661268 });
......@@ -1274,7 +1276,7 @@ pub const Scope = struct {
12741276 ) !zir.Inst.Ref {
12751277 return gz.add(.{
12761278 .tag = tag,
1277 .data = .{ .node = gz.zir_code.decl.nodeIndexToRelative(src_node) },
1279 .data = .{ .node = gz.astgen.decl.nodeIndexToRelative(src_node) },
12781280 });
12791281 }
12801282
......@@ -1298,11 +1300,12 @@ pub const Scope = struct {
12981300 /// Does *not* append the block instruction to the scope.
12991301 /// Leaves the `payload_index` field undefined.
13001302 pub fn addBlock(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index {
1301 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
1302 try gz.zir_code.instructions.append(gz.zir_code.gpa, .{
1303 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1304 const gpa = gz.astgen.mod.gpa;
1305 try gz.astgen.instructions.append(gpa, .{
13031306 .tag = tag,
13041307 .data = .{ .pl_node = .{
1305 .src_node = gz.zir_code.decl.nodeIndexToRelative(node),
1308 .src_node = gz.astgen.decl.nodeIndexToRelative(node),
13061309 .payload_index = undefined,
13071310 } },
13081311 });
......@@ -1312,12 +1315,13 @@ pub const Scope = struct {
13121315 /// Note that this returns a `zir.Inst.Index` not a ref.
13131316 /// Leaves the `payload_index` field undefined.
13141317 pub fn addCondBr(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index {
1315 try gz.instructions.ensureCapacity(gz.zir_code.gpa, gz.instructions.items.len + 1);
1316 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
1317 try gz.zir_code.instructions.append(gz.zir_code.gpa, .{
1318 const gpa = gz.astgen.mod.gpa;
1319 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1320 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1321 try gz.astgen.instructions.append(gpa, .{
13181322 .tag = tag,
13191323 .data = .{ .pl_node = .{
1320 .src_node = gz.zir_code.decl.nodeIndexToRelative(node),
1324 .src_node = gz.astgen.decl.nodeIndexToRelative(node),
13211325 .payload_index = undefined,
13221326 } },
13231327 });
......@@ -1326,16 +1330,16 @@ pub const Scope = struct {
13261330 }
13271331
13281332 pub fn add(gz: *GenZir, inst: zir.Inst) !zir.Inst.Ref {
1329 return gz.zir_code.indexToRef(try gz.addAsIndex(inst));
1333 return gz.astgen.indexToRef(try gz.addAsIndex(inst));
13301334 }
13311335
13321336 pub fn addAsIndex(gz: *GenZir, inst: zir.Inst) !zir.Inst.Index {
1333 const gpa = gz.zir_code.gpa;
1337 const gpa = gz.astgen.mod.gpa;
13341338 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1335 try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1);
1339 try gz.astgen.instructions.ensureCapacity(gpa, gz.astgen.instructions.len + 1);
13361340
1337 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
1338 gz.zir_code.instructions.appendAssumeCapacity(inst);
1341 const new_index = @intCast(zir.Inst.Index, gz.astgen.instructions.len);
1342 gz.astgen.instructions.appendAssumeCapacity(inst);
13391343 gz.instructions.appendAssumeCapacity(new_index);
13401344 return new_index;
13411345 }
......@@ -1378,100 +1382,6 @@ pub const Scope = struct {
13781382 };
13791383};
13801384
1381/// A Work-In-Progress `zir.Code`. This is a shared parent of all
1382/// `GenZir` scopes. Once the `zir.Code` is produced, this struct
1383/// is deinitialized.
1384/// The `GenZir.finish` function converts this to a `zir.Code`.
1385pub const WipZirCode = struct {
1386 instructions: std.MultiArrayList(zir.Inst) = .{},
1387 string_bytes: ArrayListUnmanaged(u8) = .{},
1388 extra: ArrayListUnmanaged(u32) = .{},
1389 decl_map: std.StringArrayHashMapUnmanaged(void) = .{},
1390 decls: ArrayListUnmanaged(*Decl) = .{},
1391 /// The end of special indexes. `zir.Inst.Ref` subtracts against this number to convert
1392 /// to `zir.Inst.Index`. The default here is correct if there are 0 parameters.
1393 ref_start_index: u32 = zir.Inst.Ref.typed_value_map.len,
1394 decl: *Decl,
1395 gpa: *Allocator,
1396 arena: *Allocator,
1397
1398 pub fn addExtra(wzc: *WipZirCode, extra: anytype) Allocator.Error!u32 {
1399 const fields = std.meta.fields(@TypeOf(extra));
1400 try wzc.extra.ensureCapacity(wzc.gpa, wzc.extra.items.len + fields.len);
1401 return addExtraAssumeCapacity(wzc, extra);
1402 }
1403
1404 pub fn addExtraAssumeCapacity(wzc: *WipZirCode, extra: anytype) u32 {
1405 const fields = std.meta.fields(@TypeOf(extra));
1406 const result = @intCast(u32, wzc.extra.items.len);
1407 inline for (fields) |field| {
1408 wzc.extra.appendAssumeCapacity(switch (field.field_type) {
1409 u32 => @field(extra, field.name),
1410 zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
1411 else => @compileError("bad field type"),
1412 });
1413 }
1414 return result;
1415 }
1416
1417 pub fn appendRefs(wzc: *WipZirCode, refs: []const zir.Inst.Ref) !void {
1418 const coerced = @bitCast([]const u32, refs);
1419 return wzc.extra.appendSlice(wzc.gpa, coerced);
1420 }
1421
1422 pub fn appendRefsAssumeCapacity(wzc: *WipZirCode, refs: []const zir.Inst.Ref) void {
1423 const coerced = @bitCast([]const u32, refs);
1424 wzc.extra.appendSliceAssumeCapacity(coerced);
1425 }
1426
1427 pub fn refIsNoReturn(wzc: WipZirCode, inst_ref: zir.Inst.Ref) bool {
1428 if (inst_ref == .unreachable_value) return true;
1429 if (wzc.refToIndex(inst_ref)) |inst_index| {
1430 return wzc.instructions.items(.tag)[inst_index].isNoReturn();
1431 }
1432 return false;
1433 }
1434
1435 pub fn indexToRef(wzc: WipZirCode, inst: zir.Inst.Index) zir.Inst.Ref {
1436 return @intToEnum(zir.Inst.Ref, wzc.ref_start_index + inst);
1437 }
1438
1439 pub fn refToIndex(wzc: WipZirCode, inst: zir.Inst.Ref) ?zir.Inst.Index {
1440 const ref_int = @enumToInt(inst);
1441 if (ref_int >= wzc.ref_start_index) {
1442 return ref_int - wzc.ref_start_index;
1443 } else {
1444 return null;
1445 }
1446 }
1447
1448 pub fn deinit(wzc: *WipZirCode) void {
1449 wzc.instructions.deinit(wzc.gpa);
1450 wzc.extra.deinit(wzc.gpa);
1451 wzc.string_bytes.deinit(wzc.gpa);
1452 wzc.decl_map.deinit(wzc.gpa);
1453 wzc.decls.deinit(wzc.gpa);
1454 }
1455};
1456
1457/// Call `deinit` on the result.
1458fn initAstGen(mod: *Module, decl: *Decl, arena: *Allocator) !WipZirCode {
1459 var wzc: WipZirCode = .{
1460 .decl = decl,
1461 .arena = arena,
1462 .gpa = mod.gpa,
1463 };
1464 // Must be a block instruction at index 0 with the root body.
1465 try wzc.instructions.append(mod.gpa, .{
1466 .tag = .block,
1467 .data = .{ .pl_node = .{
1468 .src_node = 0,
1469 .payload_index = undefined,
1470 } },
1471 });
1472 return wzc;
1473}
1474
14751385/// This struct holds data necessary to construct API-facing `AllErrors.Message`.
14761386/// Its memory is managed with the general purpose allocator so that they
14771387/// can be created and destroyed in response to incremental updates.
......@@ -2102,18 +2012,18 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
21022012 defer analysis_arena.deinit();
21032013
21042014 var code: zir.Code = blk: {
2105 var wip_zir_code = try mod.initAstGen(decl, &analysis_arena.allocator);
2106 defer wip_zir_code.deinit();
2015 var astgen = try AstGen.init(mod, decl, &analysis_arena.allocator);
2016 defer astgen.deinit();
21072017
21082018 var gen_scope: Scope.GenZir = .{
21092019 .force_comptime = true,
21102020 .parent = &decl.container.base,
2111 .zir_code = &wip_zir_code,
2021 .astgen = &astgen,
21122022 };
21132023 defer gen_scope.instructions.deinit(mod.gpa);
21142024
21152025 const block_expr = node_datas[decl_node].lhs;
2116 _ = try astgen.comptimeExpr(mod, &gen_scope.base, .none, block_expr);
2026 _ = try AstGen.comptimeExpr(mod, &gen_scope.base, .none, block_expr);
21172027
21182028 const code = try gen_scope.finish();
21192029 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
......@@ -2175,13 +2085,13 @@ fn astgenAndSemaFn(
21752085 var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
21762086 defer fn_type_scope_arena.deinit();
21772087
2178 var fn_type_wip_zir_code = try mod.initAstGen(decl, &fn_type_scope_arena.allocator);
2179 defer fn_type_wip_zir_code.deinit();
2088 var fn_type_astgen = try AstGen.init(mod, decl, &fn_type_scope_arena.allocator);
2089 defer fn_type_astgen.deinit();
21802090
21812091 var fn_type_scope: Scope.GenZir = .{
21822092 .force_comptime = true,
21832093 .parent = &decl.container.base,
2184 .zir_code = &fn_type_wip_zir_code,
2094 .astgen = &fn_type_astgen,
21852095 };
21862096 defer fn_type_scope.instructions.deinit(mod.gpa);
21872097
......@@ -2223,7 +2133,7 @@ fn astgenAndSemaFn(
22232133 const param_type_node = param.type_expr;
22242134 assert(param_type_node != 0);
22252135 param_types[param_type_i] =
2226 try astgen.expr(mod, &fn_type_scope.base, .{ .ty = .type_type }, param_type_node);
2136 try AstGen.expr(mod, &fn_type_scope.base, .{ .ty = .type_type }, param_type_node);
22272137 }
22282138 assert(param_type_i == param_count);
22292139 }
......@@ -2292,7 +2202,7 @@ fn astgenAndSemaFn(
22922202 if (token_tags[maybe_bang] == .bang) {
22932203 return mod.failTok(&fn_type_scope.base, maybe_bang, "TODO implement inferred error sets", .{});
22942204 }
2295 const return_type_inst = try astgen.expr(
2205 const return_type_inst = try AstGen.expr(
22962206 mod,
22972207 &fn_type_scope.base,
22982208 .{ .ty = .type_type },
......@@ -2308,7 +2218,7 @@ fn astgenAndSemaFn(
23082218 // TODO instead of enum literal type, this needs to be the
23092219 // std.builtin.CallingConvention enum. We need to implement importing other files
23102220 // and enums in order to fix this.
2311 try astgen.comptimeExpr(
2221 try AstGen.comptimeExpr(
23122222 mod,
23132223 &fn_type_scope.base,
23142224 .{ .ty = .enum_literal_type },
......@@ -2408,21 +2318,21 @@ fn astgenAndSemaFn(
24082318
24092319 const fn_zir: zir.Code = blk: {
24102320 // We put the ZIR inside the Decl arena.
2411 var wip_zir_code = try mod.initAstGen(decl, &decl_arena.allocator);
2412 wip_zir_code.ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count);
2413 defer wip_zir_code.deinit();
2321 var astgen = try AstGen.init(mod, decl, &decl_arena.allocator);
2322 astgen.ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count);
2323 defer astgen.deinit();
24142324
24152325 var gen_scope: Scope.GenZir = .{
24162326 .force_comptime = false,
24172327 .parent = &decl.container.base,
2418 .zir_code = &wip_zir_code,
2328 .astgen = &astgen,
24192329 };
24202330 defer gen_scope.instructions.deinit(mod.gpa);
24212331
24222332 // Iterate over the parameters. We put the param names as the first N
24232333 // items inside `extra` so that debug info later can refer to the parameter names
24242334 // even while the respective source code is unloaded.
2425 try wip_zir_code.extra.ensureCapacity(mod.gpa, param_count);
2335 try astgen.extra.ensureCapacity(mod.gpa, param_count);
24262336
24272337 var params_scope = &gen_scope.base;
24282338 var i: usize = 0;
......@@ -2443,18 +2353,18 @@ fn astgenAndSemaFn(
24432353
24442354 // Additionally put the param name into `string_bytes` and reference it with
24452355 // `extra` so that we have access to the data in codegen, for debug info.
2446 const str_index = @intCast(u32, wip_zir_code.string_bytes.items.len);
2447 wip_zir_code.extra.appendAssumeCapacity(str_index);
2448 const used_bytes = wip_zir_code.string_bytes.items.len;
2449 try wip_zir_code.string_bytes.ensureCapacity(mod.gpa, used_bytes + param_name.len + 1);
2450 wip_zir_code.string_bytes.appendSliceAssumeCapacity(param_name);
2451 wip_zir_code.string_bytes.appendAssumeCapacity(0);
2356 const str_index = @intCast(u32, astgen.string_bytes.items.len);
2357 astgen.extra.appendAssumeCapacity(str_index);
2358 const used_bytes = astgen.string_bytes.items.len;
2359 try astgen.string_bytes.ensureCapacity(mod.gpa, used_bytes + param_name.len + 1);
2360 astgen.string_bytes.appendSliceAssumeCapacity(param_name);
2361 astgen.string_bytes.appendAssumeCapacity(0);
24522362 }
24532363
2454 _ = try astgen.expr(mod, params_scope, .none, body_node);
2364 _ = try AstGen.expr(mod, params_scope, .none, body_node);
24552365
24562366 if (gen_scope.instructions.items.len == 0 or
2457 !wip_zir_code.instructions.items(.tag)[gen_scope.instructions.items.len - 1]
2367 !astgen.instructions.items(.tag)[gen_scope.instructions.items.len - 1]
24582368 .isNoReturn())
24592369 {
24602370 // astgen uses result location semantics to coerce return operands.
......@@ -2615,21 +2525,21 @@ fn astgenAndSemaVarDecl(
26152525 var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
26162526 defer gen_scope_arena.deinit();
26172527
2618 var wip_zir_code = try mod.initAstGen(decl, &gen_scope_arena.allocator);
2619 defer wip_zir_code.deinit();
2528 var astgen = try AstGen.init(mod, decl, &gen_scope_arena.allocator);
2529 defer astgen.deinit();
26202530
26212531 var gen_scope: Scope.GenZir = .{
26222532 .force_comptime = true,
26232533 .parent = &decl.container.base,
2624 .zir_code = &wip_zir_code,
2534 .astgen = &astgen,
26252535 };
26262536 defer gen_scope.instructions.deinit(mod.gpa);
26272537
2628 const init_result_loc: astgen.ResultLoc = if (var_decl.ast.type_node != 0) .{
2629 .ty = try astgen.expr(mod, &gen_scope.base, .{ .ty = .type_type }, var_decl.ast.type_node),
2538 const init_result_loc: AstGen.ResultLoc = if (var_decl.ast.type_node != 0) .{
2539 .ty = try AstGen.expr(mod, &gen_scope.base, .{ .ty = .type_type }, var_decl.ast.type_node),
26302540 } else .none;
26312541
2632 const init_inst = try astgen.comptimeExpr(
2542 const init_inst = try AstGen.comptimeExpr(
26332543 mod,
26342544 &gen_scope.base,
26352545 init_result_loc,
......@@ -2684,17 +2594,17 @@ fn astgenAndSemaVarDecl(
26842594 var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
26852595 defer type_scope_arena.deinit();
26862596
2687 var wip_zir_code = try mod.initAstGen(decl, &type_scope_arena.allocator);
2688 defer wip_zir_code.deinit();
2597 var astgen = try AstGen.init(mod, decl, &type_scope_arena.allocator);
2598 defer astgen.deinit();
26892599
26902600 var type_scope: Scope.GenZir = .{
26912601 .force_comptime = true,
26922602 .parent = &decl.container.base,
2693 .zir_code = &wip_zir_code,
2603 .astgen = &astgen,
26942604 };
26952605 defer type_scope.instructions.deinit(mod.gpa);
26962606
2697 const var_type = try astgen.typeExpr(mod, &type_scope.base, var_decl.ast.type_node);
2607 const var_type = try AstGen.typeExpr(mod, &type_scope.base, var_decl.ast.type_node);
26982608 _ = try type_scope.addBreak(.break_inline, 0, var_type);
26992609
27002610 var code = try type_scope.finish();
......@@ -3796,21 +3706,21 @@ pub fn failWithOwnedErrorMsg(mod: *Module, scope: *Scope, err_msg: *ErrorMsg) In
37963706 },
37973707 .gen_zir => {
37983708 const gen_zir = scope.cast(Scope.GenZir).?;
3799 gen_zir.zir_code.decl.analysis = .sema_failure;
3800 gen_zir.zir_code.decl.generation = mod.generation;
3801 mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.zir_code.decl, err_msg);
3709 gen_zir.astgen.decl.analysis = .sema_failure;
3710 gen_zir.astgen.decl.generation = mod.generation;
3711 mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg);
38023712 },
38033713 .local_val => {
38043714 const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir;
3805 gen_zir.zir_code.decl.analysis = .sema_failure;
3806 gen_zir.zir_code.decl.generation = mod.generation;
3807 mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.zir_code.decl, err_msg);
3715 gen_zir.astgen.decl.analysis = .sema_failure;
3716 gen_zir.astgen.decl.generation = mod.generation;
3717 mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg);
38083718 },
38093719 .local_ptr => {
38103720 const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir;
3811 gen_zir.zir_code.decl.analysis = .sema_failure;
3812 gen_zir.zir_code.decl.generation = mod.generation;
3813 mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.zir_code.decl, err_msg);
3721 gen_zir.astgen.decl.analysis = .sema_failure;
3722 gen_zir.astgen.decl.generation = mod.generation;
3723 mod.failed_decls.putAssumeCapacityNoClobber(gen_zir.astgen.decl, err_msg);
38143724 },
38153725 .file => unreachable,
38163726 .container => unreachable,
src/astgen.zig deleted-3881
......@@ -1,3881 +0,0 @@
1const std = @import("std");
2const mem = std.mem;
3const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;
5
6const Value = @import("value.zig").Value;
7const Type = @import("type.zig").Type;
8const TypedValue = @import("TypedValue.zig");
9const zir = @import("zir.zig");
10const Module = @import("Module.zig");
11const ast = std.zig.ast;
12const trace = @import("tracy.zig").trace;
13const Scope = Module.Scope;
14const InnerError = Module.InnerError;
15const BuiltinFn = @import("BuiltinFn.zig");
16
17pub const ResultLoc = union(enum) {
18 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
19 /// expression should be generated. The result instruction from the expression must
20 /// be ignored.
21 discard,
22 /// The expression has an inferred type, and it will be evaluated as an rvalue.
23 none,
24 /// The expression must generate a pointer rather than a value. For example, the left hand side
25 /// of an assignment uses this kind of result location.
26 ref,
27 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
28 ty: zir.Inst.Ref,
29 /// The expression must store its result into this typed pointer. The result instruction
30 /// from the expression must be ignored.
31 ptr: zir.Inst.Ref,
32 /// The expression must store its result into this allocation, which has an inferred type.
33 /// The result instruction from the expression must be ignored.
34 /// Always an instruction with tag `alloc_inferred`.
35 inferred_ptr: zir.Inst.Ref,
36 /// The expression must store its result into this pointer, which is a typed pointer that
37 /// has been bitcasted to whatever the expression's type is.
38 /// The result instruction from the expression must be ignored.
39 bitcasted_ptr: zir.Inst.Ref,
40 /// There is a pointer for the expression to store its result into, however, its type
41 /// is inferred based on peer type resolution for a `zir.Inst.Block`.
42 /// The result instruction from the expression must be ignored.
43 block_ptr: *Module.Scope.GenZir,
44
45 pub const Strategy = struct {
46 elide_store_to_block_ptr_instructions: bool,
47 tag: Tag,
48
49 pub const Tag = enum {
50 /// Both branches will use break_void; result location is used to communicate the
51 /// result instruction.
52 break_void,
53 /// Use break statements to pass the block result value, and call rvalue() at
54 /// the end depending on rl. Also elide the store_to_block_ptr instructions
55 /// depending on rl.
56 break_operand,
57 };
58 };
59};
60
61pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!zir.Inst.Ref {
62 return expr(mod, scope, .{ .ty = .type_type }, type_node);
63}
64
65fn lvalExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
66 const tree = scope.tree();
67 const node_tags = tree.nodes.items(.tag);
68 const main_tokens = tree.nodes.items(.main_token);
69 switch (node_tags[node]) {
70 .root => unreachable,
71 .@"usingnamespace" => unreachable,
72 .test_decl => unreachable,
73 .global_var_decl => unreachable,
74 .local_var_decl => unreachable,
75 .simple_var_decl => unreachable,
76 .aligned_var_decl => unreachable,
77 .switch_case => unreachable,
78 .switch_case_one => unreachable,
79 .container_field_init => unreachable,
80 .container_field_align => unreachable,
81 .container_field => unreachable,
82 .asm_output => unreachable,
83 .asm_input => unreachable,
84
85 .assign,
86 .assign_bit_and,
87 .assign_bit_or,
88 .assign_bit_shift_left,
89 .assign_bit_shift_right,
90 .assign_bit_xor,
91 .assign_div,
92 .assign_sub,
93 .assign_sub_wrap,
94 .assign_mod,
95 .assign_add,
96 .assign_add_wrap,
97 .assign_mul,
98 .assign_mul_wrap,
99 .add,
100 .add_wrap,
101 .sub,
102 .sub_wrap,
103 .mul,
104 .mul_wrap,
105 .div,
106 .mod,
107 .bit_and,
108 .bit_or,
109 .bit_shift_left,
110 .bit_shift_right,
111 .bit_xor,
112 .bang_equal,
113 .equal_equal,
114 .greater_than,
115 .greater_or_equal,
116 .less_than,
117 .less_or_equal,
118 .array_cat,
119 .array_mult,
120 .bool_and,
121 .bool_or,
122 .@"asm",
123 .asm_simple,
124 .string_literal,
125 .integer_literal,
126 .call,
127 .call_comma,
128 .async_call,
129 .async_call_comma,
130 .call_one,
131 .call_one_comma,
132 .async_call_one,
133 .async_call_one_comma,
134 .unreachable_literal,
135 .@"return",
136 .@"if",
137 .if_simple,
138 .@"while",
139 .while_simple,
140 .while_cont,
141 .bool_not,
142 .address_of,
143 .float_literal,
144 .undefined_literal,
145 .true_literal,
146 .false_literal,
147 .null_literal,
148 .optional_type,
149 .block,
150 .block_semicolon,
151 .block_two,
152 .block_two_semicolon,
153 .@"break",
154 .ptr_type_aligned,
155 .ptr_type_sentinel,
156 .ptr_type,
157 .ptr_type_bit_range,
158 .array_type,
159 .array_type_sentinel,
160 .enum_literal,
161 .multiline_string_literal,
162 .char_literal,
163 .@"defer",
164 .@"errdefer",
165 .@"catch",
166 .error_union,
167 .merge_error_sets,
168 .switch_range,
169 .@"await",
170 .bit_not,
171 .negation,
172 .negation_wrap,
173 .@"resume",
174 .@"try",
175 .slice,
176 .slice_open,
177 .slice_sentinel,
178 .array_init_one,
179 .array_init_one_comma,
180 .array_init_dot_two,
181 .array_init_dot_two_comma,
182 .array_init_dot,
183 .array_init_dot_comma,
184 .array_init,
185 .array_init_comma,
186 .struct_init_one,
187 .struct_init_one_comma,
188 .struct_init_dot_two,
189 .struct_init_dot_two_comma,
190 .struct_init_dot,
191 .struct_init_dot_comma,
192 .struct_init,
193 .struct_init_comma,
194 .@"switch",
195 .switch_comma,
196 .@"for",
197 .for_simple,
198 .@"suspend",
199 .@"continue",
200 .@"anytype",
201 .fn_proto_simple,
202 .fn_proto_multi,
203 .fn_proto_one,
204 .fn_proto,
205 .fn_decl,
206 .anyframe_type,
207 .anyframe_literal,
208 .error_set_decl,
209 .container_decl,
210 .container_decl_trailing,
211 .container_decl_two,
212 .container_decl_two_trailing,
213 .container_decl_arg,
214 .container_decl_arg_trailing,
215 .tagged_union,
216 .tagged_union_trailing,
217 .tagged_union_two,
218 .tagged_union_two_trailing,
219 .tagged_union_enum_tag,
220 .tagged_union_enum_tag_trailing,
221 .@"comptime",
222 .@"nosuspend",
223 .error_value,
224 => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
225
226 .builtin_call,
227 .builtin_call_comma,
228 .builtin_call_two,
229 .builtin_call_two_comma,
230 => {
231 const builtin_token = main_tokens[node];
232 const builtin_name = tree.tokenSlice(builtin_token);
233 // If the builtin is an invalid name, we don't cause an error here; instead
234 // let it pass, and the error will be "invalid builtin function" later.
235 if (BuiltinFn.list.get(builtin_name)) |info| {
236 if (!info.allows_lvalue) {
237 return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
238 }
239 }
240 },
241
242 // These can be assigned to.
243 .unwrap_optional,
244 .deref,
245 .field_access,
246 .array_access,
247 .identifier,
248 .grouped_expression,
249 .@"orelse",
250 => {},
251 }
252 return expr(mod, scope, .ref, node);
253}
254
255/// Turn Zig AST into untyped ZIR istructions.
256/// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the
257/// result instruction can be used to inspect whether it is isNoReturn() but that is it,
258/// it must otherwise not be used.
259pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref {
260 const tree = scope.tree();
261 const main_tokens = tree.nodes.items(.main_token);
262 const token_tags = tree.tokens.items(.tag);
263 const node_datas = tree.nodes.items(.data);
264 const node_tags = tree.nodes.items(.tag);
265
266 const gz = scope.getGenZir();
267
268 switch (node_tags[node]) {
269 .root => unreachable, // Top-level declaration.
270 .@"usingnamespace" => unreachable, // Top-level declaration.
271 .test_decl => unreachable, // Top-level declaration.
272 .container_field_init => unreachable, // Top-level declaration.
273 .container_field_align => unreachable, // Top-level declaration.
274 .container_field => unreachable, // Top-level declaration.
275 .fn_decl => unreachable, // Top-level declaration.
276
277 .global_var_decl => unreachable, // Handled in `blockExpr`.
278 .local_var_decl => unreachable, // Handled in `blockExpr`.
279 .simple_var_decl => unreachable, // Handled in `blockExpr`.
280 .aligned_var_decl => unreachable, // Handled in `blockExpr`.
281
282 .switch_case => unreachable, // Handled in `switchExpr`.
283 .switch_case_one => unreachable, // Handled in `switchExpr`.
284 .switch_range => unreachable, // Handled in `switchExpr`.
285
286 .asm_output => unreachable, // Handled in `asmExpr`.
287 .asm_input => unreachable, // Handled in `asmExpr`.
288
289 .assign => {
290 try assign(mod, scope, node);
291 return rvalue(mod, scope, rl, .void_value, node);
292 },
293 .assign_bit_and => {
294 try assignOp(mod, scope, node, .bit_and);
295 return rvalue(mod, scope, rl, .void_value, node);
296 },
297 .assign_bit_or => {
298 try assignOp(mod, scope, node, .bit_or);
299 return rvalue(mod, scope, rl, .void_value, node);
300 },
301 .assign_bit_shift_left => {
302 try assignOp(mod, scope, node, .shl);
303 return rvalue(mod, scope, rl, .void_value, node);
304 },
305 .assign_bit_shift_right => {
306 try assignOp(mod, scope, node, .shr);
307 return rvalue(mod, scope, rl, .void_value, node);
308 },
309 .assign_bit_xor => {
310 try assignOp(mod, scope, node, .xor);
311 return rvalue(mod, scope, rl, .void_value, node);
312 },
313 .assign_div => {
314 try assignOp(mod, scope, node, .div);
315 return rvalue(mod, scope, rl, .void_value, node);
316 },
317 .assign_sub => {
318 try assignOp(mod, scope, node, .sub);
319 return rvalue(mod, scope, rl, .void_value, node);
320 },
321 .assign_sub_wrap => {
322 try assignOp(mod, scope, node, .subwrap);
323 return rvalue(mod, scope, rl, .void_value, node);
324 },
325 .assign_mod => {
326 try assignOp(mod, scope, node, .mod_rem);
327 return rvalue(mod, scope, rl, .void_value, node);
328 },
329 .assign_add => {
330 try assignOp(mod, scope, node, .add);
331 return rvalue(mod, scope, rl, .void_value, node);
332 },
333 .assign_add_wrap => {
334 try assignOp(mod, scope, node, .addwrap);
335 return rvalue(mod, scope, rl, .void_value, node);
336 },
337 .assign_mul => {
338 try assignOp(mod, scope, node, .mul);
339 return rvalue(mod, scope, rl, .void_value, node);
340 },
341 .assign_mul_wrap => {
342 try assignOp(mod, scope, node, .mulwrap);
343 return rvalue(mod, scope, rl, .void_value, node);
344 },
345
346 .add => return simpleBinOp(mod, scope, rl, node, .add),
347 .add_wrap => return simpleBinOp(mod, scope, rl, node, .addwrap),
348 .sub => return simpleBinOp(mod, scope, rl, node, .sub),
349 .sub_wrap => return simpleBinOp(mod, scope, rl, node, .subwrap),
350 .mul => return simpleBinOp(mod, scope, rl, node, .mul),
351 .mul_wrap => return simpleBinOp(mod, scope, rl, node, .mulwrap),
352 .div => return simpleBinOp(mod, scope, rl, node, .div),
353 .mod => return simpleBinOp(mod, scope, rl, node, .mod_rem),
354 .bit_and => return simpleBinOp(mod, scope, rl, node, .bit_and),
355 .bit_or => return simpleBinOp(mod, scope, rl, node, .bit_or),
356 .bit_shift_left => return simpleBinOp(mod, scope, rl, node, .shl),
357 .bit_shift_right => return simpleBinOp(mod, scope, rl, node, .shr),
358 .bit_xor => return simpleBinOp(mod, scope, rl, node, .xor),
359
360 .bang_equal => return simpleBinOp(mod, scope, rl, node, .cmp_neq),
361 .equal_equal => return simpleBinOp(mod, scope, rl, node, .cmp_eq),
362 .greater_than => return simpleBinOp(mod, scope, rl, node, .cmp_gt),
363 .greater_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_gte),
364 .less_than => return simpleBinOp(mod, scope, rl, node, .cmp_lt),
365 .less_or_equal => return simpleBinOp(mod, scope, rl, node, .cmp_lte),
366
367 .array_cat => return simpleBinOp(mod, scope, rl, node, .array_cat),
368 .array_mult => return simpleBinOp(mod, scope, rl, node, .array_mul),
369
370 .error_union => return simpleBinOp(mod, scope, rl, node, .error_union_type),
371 .merge_error_sets => return simpleBinOp(mod, scope, rl, node, .merge_error_sets),
372
373 .bool_and => return boolBinOp(mod, scope, rl, node, .bool_br_and),
374 .bool_or => return boolBinOp(mod, scope, rl, node, .bool_br_or),
375
376 .bool_not => return boolNot(mod, scope, rl, node),
377 .bit_not => return bitNot(mod, scope, rl, node),
378
379 .negation => return negation(mod, scope, rl, node, .negate),
380 .negation_wrap => return negation(mod, scope, rl, node, .negate_wrap),
381
382 .identifier => return identifier(mod, scope, rl, node),
383
384 .asm_simple => return asmExpr(mod, scope, rl, node, tree.asmSimple(node)),
385 .@"asm" => return asmExpr(mod, scope, rl, node, tree.asmFull(node)),
386
387 .string_literal => return stringLiteral(mod, scope, rl, node),
388 .multiline_string_literal => return multilineStringLiteral(mod, scope, rl, node),
389
390 .integer_literal => return integerLiteral(mod, scope, rl, node),
391
392 .builtin_call_two, .builtin_call_two_comma => {
393 if (node_datas[node].lhs == 0) {
394 const params = [_]ast.Node.Index{};
395 return builtinCall(mod, scope, rl, node, &params);
396 } else if (node_datas[node].rhs == 0) {
397 const params = [_]ast.Node.Index{node_datas[node].lhs};
398 return builtinCall(mod, scope, rl, node, &params);
399 } else {
400 const params = [_]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
401 return builtinCall(mod, scope, rl, node, &params);
402 }
403 },
404 .builtin_call, .builtin_call_comma => {
405 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
406 return builtinCall(mod, scope, rl, node, params);
407 },
408
409 .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
410 var params: [1]ast.Node.Index = undefined;
411 return callExpr(mod, scope, rl, node, tree.callOne(&params, node));
412 },
413 .call, .call_comma, .async_call, .async_call_comma => {
414 return callExpr(mod, scope, rl, node, tree.callFull(node));
415 },
416
417 .unreachable_literal => {
418 _ = try gz.addAsIndex(.{
419 .tag = .@"unreachable",
420 .data = .{ .@"unreachable" = .{
421 .safety = true,
422 .src_node = gz.zir_code.decl.nodeIndexToRelative(node),
423 } },
424 });
425 return zir.Inst.Ref.unreachable_value;
426 },
427 .@"return" => return ret(mod, scope, node),
428 .field_access => return fieldAccess(mod, scope, rl, node),
429 .float_literal => return floatLiteral(mod, scope, rl, node),
430
431 .if_simple => return ifExpr(mod, scope, rl, node, tree.ifSimple(node)),
432 .@"if" => return ifExpr(mod, scope, rl, node, tree.ifFull(node)),
433
434 .while_simple => return whileExpr(mod, scope, rl, node, tree.whileSimple(node)),
435 .while_cont => return whileExpr(mod, scope, rl, node, tree.whileCont(node)),
436 .@"while" => return whileExpr(mod, scope, rl, node, tree.whileFull(node)),
437
438 .for_simple => return forExpr(mod, scope, rl, node, tree.forSimple(node)),
439 .@"for" => return forExpr(mod, scope, rl, node, tree.forFull(node)),
440
441 .slice_open => {
442 const lhs = try expr(mod, scope, .ref, node_datas[node].lhs);
443 const start = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].rhs);
444 const result = try gz.addPlNode(.slice_start, node, zir.Inst.SliceStart{
445 .lhs = lhs,
446 .start = start,
447 });
448 return rvalue(mod, scope, rl, result, node);
449 },
450 .slice => {
451 const lhs = try expr(mod, scope, .ref, node_datas[node].lhs);
452 const extra = tree.extraData(node_datas[node].rhs, ast.Node.Slice);
453 const start = try expr(mod, scope, .{ .ty = .usize_type }, extra.start);
454 const end = try expr(mod, scope, .{ .ty = .usize_type }, extra.end);
455 const result = try gz.addPlNode(.slice_end, node, zir.Inst.SliceEnd{
456 .lhs = lhs,
457 .start = start,
458 .end = end,
459 });
460 return rvalue(mod, scope, rl, result, node);
461 },
462 .slice_sentinel => {
463 const lhs = try expr(mod, scope, .ref, node_datas[node].lhs);
464 const extra = tree.extraData(node_datas[node].rhs, ast.Node.SliceSentinel);
465 const start = try expr(mod, scope, .{ .ty = .usize_type }, extra.start);
466 const end = try expr(mod, scope, .{ .ty = .usize_type }, extra.end);
467 const sentinel = try expr(mod, scope, .{ .ty = .usize_type }, extra.sentinel);
468 const result = try gz.addPlNode(.slice_sentinel, node, zir.Inst.SliceSentinel{
469 .lhs = lhs,
470 .start = start,
471 .end = end,
472 .sentinel = sentinel,
473 });
474 return rvalue(mod, scope, rl, result, node);
475 },
476
477 .deref => {
478 const lhs = try expr(mod, scope, .none, node_datas[node].lhs);
479 const result = try gz.addUnNode(.load, lhs, node);
480 return rvalue(mod, scope, rl, result, node);
481 },
482 .address_of => {
483 const result = try expr(mod, scope, .ref, node_datas[node].lhs);
484 return rvalue(mod, scope, rl, result, node);
485 },
486 .undefined_literal => return rvalue(mod, scope, rl, .undef, node),
487 .true_literal => return rvalue(mod, scope, rl, .bool_true, node),
488 .false_literal => return rvalue(mod, scope, rl, .bool_false, node),
489 .null_literal => return rvalue(mod, scope, rl, .null_value, node),
490 .optional_type => {
491 const operand = try typeExpr(mod, scope, node_datas[node].lhs);
492 const result = try gz.addUnNode(.optional_type, operand, node);
493 return rvalue(mod, scope, rl, result, node);
494 },
495 .unwrap_optional => switch (rl) {
496 .ref => return gz.addUnNode(
497 .optional_payload_safe_ptr,
498 try expr(mod, scope, .ref, node_datas[node].lhs),
499 node,
500 ),
501 else => return rvalue(mod, scope, rl, try gz.addUnNode(
502 .optional_payload_safe,
503 try expr(mod, scope, .none, node_datas[node].lhs),
504 node,
505 ), node),
506 },
507 .block_two, .block_two_semicolon => {
508 const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
509 if (node_datas[node].lhs == 0) {
510 return blockExpr(mod, scope, rl, node, statements[0..0]);
511 } else if (node_datas[node].rhs == 0) {
512 return blockExpr(mod, scope, rl, node, statements[0..1]);
513 } else {
514 return blockExpr(mod, scope, rl, node, statements[0..2]);
515 }
516 },
517 .block, .block_semicolon => {
518 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
519 return blockExpr(mod, scope, rl, node, statements);
520 },
521 .enum_literal => return simpleStrTok(mod, scope, rl, main_tokens[node], node, .enum_literal),
522 .error_value => return simpleStrTok(mod, scope, rl, node_datas[node].rhs, node, .error_value),
523 .anyframe_literal => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
524 .anyframe_type => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
525 .@"catch" => {
526 const catch_token = main_tokens[node];
527 const payload_token: ?ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)
528 catch_token + 2
529 else
530 null;
531 switch (rl) {
532 .ref => return orelseCatchExpr(
533 mod,
534 scope,
535 rl,
536 node,
537 node_datas[node].lhs,
538 .is_err_ptr,
539 .err_union_payload_unsafe_ptr,
540 .err_union_code_ptr,
541 node_datas[node].rhs,
542 payload_token,
543 ),
544 else => return orelseCatchExpr(
545 mod,
546 scope,
547 rl,
548 node,
549 node_datas[node].lhs,
550 .is_err,
551 .err_union_payload_unsafe,
552 .err_union_code,
553 node_datas[node].rhs,
554 payload_token,
555 ),
556 }
557 },
558 .@"orelse" => switch (rl) {
559 .ref => return orelseCatchExpr(
560 mod,
561 scope,
562 rl,
563 node,
564 node_datas[node].lhs,
565 .is_null_ptr,
566 .optional_payload_unsafe_ptr,
567 undefined,
568 node_datas[node].rhs,
569 null,
570 ),
571 else => return orelseCatchExpr(
572 mod,
573 scope,
574 rl,
575 node,
576 node_datas[node].lhs,
577 .is_null,
578 .optional_payload_unsafe,
579 undefined,
580 node_datas[node].rhs,
581 null,
582 ),
583 },
584
585 .ptr_type_aligned => return ptrType(mod, scope, rl, node, tree.ptrTypeAligned(node)),
586 .ptr_type_sentinel => return ptrType(mod, scope, rl, node, tree.ptrTypeSentinel(node)),
587 .ptr_type => return ptrType(mod, scope, rl, node, tree.ptrType(node)),
588 .ptr_type_bit_range => return ptrType(mod, scope, rl, node, tree.ptrTypeBitRange(node)),
589
590 .container_decl,
591 .container_decl_trailing,
592 => return containerDecl(mod, scope, rl, tree.containerDecl(node)),
593 .container_decl_two, .container_decl_two_trailing => {
594 var buffer: [2]ast.Node.Index = undefined;
595 return containerDecl(mod, scope, rl, tree.containerDeclTwo(&buffer, node));
596 },
597 .container_decl_arg,
598 .container_decl_arg_trailing,
599 => return containerDecl(mod, scope, rl, tree.containerDeclArg(node)),
600
601 .tagged_union,
602 .tagged_union_trailing,
603 => return containerDecl(mod, scope, rl, tree.taggedUnion(node)),
604 .tagged_union_two, .tagged_union_two_trailing => {
605 var buffer: [2]ast.Node.Index = undefined;
606 return containerDecl(mod, scope, rl, tree.taggedUnionTwo(&buffer, node));
607 },
608 .tagged_union_enum_tag,
609 .tagged_union_enum_tag_trailing,
610 => return containerDecl(mod, scope, rl, tree.taggedUnionEnumTag(node)),
611
612 .@"break" => return breakExpr(mod, scope, node),
613 .@"continue" => return continueExpr(mod, scope, node),
614 .grouped_expression => return expr(mod, scope, rl, node_datas[node].lhs),
615 .array_type => return arrayType(mod, scope, rl, node),
616 .array_type_sentinel => return arrayTypeSentinel(mod, scope, rl, node),
617 .char_literal => return charLiteral(mod, scope, rl, node),
618 .error_set_decl => return errorSetDecl(mod, scope, rl, node),
619 .array_access => return arrayAccess(mod, scope, rl, node),
620 .@"comptime" => return comptimeExpr(mod, scope, rl, node_datas[node].lhs),
621 .@"switch", .switch_comma => return switchExpr(mod, scope, rl, node),
622
623 .@"nosuspend" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
624 .@"suspend" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
625 .@"await" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
626 .@"resume" => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
627
628 .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}),
629 .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}),
630 .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
631
632 .array_init_one,
633 .array_init_one_comma,
634 .array_init_dot_two,
635 .array_init_dot_two_comma,
636 .array_init_dot,
637 .array_init_dot_comma,
638 .array_init,
639 .array_init_comma,
640 => return mod.failNode(scope, node, "TODO implement astgen.expr for array literals", .{}),
641
642 .struct_init_one,
643 .struct_init_one_comma,
644 .struct_init_dot_two,
645 .struct_init_dot_two_comma,
646 .struct_init_dot,
647 .struct_init_dot_comma,
648 .struct_init,
649 .struct_init_comma,
650 => return mod.failNode(scope, node, "TODO implement astgen.expr for struct literals", .{}),
651
652 .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}),
653 .fn_proto_simple,
654 .fn_proto_multi,
655 .fn_proto_one,
656 .fn_proto,
657 => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}),
658 }
659}
660
661pub fn comptimeExpr(
662 mod: *Module,
663 parent_scope: *Scope,
664 rl: ResultLoc,
665 node: ast.Node.Index,
666) InnerError!zir.Inst.Ref {
667 const gz = parent_scope.getGenZir();
668
669 const prev_force_comptime = gz.force_comptime;
670 gz.force_comptime = true;
671 const result = try expr(mod, parent_scope, rl, node);
672 gz.force_comptime = prev_force_comptime;
673 return result;
674}
675
676fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
677 const parent_gz = parent_scope.getGenZir();
678 const tree = parent_gz.tree();
679 const node_datas = tree.nodes.items(.data);
680 const break_label = node_datas[node].lhs;
681 const rhs = node_datas[node].rhs;
682
683 // Look for the label in the scope.
684 var scope = parent_scope;
685 while (true) {
686 switch (scope.tag) {
687 .gen_zir => {
688 const block_gz = scope.cast(Scope.GenZir).?;
689
690 const block_inst = blk: {
691 if (break_label != 0) {
692 if (block_gz.label) |*label| {
693 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
694 label.used = true;
695 break :blk label.block_inst;
696 }
697 }
698 } else if (block_gz.break_block != 0) {
699 break :blk block_gz.break_block;
700 }
701 scope = block_gz.parent;
702 continue;
703 };
704
705 if (rhs == 0) {
706 _ = try parent_gz.addBreak(.@"break", block_inst, .void_value);
707 return zir.Inst.Ref.unreachable_value;
708 }
709 block_gz.break_count += 1;
710 const prev_rvalue_rl_count = block_gz.rvalue_rl_count;
711 const operand = try expr(mod, parent_scope, block_gz.break_result_loc, rhs);
712 const have_store_to_block = block_gz.rvalue_rl_count != prev_rvalue_rl_count;
713
714 const br = try parent_gz.addBreak(.@"break", block_inst, operand);
715
716 if (block_gz.break_result_loc == .block_ptr) {
717 try block_gz.labeled_breaks.append(mod.gpa, br);
718
719 if (have_store_to_block) {
720 const zir_tags = parent_gz.zir_code.instructions.items(.tag);
721 const zir_datas = parent_gz.zir_code.instructions.items(.data);
722 const store_inst = @intCast(u32, zir_tags.len - 2);
723 assert(zir_tags[store_inst] == .store_to_block_ptr);
724 assert(zir_datas[store_inst].bin.lhs == block_gz.rl_ptr);
725 try block_gz.labeled_store_to_block_ptr_list.append(mod.gpa, store_inst);
726 }
727 }
728 return zir.Inst.Ref.unreachable_value;
729 },
730 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
731 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
732 else => if (break_label != 0) {
733 const label_name = try mod.identifierTokenString(parent_scope, break_label);
734 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
735 } else {
736 return mod.failNode(parent_scope, node, "break expression outside loop", .{});
737 },
738 }
739 }
740}
741
742fn continueExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
743 const parent_gz = parent_scope.getGenZir();
744 const tree = parent_gz.tree();
745 const node_datas = tree.nodes.items(.data);
746 const break_label = node_datas[node].lhs;
747
748 // Look for the label in the scope.
749 var scope = parent_scope;
750 while (true) {
751 switch (scope.tag) {
752 .gen_zir => {
753 const gen_zir = scope.cast(Scope.GenZir).?;
754 const continue_block = gen_zir.continue_block;
755 if (continue_block == 0) {
756 scope = gen_zir.parent;
757 continue;
758 }
759 if (break_label != 0) blk: {
760 if (gen_zir.label) |*label| {
761 if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
762 label.used = true;
763 break :blk;
764 }
765 }
766 // found continue but either it has a different label, or no label
767 scope = gen_zir.parent;
768 continue;
769 }
770
771 // TODO emit a break_inline if the loop being continued is inline
772 _ = try parent_gz.addBreak(.@"break", continue_block, .void_value);
773 return zir.Inst.Ref.unreachable_value;
774 },
775 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
776 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
777 else => if (break_label != 0) {
778 const label_name = try mod.identifierTokenString(parent_scope, break_label);
779 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
780 } else {
781 return mod.failNode(parent_scope, node, "continue expression outside loop", .{});
782 },
783 }
784 }
785}
786
787pub fn blockExpr(
788 mod: *Module,
789 scope: *Scope,
790 rl: ResultLoc,
791 block_node: ast.Node.Index,
792 statements: []const ast.Node.Index,
793) InnerError!zir.Inst.Ref {
794 const tracy = trace(@src());
795 defer tracy.end();
796
797 const tree = scope.tree();
798 const main_tokens = tree.nodes.items(.main_token);
799 const token_tags = tree.tokens.items(.tag);
800
801 const lbrace = main_tokens[block_node];
802 if (token_tags[lbrace - 1] == .colon and
803 token_tags[lbrace - 2] == .identifier)
804 {
805 return labeledBlockExpr(mod, scope, rl, block_node, statements, .block);
806 }
807
808 try blockExprStmts(mod, scope, block_node, statements);
809 return rvalue(mod, scope, rl, .void_value, block_node);
810}
811
812fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void {
813 // Look for the label in the scope.
814 var scope = parent_scope;
815 while (true) {
816 switch (scope.tag) {
817 .gen_zir => {
818 const gen_zir = scope.cast(Scope.GenZir).?;
819 if (gen_zir.label) |prev_label| {
820 if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) {
821 const tree = parent_scope.tree();
822 const main_tokens = tree.nodes.items(.main_token);
823
824 const label_name = try mod.identifierTokenString(parent_scope, label);
825 const msg = msg: {
826 const msg = try mod.errMsg(
827 parent_scope,
828 gen_zir.tokSrcLoc(label),
829 "redefinition of label '{s}'",
830 .{label_name},
831 );
832 errdefer msg.destroy(mod.gpa);
833 try mod.errNote(
834 parent_scope,
835 gen_zir.tokSrcLoc(prev_label.token),
836 msg,
837 "previous definition is here",
838 .{},
839 );
840 break :msg msg;
841 };
842 return mod.failWithOwnedErrorMsg(parent_scope, msg);
843 }
844 }
845 scope = gen_zir.parent;
846 },
847 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
848 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
849 else => return,
850 }
851 }
852}
853
854fn labeledBlockExpr(
855 mod: *Module,
856 parent_scope: *Scope,
857 rl: ResultLoc,
858 block_node: ast.Node.Index,
859 statements: []const ast.Node.Index,
860 zir_tag: zir.Inst.Tag,
861) InnerError!zir.Inst.Ref {
862 const tracy = trace(@src());
863 defer tracy.end();
864
865 assert(zir_tag == .block);
866
867 const tree = parent_scope.tree();
868 const main_tokens = tree.nodes.items(.main_token);
869 const token_tags = tree.tokens.items(.tag);
870
871 const lbrace = main_tokens[block_node];
872 const label_token = lbrace - 2;
873 assert(token_tags[label_token] == .identifier);
874
875 try checkLabelRedefinition(mod, parent_scope, label_token);
876
877 // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
878 // so that break statements can reference it.
879 const gz = parent_scope.getGenZir();
880 const block_inst = try gz.addBlock(zir_tag, block_node);
881 try gz.instructions.append(mod.gpa, block_inst);
882
883 var block_scope: Scope.GenZir = .{
884 .parent = parent_scope,
885 .zir_code = gz.zir_code,
886 .force_comptime = gz.force_comptime,
887 .instructions = .{},
888 // TODO @as here is working around a stage1 miscompilation bug :(
889 .label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{
890 .token = label_token,
891 .block_inst = block_inst,
892 }),
893 };
894 setBlockResultLoc(&block_scope, rl);
895 defer block_scope.instructions.deinit(mod.gpa);
896 defer block_scope.labeled_breaks.deinit(mod.gpa);
897 defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa);
898
899 try blockExprStmts(mod, &block_scope.base, block_node, statements);
900
901 if (!block_scope.label.?.used) {
902 return mod.failTok(parent_scope, label_token, "unused block label", .{});
903 }
904
905 const zir_tags = gz.zir_code.instructions.items(.tag);
906 const zir_datas = gz.zir_code.instructions.items(.data);
907
908 const strat = rlStrategy(rl, &block_scope);
909 switch (strat.tag) {
910 .break_void => {
911 // The code took advantage of the result location as a pointer.
912 // Turn the break instruction operands into void.
913 for (block_scope.labeled_breaks.items) |br| {
914 zir_datas[br].@"break".operand = .void_value;
915 }
916 try block_scope.setBlockBody(block_inst);
917
918 return gz.zir_code.indexToRef(block_inst);
919 },
920 .break_operand => {
921 // All break operands are values that did not use the result location pointer.
922 if (strat.elide_store_to_block_ptr_instructions) {
923 for (block_scope.labeled_store_to_block_ptr_list.items) |inst| {
924 zir_tags[inst] = .elided;
925 zir_datas[inst] = undefined;
926 }
927 // TODO technically not needed since we changed the tag to elided but
928 // would be better still to elide the ones that are in this list.
929 }
930 try block_scope.setBlockBody(block_inst);
931 const block_ref = gz.zir_code.indexToRef(block_inst);
932 switch (rl) {
933 .ref => return block_ref,
934 else => return rvalue(mod, parent_scope, rl, block_ref, block_node),
935 }
936 },
937 }
938}
939
940fn blockExprStmts(
941 mod: *Module,
942 parent_scope: *Scope,
943 node: ast.Node.Index,
944 statements: []const ast.Node.Index,
945) !void {
946 const tree = parent_scope.tree();
947 const main_tokens = tree.nodes.items(.main_token);
948 const node_tags = tree.nodes.items(.tag);
949
950 var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
951 defer block_arena.deinit();
952
953 const gz = parent_scope.getGenZir();
954
955 var scope = parent_scope;
956 for (statements) |statement| {
957 if (!gz.force_comptime) {
958 _ = try gz.addNode(.dbg_stmt_node, statement);
959 }
960 switch (node_tags[statement]) {
961 .global_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.globalVarDecl(statement)),
962 .local_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.localVarDecl(statement)),
963 .simple_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.simpleVarDecl(statement)),
964 .aligned_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.alignedVarDecl(statement)),
965
966 .assign => try assign(mod, scope, statement),
967 .assign_bit_and => try assignOp(mod, scope, statement, .bit_and),
968 .assign_bit_or => try assignOp(mod, scope, statement, .bit_or),
969 .assign_bit_shift_left => try assignOp(mod, scope, statement, .shl),
970 .assign_bit_shift_right => try assignOp(mod, scope, statement, .shr),
971 .assign_bit_xor => try assignOp(mod, scope, statement, .xor),
972 .assign_div => try assignOp(mod, scope, statement, .div),
973 .assign_sub => try assignOp(mod, scope, statement, .sub),
974 .assign_sub_wrap => try assignOp(mod, scope, statement, .subwrap),
975 .assign_mod => try assignOp(mod, scope, statement, .mod_rem),
976 .assign_add => try assignOp(mod, scope, statement, .add),
977 .assign_add_wrap => try assignOp(mod, scope, statement, .addwrap),
978 .assign_mul => try assignOp(mod, scope, statement, .mul),
979 .assign_mul_wrap => try assignOp(mod, scope, statement, .mulwrap),
980
981 else => {
982 // We need to emit an error if the result is not `noreturn` or `void`, but
983 // we want to avoid adding the ZIR instruction if possible for performance.
984 const maybe_unused_result = try expr(mod, scope, .none, statement);
985 const elide_check = if (gz.zir_code.refToIndex(maybe_unused_result)) |inst| b: {
986 // Note that this array becomes invalid after appending more items to it
987 // in the above while loop.
988 const zir_tags = gz.zir_code.instructions.items(.tag);
989 switch (zir_tags[inst]) {
990 .@"const" => {
991 const tv = gz.zir_code.instructions.items(.data)[inst].@"const";
992 break :b switch (tv.ty.zigTypeTag()) {
993 .NoReturn, .Void => true,
994 else => false,
995 };
996 },
997 // For some instructions, swap in a slightly different ZIR tag
998 // so we can avoid a separate ensure_result_used instruction.
999 .call_none_chkused => unreachable,
1000 .call_none => {
1001 zir_tags[inst] = .call_none_chkused;
1002 break :b true;
1003 },
1004 .call_chkused => unreachable,
1005 .call => {
1006 zir_tags[inst] = .call_chkused;
1007 break :b true;
1008 },
1009
1010 // ZIR instructions that might be a type other than `noreturn` or `void`.
1011 .add,
1012 .addwrap,
1013 .alloc,
1014 .alloc_mut,
1015 .alloc_inferred,
1016 .alloc_inferred_mut,
1017 .array_cat,
1018 .array_mul,
1019 .array_type,
1020 .array_type_sentinel,
1021 .indexable_ptr_len,
1022 .as,
1023 .as_node,
1024 .@"asm",
1025 .asm_volatile,
1026 .bit_and,
1027 .bitcast,
1028 .bitcast_ref,
1029 .bitcast_result_ptr,
1030 .bit_or,
1031 .block,
1032 .block_inline,
1033 .loop,
1034 .bool_br_and,
1035 .bool_br_or,
1036 .bool_not,
1037 .bool_and,
1038 .bool_or,
1039 .call_compile_time,
1040 .cmp_lt,
1041 .cmp_lte,
1042 .cmp_eq,
1043 .cmp_gte,
1044 .cmp_gt,
1045 .cmp_neq,
1046 .coerce_result_ptr,
1047 .decl_ref,
1048 .decl_val,
1049 .load,
1050 .div,
1051 .elem_ptr,
1052 .elem_val,
1053 .elem_ptr_node,
1054 .elem_val_node,
1055 .floatcast,
1056 .field_ptr,
1057 .field_val,
1058 .field_ptr_named,
1059 .field_val_named,
1060 .fn_type,
1061 .fn_type_var_args,
1062 .fn_type_cc,
1063 .fn_type_cc_var_args,
1064 .int,
1065 .intcast,
1066 .int_type,
1067 .is_non_null,
1068 .is_null,
1069 .is_non_null_ptr,
1070 .is_null_ptr,
1071 .is_err,
1072 .is_err_ptr,
1073 .mod_rem,
1074 .mul,
1075 .mulwrap,
1076 .param_type,
1077 .ptrtoint,
1078 .ref,
1079 .ret_ptr,
1080 .ret_type,
1081 .shl,
1082 .shr,
1083 .str,
1084 .sub,
1085 .subwrap,
1086 .negate,
1087 .negate_wrap,
1088 .typeof,
1089 .xor,
1090 .optional_type,
1091 .optional_type_from_ptr_elem,
1092 .optional_payload_safe,
1093 .optional_payload_unsafe,
1094 .optional_payload_safe_ptr,
1095 .optional_payload_unsafe_ptr,
1096 .err_union_payload_safe,
1097 .err_union_payload_unsafe,
1098 .err_union_payload_safe_ptr,
1099 .err_union_payload_unsafe_ptr,
1100 .err_union_code,
1101 .err_union_code_ptr,
1102 .ptr_type,
1103 .ptr_type_simple,
1104 .enum_literal,
1105 .enum_literal_small,
1106 .merge_error_sets,
1107 .error_union_type,
1108 .bit_not,
1109 .error_set,
1110 .error_value,
1111 .slice_start,
1112 .slice_end,
1113 .slice_sentinel,
1114 .import,
1115 .typeof_peer,
1116 => break :b false,
1117
1118 // ZIR instructions that are always either `noreturn` or `void`.
1119 .breakpoint,
1120 .dbg_stmt_node,
1121 .ensure_result_used,
1122 .ensure_result_non_error,
1123 .set_eval_branch_quota,
1124 .compile_log,
1125 .ensure_err_payload_void,
1126 .@"break",
1127 .break_inline,
1128 .condbr,
1129 .condbr_inline,
1130 .compile_error,
1131 .ret_node,
1132 .ret_tok,
1133 .ret_coerce,
1134 .@"unreachable",
1135 .elided,
1136 .store,
1137 .store_node,
1138 .store_to_block_ptr,
1139 .store_to_inferred_ptr,
1140 .resolve_inferred_alloc,
1141 .repeat,
1142 .repeat_inline,
1143 => break :b true,
1144 }
1145 } else switch (maybe_unused_result) {
1146 .none => unreachable,
1147
1148 .void_value,
1149 .unreachable_value,
1150 => true,
1151
1152 else => false,
1153 };
1154 if (!elide_check) {
1155 _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
1156 }
1157 },
1158 }
1159 }
1160}
1161
1162fn varDecl(
1163 mod: *Module,
1164 scope: *Scope,
1165 node: ast.Node.Index,
1166 block_arena: *Allocator,
1167 var_decl: ast.full.VarDecl,
1168) InnerError!*Scope {
1169 if (var_decl.comptime_token) |comptime_token| {
1170 return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
1171 }
1172 if (var_decl.ast.align_node != 0) {
1173 return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{});
1174 }
1175 const gz = scope.getGenZir();
1176 const wzc = gz.zir_code;
1177 const tree = scope.tree();
1178 const token_tags = tree.tokens.items(.tag);
1179
1180 const name_token = var_decl.ast.mut_token + 1;
1181 const name_src = gz.tokSrcLoc(name_token);
1182 const ident_name = try mod.identifierTokenString(scope, name_token);
1183
1184 // Local variables shadowing detection, including function parameters.
1185 {
1186 var s = scope;
1187 while (true) switch (s.tag) {
1188 .local_val => {
1189 const local_val = s.cast(Scope.LocalVal).?;
1190 if (mem.eql(u8, local_val.name, ident_name)) {
1191 const msg = msg: {
1192 const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
1193 ident_name,
1194 });
1195 errdefer msg.destroy(mod.gpa);
1196 try mod.errNote(scope, local_val.src, msg, "previous definition is here", .{});
1197 break :msg msg;
1198 };
1199 return mod.failWithOwnedErrorMsg(scope, msg);
1200 }
1201 s = local_val.parent;
1202 },
1203 .local_ptr => {
1204 const local_ptr = s.cast(Scope.LocalPtr).?;
1205 if (mem.eql(u8, local_ptr.name, ident_name)) {
1206 const msg = msg: {
1207 const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
1208 ident_name,
1209 });
1210 errdefer msg.destroy(mod.gpa);
1211 try mod.errNote(scope, local_ptr.src, msg, "previous definition is here", .{});
1212 break :msg msg;
1213 };
1214 return mod.failWithOwnedErrorMsg(scope, msg);
1215 }
1216 s = local_ptr.parent;
1217 },
1218 .gen_zir => s = s.cast(Scope.GenZir).?.parent,
1219 else => break,
1220 };
1221 }
1222
1223 // Namespace vars shadowing detection
1224 if (mod.lookupDeclName(scope, ident_name)) |_| {
1225 // TODO add note for other definition
1226 return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});
1227 }
1228 if (var_decl.ast.init_node == 0) {
1229 return mod.fail(scope, name_src, "variables must be initialized", .{});
1230 }
1231
1232 switch (token_tags[var_decl.ast.mut_token]) {
1233 .keyword_const => {
1234 // Depending on the type of AST the initialization expression is, we may need an lvalue
1235 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
1236 // the variable, no memory location needed.
1237 if (!nodeMayNeedMemoryLocation(scope, var_decl.ast.init_node)) {
1238 const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{
1239 .ty = try typeExpr(mod, scope, var_decl.ast.type_node),
1240 } else .none;
1241 const init_inst = try expr(mod, scope, result_loc, var_decl.ast.init_node);
1242 const sub_scope = try block_arena.create(Scope.LocalVal);
1243 sub_scope.* = .{
1244 .parent = scope,
1245 .gen_zir = gz,
1246 .name = ident_name,
1247 .inst = init_inst,
1248 .src = name_src,
1249 };
1250 return &sub_scope.base;
1251 }
1252
1253 // Detect whether the initialization expression actually uses the
1254 // result location pointer.
1255 var init_scope: Scope.GenZir = .{
1256 .parent = scope,
1257 .force_comptime = gz.force_comptime,
1258 .zir_code = wzc,
1259 };
1260 defer init_scope.instructions.deinit(mod.gpa);
1261
1262 var resolve_inferred_alloc: zir.Inst.Ref = .none;
1263 var opt_type_inst: zir.Inst.Ref = .none;
1264 if (var_decl.ast.type_node != 0) {
1265 const type_inst = try typeExpr(mod, &init_scope.base, var_decl.ast.type_node);
1266 opt_type_inst = type_inst;
1267 init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node);
1268 } else {
1269 const alloc = try init_scope.addUnNode(.alloc_inferred, undefined, node);
1270 resolve_inferred_alloc = alloc;
1271 init_scope.rl_ptr = alloc;
1272 }
1273 const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };
1274 const init_inst = try expr(mod, &init_scope.base, init_result_loc, var_decl.ast.init_node);
1275 const zir_tags = wzc.instructions.items(.tag);
1276 const zir_datas = wzc.instructions.items(.data);
1277
1278 const parent_zir = &gz.instructions;
1279 if (init_scope.rvalue_rl_count == 1) {
1280 // Result location pointer not used. We don't need an alloc for this
1281 // const local, and type inference becomes trivial.
1282 // Move the init_scope instructions into the parent scope, eliding
1283 // the alloc instruction and the store_to_block_ptr instruction.
1284 const expected_len = parent_zir.items.len + init_scope.instructions.items.len - 2;
1285 try parent_zir.ensureCapacity(mod.gpa, expected_len);
1286 for (init_scope.instructions.items) |src_inst| {
1287 if (wzc.indexToRef(src_inst) == init_scope.rl_ptr) continue;
1288 if (zir_tags[src_inst] == .store_to_block_ptr) {
1289 if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue;
1290 }
1291 parent_zir.appendAssumeCapacity(src_inst);
1292 }
1293 assert(parent_zir.items.len == expected_len);
1294 const casted_init = if (opt_type_inst != .none)
1295 try gz.addPlNode(.as_node, var_decl.ast.type_node, zir.Inst.As{
1296 .dest_type = opt_type_inst,
1297 .operand = init_inst,
1298 })
1299 else
1300 init_inst;
1301
1302 const sub_scope = try block_arena.create(Scope.LocalVal);
1303 sub_scope.* = .{
1304 .parent = scope,
1305 .gen_zir = gz,
1306 .name = ident_name,
1307 .inst = casted_init,
1308 .src = name_src,
1309 };
1310 return &sub_scope.base;
1311 }
1312 // The initialization expression took advantage of the result location
1313 // of the const local. In this case we will create an alloc and a LocalPtr for it.
1314 // Move the init_scope instructions into the parent scope, swapping
1315 // store_to_block_ptr for store_to_inferred_ptr.
1316 const expected_len = parent_zir.items.len + init_scope.instructions.items.len;
1317 try parent_zir.ensureCapacity(mod.gpa, expected_len);
1318 for (init_scope.instructions.items) |src_inst| {
1319 if (zir_tags[src_inst] == .store_to_block_ptr) {
1320 if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) {
1321 zir_tags[src_inst] = .store_to_inferred_ptr;
1322 }
1323 }
1324 parent_zir.appendAssumeCapacity(src_inst);
1325 }
1326 assert(parent_zir.items.len == expected_len);
1327 if (resolve_inferred_alloc != .none) {
1328 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
1329 }
1330 const sub_scope = try block_arena.create(Scope.LocalPtr);
1331 sub_scope.* = .{
1332 .parent = scope,
1333 .gen_zir = gz,
1334 .name = ident_name,
1335 .ptr = init_scope.rl_ptr,
1336 .src = name_src,
1337 };
1338 return &sub_scope.base;
1339 },
1340 .keyword_var => {
1341 var resolve_inferred_alloc: zir.Inst.Ref = .none;
1342 const var_data: struct {
1343 result_loc: ResultLoc,
1344 alloc: zir.Inst.Ref,
1345 } = if (var_decl.ast.type_node != 0) a: {
1346 const type_inst = try typeExpr(mod, scope, var_decl.ast.type_node);
1347
1348 const alloc = try gz.addUnNode(.alloc_mut, type_inst, node);
1349 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
1350 } else a: {
1351 const alloc = try gz.addUnNode(.alloc_inferred_mut, undefined, node);
1352 resolve_inferred_alloc = alloc;
1353 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } };
1354 };
1355 const init_inst = try expr(mod, scope, var_data.result_loc, var_decl.ast.init_node);
1356 if (resolve_inferred_alloc != .none) {
1357 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
1358 }
1359 const sub_scope = try block_arena.create(Scope.LocalPtr);
1360 sub_scope.* = .{
1361 .parent = scope,
1362 .gen_zir = gz,
1363 .name = ident_name,
1364 .ptr = var_data.alloc,
1365 .src = name_src,
1366 };
1367 return &sub_scope.base;
1368 },
1369 else => unreachable,
1370 }
1371}
1372
1373fn assign(mod: *Module, scope: *Scope, infix_node: ast.Node.Index) InnerError!void {
1374 const tree = scope.tree();
1375 const node_datas = tree.nodes.items(.data);
1376 const main_tokens = tree.nodes.items(.main_token);
1377 const node_tags = tree.nodes.items(.tag);
1378
1379 const lhs = node_datas[infix_node].lhs;
1380 const rhs = node_datas[infix_node].rhs;
1381 if (node_tags[lhs] == .identifier) {
1382 // This intentionally does not support `@"_"` syntax.
1383 const ident_name = tree.tokenSlice(main_tokens[lhs]);
1384 if (mem.eql(u8, ident_name, "_")) {
1385 _ = try expr(mod, scope, .discard, rhs);
1386 return;
1387 }
1388 }
1389 const lvalue = try lvalExpr(mod, scope, lhs);
1390 _ = try expr(mod, scope, .{ .ptr = lvalue }, rhs);
1391}
1392
1393fn assignOp(
1394 mod: *Module,
1395 scope: *Scope,
1396 infix_node: ast.Node.Index,
1397 op_inst_tag: zir.Inst.Tag,
1398) InnerError!void {
1399 const tree = scope.tree();
1400 const node_datas = tree.nodes.items(.data);
1401 const gz = scope.getGenZir();
1402
1403 const lhs_ptr = try lvalExpr(mod, scope, node_datas[infix_node].lhs);
1404 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
1405 const lhs_type = try gz.addUnTok(.typeof, lhs, infix_node);
1406 const rhs = try expr(mod, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);
1407
1408 const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{
1409 .lhs = lhs,
1410 .rhs = rhs,
1411 });
1412 _ = try gz.addBin(.store, lhs_ptr, result);
1413}
1414
1415fn boolNot(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref {
1416 const tree = scope.tree();
1417 const node_datas = tree.nodes.items(.data);
1418
1419 const operand = try expr(mod, scope, .{ .ty = .bool_type }, node_datas[node].lhs);
1420 const gz = scope.getGenZir();
1421 const result = try gz.addUnNode(.bool_not, operand, node);
1422 return rvalue(mod, scope, rl, result, node);
1423}
1424
1425fn bitNot(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!zir.Inst.Ref {
1426 const tree = scope.tree();
1427 const node_datas = tree.nodes.items(.data);
1428
1429 const gz = scope.getGenZir();
1430 const operand = try expr(mod, scope, .none, node_datas[node].lhs);
1431 const result = try gz.addUnNode(.bit_not, operand, node);
1432 return rvalue(mod, scope, rl, result, node);
1433}
1434
1435fn negation(
1436 mod: *Module,
1437 scope: *Scope,
1438 rl: ResultLoc,
1439 node: ast.Node.Index,
1440 tag: zir.Inst.Tag,
1441) InnerError!zir.Inst.Ref {
1442 const tree = scope.tree();
1443 const node_datas = tree.nodes.items(.data);
1444
1445 const gz = scope.getGenZir();
1446 const operand = try expr(mod, scope, .none, node_datas[node].lhs);
1447 const result = try gz.addUnNode(tag, operand, node);
1448 return rvalue(mod, scope, rl, result, node);
1449}
1450
1451fn ptrType(
1452 mod: *Module,
1453 scope: *Scope,
1454 rl: ResultLoc,
1455 node: ast.Node.Index,
1456 ptr_info: ast.full.PtrType,
1457) InnerError!zir.Inst.Ref {
1458 const tree = scope.tree();
1459 const gz = scope.getGenZir();
1460
1461 const elem_type = try typeExpr(mod, scope, ptr_info.ast.child_type);
1462
1463 const simple = ptr_info.ast.align_node == 0 and
1464 ptr_info.ast.sentinel == 0 and
1465 ptr_info.ast.bit_range_start == 0;
1466
1467 if (simple) {
1468 const result = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
1469 .ptr_type_simple = .{
1470 .is_allowzero = ptr_info.allowzero_token != null,
1471 .is_mutable = ptr_info.const_token == null,
1472 .is_volatile = ptr_info.volatile_token != null,
1473 .size = ptr_info.size,
1474 .elem_type = elem_type,
1475 },
1476 } });
1477 return rvalue(mod, scope, rl, result, node);
1478 }
1479
1480 var sentinel_ref: zir.Inst.Ref = .none;
1481 var align_ref: zir.Inst.Ref = .none;
1482 var bit_start_ref: zir.Inst.Ref = .none;
1483 var bit_end_ref: zir.Inst.Ref = .none;
1484 var trailing_count: u32 = 0;
1485
1486 if (ptr_info.ast.sentinel != 0) {
1487 sentinel_ref = try expr(mod, scope, .{ .ty = elem_type }, ptr_info.ast.sentinel);
1488 trailing_count += 1;
1489 }
1490 if (ptr_info.ast.align_node != 0) {
1491 align_ref = try expr(mod, scope, .none, ptr_info.ast.align_node);
1492 trailing_count += 1;
1493 }
1494 if (ptr_info.ast.bit_range_start != 0) {
1495 assert(ptr_info.ast.bit_range_end != 0);
1496 bit_start_ref = try expr(mod, scope, .none, ptr_info.ast.bit_range_start);
1497 bit_end_ref = try expr(mod, scope, .none, ptr_info.ast.bit_range_end);
1498 trailing_count += 2;
1499 }
1500
1501 const gpa = gz.zir_code.gpa;
1502 try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
1503 try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1);
1504 try gz.zir_code.extra.ensureCapacity(gpa, gz.zir_code.extra.items.len +
1505 @typeInfo(zir.Inst.PtrType).Struct.fields.len + trailing_count);
1506
1507 const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.PtrType{ .elem_type = elem_type });
1508 if (sentinel_ref != .none) {
1509 gz.zir_code.extra.appendAssumeCapacity(@enumToInt(sentinel_ref));
1510 }
1511 if (align_ref != .none) {
1512 gz.zir_code.extra.appendAssumeCapacity(@enumToInt(align_ref));
1513 }
1514 if (bit_start_ref != .none) {
1515 gz.zir_code.extra.appendAssumeCapacity(@enumToInt(bit_start_ref));
1516 gz.zir_code.extra.appendAssumeCapacity(@enumToInt(bit_end_ref));
1517 }
1518
1519 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
1520 const result = gz.zir_code.indexToRef(new_index);
1521 gz.zir_code.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
1522 .ptr_type = .{
1523 .flags = .{
1524 .is_allowzero = ptr_info.allowzero_token != null,
1525 .is_mutable = ptr_info.const_token == null,
1526 .is_volatile = ptr_info.volatile_token != null,
1527 .has_sentinel = sentinel_ref != .none,
1528 .has_align = align_ref != .none,
1529 .has_bit_range = bit_start_ref != .none,
1530 },
1531 .size = ptr_info.size,
1532 .payload_index = payload_index,
1533 },
1534 } });
1535 gz.instructions.appendAssumeCapacity(new_index);
1536
1537 return rvalue(mod, scope, rl, result, node);
1538}
1539
1540fn arrayType(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref {
1541 const tree = scope.tree();
1542 const node_datas = tree.nodes.items(.data);
1543 const gz = scope.getGenZir();
1544
1545 // TODO check for [_]T
1546 const len = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].lhs);
1547 const elem_type = try typeExpr(mod, scope, node_datas[node].rhs);
1548
1549 const result = try gz.addBin(.array_type, len, elem_type);
1550 return rvalue(mod, scope, rl, result, node);
1551}
1552
1553fn arrayTypeSentinel(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref {
1554 const tree = scope.tree();
1555 const node_datas = tree.nodes.items(.data);
1556 const extra = tree.extraData(node_datas[node].rhs, ast.Node.ArrayTypeSentinel);
1557 const gz = scope.getGenZir();
1558
1559 // TODO check for [_]T
1560 const len = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].lhs);
1561 const elem_type = try typeExpr(mod, scope, extra.elem_type);
1562 const sentinel = try expr(mod, scope, .{ .ty = elem_type }, extra.sentinel);
1563
1564 const result = try gz.addArrayTypeSentinel(len, elem_type, sentinel);
1565 return rvalue(mod, scope, rl, result, node);
1566}
1567
1568fn containerDecl(
1569 mod: *Module,
1570 scope: *Scope,
1571 rl: ResultLoc,
1572 container_decl: ast.full.ContainerDecl,
1573) InnerError!zir.Inst.Ref {
1574 return mod.failTok(scope, container_decl.ast.main_token, "TODO implement container decls", .{});
1575}
1576
1577fn errorSetDecl(
1578 mod: *Module,
1579 scope: *Scope,
1580 rl: ResultLoc,
1581 node: ast.Node.Index,
1582) InnerError!zir.Inst.Ref {
1583 if (true) @panic("TODO update for zir-memory-layout branch");
1584 const gz = scope.getGenZir();
1585 const tree = gz.tree();
1586 const main_tokens = tree.nodes.items(.main_token);
1587 const token_tags = tree.tokens.items(.tag);
1588
1589 // Count how many fields there are.
1590 const error_token = main_tokens[node];
1591 const count: usize = count: {
1592 var tok_i = error_token + 2;
1593 var count: usize = 0;
1594 while (true) : (tok_i += 1) {
1595 switch (token_tags[tok_i]) {
1596 .doc_comment, .comma => {},
1597 .identifier => count += 1,
1598 .r_brace => break :count count,
1599 else => unreachable,
1600 }
1601 } else unreachable; // TODO should not need else unreachable here
1602 };
1603
1604 const fields = try scope.arena().alloc([]const u8, count);
1605 {
1606 var tok_i = error_token + 2;
1607 var field_i: usize = 0;
1608 while (true) : (tok_i += 1) {
1609 switch (token_tags[tok_i]) {
1610 .doc_comment, .comma => {},
1611 .identifier => {
1612 fields[field_i] = try mod.identifierTokenString(scope, tok_i);
1613 field_i += 1;
1614 },
1615 .r_brace => break,
1616 else => unreachable,
1617 }
1618 }
1619 }
1620 const result = try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{});
1621 return rvalue(mod, scope, rl, result);
1622}
1623
1624fn orelseCatchExpr(
1625 mod: *Module,
1626 scope: *Scope,
1627 rl: ResultLoc,
1628 node: ast.Node.Index,
1629 lhs: ast.Node.Index,
1630 cond_op: zir.Inst.Tag,
1631 unwrap_op: zir.Inst.Tag,
1632 unwrap_code_op: zir.Inst.Tag,
1633 rhs: ast.Node.Index,
1634 payload_token: ?ast.TokenIndex,
1635) InnerError!zir.Inst.Ref {
1636 const parent_gz = scope.getGenZir();
1637 const tree = parent_gz.tree();
1638
1639 var block_scope: Scope.GenZir = .{
1640 .parent = scope,
1641 .zir_code = parent_gz.zir_code,
1642 .force_comptime = parent_gz.force_comptime,
1643 .instructions = .{},
1644 };
1645 setBlockResultLoc(&block_scope, rl);
1646 defer block_scope.instructions.deinit(mod.gpa);
1647
1648 // This could be a pointer or value depending on the `operand_rl` parameter.
1649 // We cannot use `block_scope.break_result_loc` because that has the bare
1650 // type, whereas this expression has the optional type. Later we make
1651 // up for this fact by calling rvalue on the else branch.
1652 block_scope.break_count += 1;
1653
1654 // TODO handle catch
1655 const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
1656 .ref => .ref,
1657 .discard, .none, .block_ptr, .inferred_ptr, .bitcasted_ptr => .none,
1658 .ty => |elem_ty| blk: {
1659 const wrapped_ty = try block_scope.addUnNode(.optional_type, elem_ty, node);
1660 break :blk .{ .ty = wrapped_ty };
1661 },
1662 .ptr => |ptr_ty| blk: {
1663 const wrapped_ty = try block_scope.addUnNode(.optional_type_from_ptr_elem, ptr_ty, node);
1664 break :blk .{ .ty = wrapped_ty };
1665 },
1666 };
1667 const operand = try expr(mod, &block_scope.base, operand_rl, lhs);
1668 const cond = try block_scope.addUnNode(cond_op, operand, node);
1669 const condbr = try block_scope.addCondBr(.condbr, node);
1670
1671 const block = try parent_gz.addBlock(.block, node);
1672 try parent_gz.instructions.append(mod.gpa, block);
1673 try block_scope.setBlockBody(block);
1674
1675 var then_scope: Scope.GenZir = .{
1676 .parent = scope,
1677 .zir_code = parent_gz.zir_code,
1678 .force_comptime = block_scope.force_comptime,
1679 .instructions = .{},
1680 };
1681 defer then_scope.instructions.deinit(mod.gpa);
1682
1683 var err_val_scope: Scope.LocalVal = undefined;
1684 const then_sub_scope = blk: {
1685 const payload = payload_token orelse break :blk &then_scope.base;
1686 if (mem.eql(u8, tree.tokenSlice(payload), "_")) {
1687 return mod.failTok(&then_scope.base, payload, "discard of error capture; omit it instead", .{});
1688 }
1689 const err_name = try mod.identifierTokenString(scope, payload);
1690 err_val_scope = .{
1691 .parent = &then_scope.base,
1692 .gen_zir = &then_scope,
1693 .name = err_name,
1694 .inst = try then_scope.addUnNode(unwrap_code_op, operand, node),
1695 .src = parent_gz.tokSrcLoc(payload),
1696 };
1697 break :blk &err_val_scope.base;
1698 };
1699
1700 block_scope.break_count += 1;
1701 const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, rhs);
1702 // We hold off on the break instructions as well as copying the then/else
1703 // instructions into place until we know whether to keep store_to_block_ptr
1704 // instructions or not.
1705
1706 var else_scope: Scope.GenZir = .{
1707 .parent = scope,
1708 .zir_code = parent_gz.zir_code,
1709 .force_comptime = block_scope.force_comptime,
1710 .instructions = .{},
1711 };
1712 defer else_scope.instructions.deinit(mod.gpa);
1713
1714 // This could be a pointer or value depending on `unwrap_op`.
1715 const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node);
1716 const else_result = switch (rl) {
1717 .ref => unwrapped_payload,
1718 else => try rvalue(mod, &else_scope.base, block_scope.break_result_loc, unwrapped_payload, node),
1719 };
1720
1721 return finishThenElseBlock(
1722 mod,
1723 scope,
1724 rl,
1725 node,
1726 &block_scope,
1727 &then_scope,
1728 &else_scope,
1729 condbr,
1730 cond,
1731 node,
1732 node,
1733 then_result,
1734 else_result,
1735 block,
1736 block,
1737 .@"break",
1738 );
1739}
1740
1741fn finishThenElseBlock(
1742 mod: *Module,
1743 parent_scope: *Scope,
1744 rl: ResultLoc,
1745 node: ast.Node.Index,
1746 block_scope: *Scope.GenZir,
1747 then_scope: *Scope.GenZir,
1748 else_scope: *Scope.GenZir,
1749 condbr: zir.Inst.Index,
1750 cond: zir.Inst.Ref,
1751 then_src: ast.Node.Index,
1752 else_src: ast.Node.Index,
1753 then_result: zir.Inst.Ref,
1754 else_result: zir.Inst.Ref,
1755 main_block: zir.Inst.Index,
1756 then_break_block: zir.Inst.Index,
1757 break_tag: zir.Inst.Tag,
1758) InnerError!zir.Inst.Ref {
1759 // We now have enough information to decide whether the result instruction should
1760 // be communicated via result location pointer or break instructions.
1761 const strat = rlStrategy(rl, block_scope);
1762 const wzc = block_scope.zir_code;
1763 switch (strat.tag) {
1764 .break_void => {
1765 if (!wzc.refIsNoReturn(then_result)) {
1766 _ = try then_scope.addBreak(break_tag, then_break_block, .void_value);
1767 }
1768 const elide_else = if (else_result != .none) wzc.refIsNoReturn(else_result) else false;
1769 if (!elide_else) {
1770 _ = try else_scope.addBreak(break_tag, main_block, .void_value);
1771 }
1772 assert(!strat.elide_store_to_block_ptr_instructions);
1773 try setCondBrPayload(condbr, cond, then_scope, else_scope);
1774 return wzc.indexToRef(main_block);
1775 },
1776 .break_operand => {
1777 if (!wzc.refIsNoReturn(then_result)) {
1778 _ = try then_scope.addBreak(break_tag, then_break_block, then_result);
1779 }
1780 if (else_result != .none) {
1781 if (!wzc.refIsNoReturn(else_result)) {
1782 _ = try else_scope.addBreak(break_tag, main_block, else_result);
1783 }
1784 } else {
1785 _ = try else_scope.addBreak(break_tag, main_block, .void_value);
1786 }
1787 if (strat.elide_store_to_block_ptr_instructions) {
1788 try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope);
1789 } else {
1790 try setCondBrPayload(condbr, cond, then_scope, else_scope);
1791 }
1792 const block_ref = wzc.indexToRef(main_block);
1793 switch (rl) {
1794 .ref => return block_ref,
1795 else => return rvalue(mod, parent_scope, rl, block_ref, node),
1796 }
1797 },
1798 }
1799}
1800
1801/// Return whether the identifier names of two tokens are equal. Resolves @""
1802/// tokens without allocating.
1803/// OK in theory it could do it without allocating. This implementation
1804/// allocates when the @"" form is used.
1805fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
1806 const ident_name_1 = try mod.identifierTokenString(scope, token1);
1807 const ident_name_2 = try mod.identifierTokenString(scope, token2);
1808 return mem.eql(u8, ident_name_1, ident_name_2);
1809}
1810
1811pub fn fieldAccess(
1812 mod: *Module,
1813 scope: *Scope,
1814 rl: ResultLoc,
1815 node: ast.Node.Index,
1816) InnerError!zir.Inst.Ref {
1817 const gz = scope.getGenZir();
1818 const tree = gz.tree();
1819 const main_tokens = tree.nodes.items(.main_token);
1820 const node_datas = tree.nodes.items(.data);
1821 const object_node = node_datas[node].lhs;
1822 const dot_token = main_tokens[node];
1823 const field_ident = dot_token + 1;
1824 const string_bytes = &gz.zir_code.string_bytes;
1825 const str_index = @intCast(u32, string_bytes.items.len);
1826 try mod.appendIdentStr(scope, field_ident, string_bytes);
1827 try string_bytes.append(mod.gpa, 0);
1828 switch (rl) {
1829 .ref => return gz.addPlNode(.field_ptr, node, zir.Inst.Field{
1830 .lhs = try expr(mod, scope, .ref, object_node),
1831 .field_name_start = str_index,
1832 }),
1833 else => return rvalue(mod, scope, rl, try gz.addPlNode(.field_val, node, zir.Inst.Field{
1834 .lhs = try expr(mod, scope, .none, object_node),
1835 .field_name_start = str_index,
1836 }), node),
1837 }
1838}
1839
1840fn arrayAccess(
1841 mod: *Module,
1842 scope: *Scope,
1843 rl: ResultLoc,
1844 node: ast.Node.Index,
1845) InnerError!zir.Inst.Ref {
1846 const gz = scope.getGenZir();
1847 const tree = gz.tree();
1848 const main_tokens = tree.nodes.items(.main_token);
1849 const node_datas = tree.nodes.items(.data);
1850 switch (rl) {
1851 .ref => return gz.addBin(
1852 .elem_ptr,
1853 try expr(mod, scope, .ref, node_datas[node].lhs),
1854 try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
1855 ),
1856 else => return rvalue(mod, scope, rl, try gz.addBin(
1857 .elem_val,
1858 try expr(mod, scope, .none, node_datas[node].lhs),
1859 try expr(mod, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
1860 ), node),
1861 }
1862}
1863
1864fn simpleBinOp(
1865 mod: *Module,
1866 scope: *Scope,
1867 rl: ResultLoc,
1868 node: ast.Node.Index,
1869 op_inst_tag: zir.Inst.Tag,
1870) InnerError!zir.Inst.Ref {
1871 const gz = scope.getGenZir();
1872 const tree = gz.tree();
1873 const node_datas = tree.nodes.items(.data);
1874
1875 const result = try gz.addPlNode(op_inst_tag, node, zir.Inst.Bin{
1876 .lhs = try expr(mod, scope, .none, node_datas[node].lhs),
1877 .rhs = try expr(mod, scope, .none, node_datas[node].rhs),
1878 });
1879 return rvalue(mod, scope, rl, result, node);
1880}
1881
1882fn simpleStrTok(
1883 mod: *Module,
1884 scope: *Scope,
1885 rl: ResultLoc,
1886 ident_token: ast.TokenIndex,
1887 node: ast.Node.Index,
1888 op_inst_tag: zir.Inst.Tag,
1889) InnerError!zir.Inst.Ref {
1890 const gz = scope.getGenZir();
1891 const string_bytes = &gz.zir_code.string_bytes;
1892 const str_index = @intCast(u32, string_bytes.items.len);
1893 try mod.appendIdentStr(scope, ident_token, string_bytes);
1894 try string_bytes.append(mod.gpa, 0);
1895 const result = try gz.addStrTok(op_inst_tag, str_index, ident_token);
1896 return rvalue(mod, scope, rl, result, node);
1897}
1898
1899fn boolBinOp(
1900 mod: *Module,
1901 scope: *Scope,
1902 rl: ResultLoc,
1903 node: ast.Node.Index,
1904 zir_tag: zir.Inst.Tag,
1905) InnerError!zir.Inst.Ref {
1906 const gz = scope.getGenZir();
1907 const node_datas = gz.tree().nodes.items(.data);
1908
1909 const lhs = try expr(mod, scope, .{ .ty = .bool_type }, node_datas[node].lhs);
1910 const bool_br = try gz.addBoolBr(zir_tag, lhs);
1911
1912 var rhs_scope: Scope.GenZir = .{
1913 .parent = scope,
1914 .zir_code = gz.zir_code,
1915 .force_comptime = gz.force_comptime,
1916 };
1917 defer rhs_scope.instructions.deinit(mod.gpa);
1918 const rhs = try expr(mod, &rhs_scope.base, .{ .ty = .bool_type }, node_datas[node].rhs);
1919 _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
1920 try rhs_scope.setBoolBrBody(bool_br);
1921
1922 const block_ref = gz.zir_code.indexToRef(bool_br);
1923 return rvalue(mod, scope, rl, block_ref, node);
1924}
1925
1926fn ifExpr(
1927 mod: *Module,
1928 scope: *Scope,
1929 rl: ResultLoc,
1930 node: ast.Node.Index,
1931 if_full: ast.full.If,
1932) InnerError!zir.Inst.Ref {
1933 const parent_gz = scope.getGenZir();
1934 var block_scope: Scope.GenZir = .{
1935 .parent = scope,
1936 .zir_code = parent_gz.zir_code,
1937 .force_comptime = parent_gz.force_comptime,
1938 .instructions = .{},
1939 };
1940 setBlockResultLoc(&block_scope, rl);
1941 defer block_scope.instructions.deinit(mod.gpa);
1942
1943 const cond = c: {
1944 // TODO https://github.com/ziglang/zig/issues/7929
1945 if (if_full.error_token) |error_token| {
1946 return mod.failTok(scope, error_token, "TODO implement if error union", .{});
1947 } else if (if_full.payload_token) |payload_token| {
1948 return mod.failTok(scope, payload_token, "TODO implement if optional", .{});
1949 } else {
1950 break :c try expr(mod, &block_scope.base, .{ .ty = .bool_type }, if_full.ast.cond_expr);
1951 }
1952 };
1953
1954 const condbr = try block_scope.addCondBr(.condbr, node);
1955
1956 const block = try parent_gz.addBlock(.block, node);
1957 try parent_gz.instructions.append(mod.gpa, block);
1958 try block_scope.setBlockBody(block);
1959
1960 var then_scope: Scope.GenZir = .{
1961 .parent = scope,
1962 .zir_code = parent_gz.zir_code,
1963 .force_comptime = block_scope.force_comptime,
1964 .instructions = .{},
1965 };
1966 defer then_scope.instructions.deinit(mod.gpa);
1967
1968 // declare payload to the then_scope
1969 const then_sub_scope = &then_scope.base;
1970
1971 block_scope.break_count += 1;
1972 const then_result = try expr(mod, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr);
1973 // We hold off on the break instructions as well as copying the then/else
1974 // instructions into place until we know whether to keep store_to_block_ptr
1975 // instructions or not.
1976
1977 var else_scope: Scope.GenZir = .{
1978 .parent = scope,
1979 .zir_code = parent_gz.zir_code,
1980 .force_comptime = block_scope.force_comptime,
1981 .instructions = .{},
1982 };
1983 defer else_scope.instructions.deinit(mod.gpa);
1984
1985 const else_node = if_full.ast.else_expr;
1986 const else_info: struct {
1987 src: ast.Node.Index,
1988 result: zir.Inst.Ref,
1989 } = if (else_node != 0) blk: {
1990 block_scope.break_count += 1;
1991 const sub_scope = &else_scope.base;
1992 break :blk .{
1993 .src = else_node,
1994 .result = try expr(mod, sub_scope, block_scope.break_result_loc, else_node),
1995 };
1996 } else .{
1997 .src = if_full.ast.then_expr,
1998 .result = .none,
1999 };
2000
2001 return finishThenElseBlock(
2002 mod,
2003 scope,
2004 rl,
2005 node,
2006 &block_scope,
2007 &then_scope,
2008 &else_scope,
2009 condbr,
2010 cond,
2011 if_full.ast.then_expr,
2012 else_info.src,
2013 then_result,
2014 else_info.result,
2015 block,
2016 block,
2017 .@"break",
2018 );
2019}
2020
2021fn setCondBrPayload(
2022 condbr: zir.Inst.Index,
2023 cond: zir.Inst.Ref,
2024 then_scope: *Scope.GenZir,
2025 else_scope: *Scope.GenZir,
2026) !void {
2027 const wzc = then_scope.zir_code;
2028
2029 try wzc.extra.ensureCapacity(wzc.gpa, wzc.extra.items.len +
2030 @typeInfo(zir.Inst.CondBr).Struct.fields.len +
2031 then_scope.instructions.items.len + else_scope.instructions.items.len);
2032
2033 const zir_datas = wzc.instructions.items(.data);
2034 zir_datas[condbr].pl_node.payload_index = wzc.addExtraAssumeCapacity(zir.Inst.CondBr{
2035 .condition = cond,
2036 .then_body_len = @intCast(u32, then_scope.instructions.items.len),
2037 .else_body_len = @intCast(u32, else_scope.instructions.items.len),
2038 });
2039 wzc.extra.appendSliceAssumeCapacity(then_scope.instructions.items);
2040 wzc.extra.appendSliceAssumeCapacity(else_scope.instructions.items);
2041}
2042
2043/// If `elide_block_store_ptr` is set, expects to find exactly 1 .store_to_block_ptr instruction.
2044fn setCondBrPayloadElideBlockStorePtr(
2045 condbr: zir.Inst.Index,
2046 cond: zir.Inst.Ref,
2047 then_scope: *Scope.GenZir,
2048 else_scope: *Scope.GenZir,
2049) !void {
2050 const wzc = then_scope.zir_code;
2051
2052 try wzc.extra.ensureCapacity(wzc.gpa, wzc.extra.items.len +
2053 @typeInfo(zir.Inst.CondBr).Struct.fields.len +
2054 then_scope.instructions.items.len + else_scope.instructions.items.len - 2);
2055
2056 const zir_datas = wzc.instructions.items(.data);
2057 zir_datas[condbr].pl_node.payload_index = wzc.addExtraAssumeCapacity(zir.Inst.CondBr{
2058 .condition = cond,
2059 .then_body_len = @intCast(u32, then_scope.instructions.items.len - 1),
2060 .else_body_len = @intCast(u32, else_scope.instructions.items.len - 1),
2061 });
2062
2063 const zir_tags = wzc.instructions.items(.tag);
2064 for ([_]*Scope.GenZir{ then_scope, else_scope }) |scope| {
2065 for (scope.instructions.items) |src_inst| {
2066 if (zir_tags[src_inst] != .store_to_block_ptr) {
2067 wzc.extra.appendAssumeCapacity(src_inst);
2068 }
2069 }
2070 }
2071}
2072
2073fn whileExpr(
2074 mod: *Module,
2075 scope: *Scope,
2076 rl: ResultLoc,
2077 node: ast.Node.Index,
2078 while_full: ast.full.While,
2079) InnerError!zir.Inst.Ref {
2080 if (while_full.label_token) |label_token| {
2081 try checkLabelRedefinition(mod, scope, label_token);
2082 }
2083 const parent_gz = scope.getGenZir();
2084 const is_inline = parent_gz.force_comptime or while_full.inline_token != null;
2085 const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
2086 const loop_block = try parent_gz.addBlock(loop_tag, node);
2087 try parent_gz.instructions.append(mod.gpa, loop_block);
2088
2089 var loop_scope: Scope.GenZir = .{
2090 .parent = scope,
2091 .zir_code = parent_gz.zir_code,
2092 .force_comptime = parent_gz.force_comptime,
2093 .instructions = .{},
2094 };
2095 setBlockResultLoc(&loop_scope, rl);
2096 defer loop_scope.instructions.deinit(mod.gpa);
2097
2098 var continue_scope: Scope.GenZir = .{
2099 .parent = &loop_scope.base,
2100 .zir_code = parent_gz.zir_code,
2101 .force_comptime = loop_scope.force_comptime,
2102 .instructions = .{},
2103 };
2104 defer continue_scope.instructions.deinit(mod.gpa);
2105
2106 const cond = c: {
2107 // TODO https://github.com/ziglang/zig/issues/7929
2108 if (while_full.error_token) |error_token| {
2109 return mod.failTok(scope, error_token, "TODO implement while error union", .{});
2110 } else if (while_full.payload_token) |payload_token| {
2111 return mod.failTok(scope, payload_token, "TODO implement while optional", .{});
2112 } else {
2113 const bool_type_rl: ResultLoc = .{ .ty = .bool_type };
2114 break :c try expr(mod, &continue_scope.base, bool_type_rl, while_full.ast.cond_expr);
2115 }
2116 };
2117
2118 const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
2119 const condbr = try continue_scope.addCondBr(condbr_tag, node);
2120 const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
2121 const cond_block = try loop_scope.addBlock(block_tag, node);
2122 try loop_scope.instructions.append(mod.gpa, cond_block);
2123 try continue_scope.setBlockBody(cond_block);
2124
2125 // TODO avoid emitting the continue expr when there
2126 // are no jumps to it. This happens when the last statement of a while body is noreturn
2127 // and there are no `continue` statements.
2128 if (while_full.ast.cont_expr != 0) {
2129 _ = try expr(mod, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr);
2130 }
2131 const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
2132 _ = try loop_scope.addNode(repeat_tag, node);
2133
2134 try loop_scope.setBlockBody(loop_block);
2135 loop_scope.break_block = loop_block;
2136 loop_scope.continue_block = cond_block;
2137 if (while_full.label_token) |label_token| {
2138 loop_scope.label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{
2139 .token = label_token,
2140 .block_inst = loop_block,
2141 });
2142 }
2143
2144 var then_scope: Scope.GenZir = .{
2145 .parent = &continue_scope.base,
2146 .zir_code = parent_gz.zir_code,
2147 .force_comptime = continue_scope.force_comptime,
2148 .instructions = .{},
2149 };
2150 defer then_scope.instructions.deinit(mod.gpa);
2151
2152 const then_sub_scope = &then_scope.base;
2153
2154 loop_scope.break_count += 1;
2155 const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
2156
2157 var else_scope: Scope.GenZir = .{
2158 .parent = &continue_scope.base,
2159 .zir_code = parent_gz.zir_code,
2160 .force_comptime = continue_scope.force_comptime,
2161 .instructions = .{},
2162 };
2163 defer else_scope.instructions.deinit(mod.gpa);
2164
2165 const else_node = while_full.ast.else_expr;
2166 const else_info: struct {
2167 src: ast.Node.Index,
2168 result: zir.Inst.Ref,
2169 } = if (else_node != 0) blk: {
2170 loop_scope.break_count += 1;
2171 const sub_scope = &else_scope.base;
2172 break :blk .{
2173 .src = else_node,
2174 .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
2175 };
2176 } else .{
2177 .src = while_full.ast.then_expr,
2178 .result = .none,
2179 };
2180
2181 if (loop_scope.label) |some| {
2182 if (!some.used) {
2183 return mod.failTok(scope, some.token, "unused while loop label", .{});
2184 }
2185 }
2186 const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
2187 return finishThenElseBlock(
2188 mod,
2189 scope,
2190 rl,
2191 node,
2192 &loop_scope,
2193 &then_scope,
2194 &else_scope,
2195 condbr,
2196 cond,
2197 while_full.ast.then_expr,
2198 else_info.src,
2199 then_result,
2200 else_info.result,
2201 loop_block,
2202 cond_block,
2203 break_tag,
2204 );
2205}
2206
2207fn forExpr(
2208 mod: *Module,
2209 scope: *Scope,
2210 rl: ResultLoc,
2211 node: ast.Node.Index,
2212 for_full: ast.full.While,
2213) InnerError!zir.Inst.Ref {
2214 if (for_full.label_token) |label_token| {
2215 try checkLabelRedefinition(mod, scope, label_token);
2216 }
2217 // Set up variables and constants.
2218 const parent_gz = scope.getGenZir();
2219 const is_inline = parent_gz.force_comptime or for_full.inline_token != null;
2220 const tree = parent_gz.tree();
2221 const token_tags = tree.tokens.items(.tag);
2222
2223 const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr);
2224 const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
2225
2226 const index_ptr = blk: {
2227 const index_ptr = try parent_gz.addUnNode(.alloc, .usize_type, node);
2228 // initialize to zero
2229 _ = try parent_gz.addBin(.store, index_ptr, .zero_usize);
2230 break :blk index_ptr;
2231 };
2232
2233 const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
2234 const loop_block = try parent_gz.addBlock(loop_tag, node);
2235 try parent_gz.instructions.append(mod.gpa, loop_block);
2236
2237 var loop_scope: Scope.GenZir = .{
2238 .parent = scope,
2239 .zir_code = parent_gz.zir_code,
2240 .force_comptime = parent_gz.force_comptime,
2241 .instructions = .{},
2242 };
2243 setBlockResultLoc(&loop_scope, rl);
2244 defer loop_scope.instructions.deinit(mod.gpa);
2245
2246 var cond_scope: Scope.GenZir = .{
2247 .parent = &loop_scope.base,
2248 .zir_code = parent_gz.zir_code,
2249 .force_comptime = loop_scope.force_comptime,
2250 .instructions = .{},
2251 };
2252 defer cond_scope.instructions.deinit(mod.gpa);
2253
2254 // check condition i < array_expr.len
2255 const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
2256 const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, zir.Inst.Bin{
2257 .lhs = index,
2258 .rhs = len,
2259 });
2260
2261 const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
2262 const condbr = try cond_scope.addCondBr(condbr_tag, node);
2263 const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
2264 const cond_block = try loop_scope.addBlock(block_tag, node);
2265 try loop_scope.instructions.append(mod.gpa, cond_block);
2266 try cond_scope.setBlockBody(cond_block);
2267
2268 // Increment the index variable.
2269 const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
2270 const index_plus_one = try loop_scope.addPlNode(.add, node, zir.Inst.Bin{
2271 .lhs = index_2,
2272 .rhs = .one_usize,
2273 });
2274 _ = try loop_scope.addBin(.store, index_ptr, index_plus_one);
2275 const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
2276 _ = try loop_scope.addNode(repeat_tag, node);
2277
2278 try loop_scope.setBlockBody(loop_block);
2279 loop_scope.break_block = loop_block;
2280 loop_scope.continue_block = cond_block;
2281 if (for_full.label_token) |label_token| {
2282 loop_scope.label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{
2283 .token = label_token,
2284 .block_inst = loop_block,
2285 });
2286 }
2287
2288 var then_scope: Scope.GenZir = .{
2289 .parent = &cond_scope.base,
2290 .zir_code = parent_gz.zir_code,
2291 .force_comptime = cond_scope.force_comptime,
2292 .instructions = .{},
2293 };
2294 defer then_scope.instructions.deinit(mod.gpa);
2295
2296 var index_scope: Scope.LocalPtr = undefined;
2297 const then_sub_scope = blk: {
2298 const payload_token = for_full.payload_token.?;
2299 const ident = if (token_tags[payload_token] == .asterisk)
2300 payload_token + 1
2301 else
2302 payload_token;
2303 const is_ptr = ident != payload_token;
2304 const value_name = tree.tokenSlice(ident);
2305 if (!mem.eql(u8, value_name, "_")) {
2306 return mod.failNode(&then_scope.base, ident, "TODO implement for loop value payload", .{});
2307 } else if (is_ptr) {
2308 return mod.failTok(&then_scope.base, payload_token, "pointer modifier invalid on discard", .{});
2309 }
2310
2311 const index_token = if (token_tags[ident + 1] == .comma)
2312 ident + 2
2313 else
2314 break :blk &then_scope.base;
2315 if (mem.eql(u8, tree.tokenSlice(index_token), "_")) {
2316 return mod.failTok(&then_scope.base, index_token, "discard of index capture; omit it instead", .{});
2317 }
2318 const index_name = try mod.identifierTokenString(&then_scope.base, index_token);
2319 index_scope = .{
2320 .parent = &then_scope.base,
2321 .gen_zir = &then_scope,
2322 .name = index_name,
2323 .ptr = index_ptr,
2324 .src = parent_gz.tokSrcLoc(index_token),
2325 };
2326 break :blk &index_scope.base;
2327 };
2328
2329 loop_scope.break_count += 1;
2330 const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
2331
2332 var else_scope: Scope.GenZir = .{
2333 .parent = &cond_scope.base,
2334 .zir_code = parent_gz.zir_code,
2335 .force_comptime = cond_scope.force_comptime,
2336 .instructions = .{},
2337 };
2338 defer else_scope.instructions.deinit(mod.gpa);
2339
2340 const else_node = for_full.ast.else_expr;
2341 const else_info: struct {
2342 src: ast.Node.Index,
2343 result: zir.Inst.Ref,
2344 } = if (else_node != 0) blk: {
2345 loop_scope.break_count += 1;
2346 const sub_scope = &else_scope.base;
2347 break :blk .{
2348 .src = else_node,
2349 .result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
2350 };
2351 } else .{
2352 .src = for_full.ast.then_expr,
2353 .result = .none,
2354 };
2355
2356 if (loop_scope.label) |some| {
2357 if (!some.used) {
2358 return mod.failTok(scope, some.token, "unused for loop label", .{});
2359 }
2360 }
2361 const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
2362 return finishThenElseBlock(
2363 mod,
2364 scope,
2365 rl,
2366 node,
2367 &loop_scope,
2368 &then_scope,
2369 &else_scope,
2370 condbr,
2371 cond,
2372 for_full.ast.then_expr,
2373 else_info.src,
2374 then_result,
2375 else_info.result,
2376 loop_block,
2377 cond_block,
2378 break_tag,
2379 );
2380}
2381
2382fn getRangeNode(
2383 node_tags: []const ast.Node.Tag,
2384 node_datas: []const ast.Node.Data,
2385 start_node: ast.Node.Index,
2386) ?ast.Node.Index {
2387 var node = start_node;
2388 while (true) {
2389 switch (node_tags[node]) {
2390 .switch_range => return node,
2391 .grouped_expression => node = node_datas[node].lhs,
2392 else => return null,
2393 }
2394 }
2395}
2396
2397fn switchExpr(
2398 mod: *Module,
2399 scope: *Scope,
2400 rl: ResultLoc,
2401 switch_node: ast.Node.Index,
2402) InnerError!zir.Inst.Ref {
2403 if (true) @panic("TODO update for zir-memory-layout");
2404 const parent_gz = scope.getGenZir();
2405 const tree = parent_gz.tree();
2406 const node_datas = tree.nodes.items(.data);
2407 const main_tokens = tree.nodes.items(.main_token);
2408 const token_tags = tree.tokens.items(.tag);
2409 const node_tags = tree.nodes.items(.tag);
2410
2411 const switch_token = main_tokens[switch_node];
2412 const target_node = node_datas[switch_node].lhs;
2413 const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
2414 const case_nodes = tree.extra_data[extra.start..extra.end];
2415
2416 const switch_src = token_starts[switch_token];
2417
2418 var block_scope: Scope.GenZir = .{
2419 .parent = scope,
2420 .decl = scope.ownerDecl().?,
2421 .arena = scope.arena(),
2422 .force_comptime = parent_gz.force_comptime,
2423 .instructions = .{},
2424 };
2425 setBlockResultLoc(&block_scope, rl);
2426 defer block_scope.instructions.deinit(mod.gpa);
2427
2428 var items = std.ArrayList(zir.Inst.Ref).init(mod.gpa);
2429 defer items.deinit();
2430
2431 // First we gather all the switch items and check else/'_' prongs.
2432 var else_src: ?usize = null;
2433 var underscore_src: ?usize = null;
2434 var first_range: ?*zir.Inst = null;
2435 var simple_case_count: usize = 0;
2436 var any_payload_is_ref = false;
2437 for (case_nodes) |case_node| {
2438 const case = switch (node_tags[case_node]) {
2439 .switch_case_one => tree.switchCaseOne(case_node),
2440 .switch_case => tree.switchCase(case_node),
2441 else => unreachable,
2442 };
2443 if (case.payload_token) |payload_token| {
2444 if (token_tags[payload_token] == .asterisk) {
2445 any_payload_is_ref = true;
2446 }
2447 }
2448 // Check for else/_ prong, those are handled last.
2449 if (case.ast.values.len == 0) {
2450 const case_src = token_starts[case.ast.arrow_token - 1];
2451 if (else_src) |src| {
2452 const msg = msg: {
2453 const msg = try mod.errMsg(
2454 scope,
2455 case_src,
2456 "multiple else prongs in switch expression",
2457 .{},
2458 );
2459 errdefer msg.destroy(mod.gpa);
2460 try mod.errNote(scope, src, msg, "previous else prong is here", .{});
2461 break :msg msg;
2462 };
2463 return mod.failWithOwnedErrorMsg(scope, msg);
2464 }
2465 else_src = case_src;
2466 continue;
2467 } else if (case.ast.values.len == 1 and
2468 node_tags[case.ast.values[0]] == .identifier and
2469 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2470 {
2471 const case_src = token_starts[case.ast.arrow_token - 1];
2472 if (underscore_src) |src| {
2473 const msg = msg: {
2474 const msg = try mod.errMsg(
2475 scope,
2476 case_src,
2477 "multiple '_' prongs in switch expression",
2478 .{},
2479 );
2480 errdefer msg.destroy(mod.gpa);
2481 try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
2482 break :msg msg;
2483 };
2484 return mod.failWithOwnedErrorMsg(scope, msg);
2485 }
2486 underscore_src = case_src;
2487 continue;
2488 }
2489
2490 if (else_src) |some_else| {
2491 if (underscore_src) |some_underscore| {
2492 const msg = msg: {
2493 const msg = try mod.errMsg(
2494 scope,
2495 switch_src,
2496 "else and '_' prong in switch expression",
2497 .{},
2498 );
2499 errdefer msg.destroy(mod.gpa);
2500 try mod.errNote(scope, some_else, msg, "else prong is here", .{});
2501 try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
2502 break :msg msg;
2503 };
2504 return mod.failWithOwnedErrorMsg(scope, msg);
2505 }
2506 }
2507
2508 if (case.ast.values.len == 1 and
2509 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2510 {
2511 simple_case_count += 1;
2512 }
2513
2514 // Generate all the switch items as comptime expressions.
2515 for (case.ast.values) |item| {
2516 if (getRangeNode(node_tags, node_datas, item)) |range| {
2517 const start = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].lhs);
2518 const end = try comptimeExpr(mod, &block_scope.base, .none, node_datas[range].rhs);
2519 const range_src = token_starts[main_tokens[range]];
2520 const range_inst = try addZIRBinOp(mod, &block_scope.base, range_src, .switch_range, start, end);
2521 try items.append(range_inst);
2522 } else {
2523 const item_inst = try comptimeExpr(mod, &block_scope.base, .none, item);
2524 try items.append(item_inst);
2525 }
2526 }
2527 }
2528
2529 var special_prong: zir.Inst.SwitchBr.SpecialProng = .none;
2530 if (else_src != null) special_prong = .@"else";
2531 if (underscore_src != null) special_prong = .underscore;
2532 var cases = try block_scope.arena.alloc(zir.Inst.SwitchBr.Case, simple_case_count);
2533
2534 const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{
2535 .rl = .ref,
2536 .tag = .switchbr_ref,
2537 } else .{
2538 .rl = .none,
2539 .tag = .switchbr,
2540 };
2541 const target = try expr(mod, &block_scope.base, rl_and_tag.rl, target_node);
2542 const switch_inst = try addZirInstT(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, rl_and_tag.tag, .{
2543 .target = target,
2544 .cases = cases,
2545 .items = try block_scope.arena.dupe(zir.Inst.Ref, items.items),
2546 .else_body = undefined, // populated below
2547 .range = first_range,
2548 .special_prong = special_prong,
2549 });
2550 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
2551 .instructions = try block_scope.arena.dupe(zir.Inst.Ref, block_scope.instructions.items),
2552 });
2553
2554 var case_scope: Scope.GenZir = .{
2555 .parent = scope,
2556 .decl = block_scope.decl,
2557 .arena = block_scope.arena,
2558 .force_comptime = block_scope.force_comptime,
2559 .instructions = .{},
2560 };
2561 defer case_scope.instructions.deinit(mod.gpa);
2562
2563 var else_scope: Scope.GenZir = .{
2564 .parent = scope,
2565 .decl = case_scope.decl,
2566 .arena = case_scope.arena,
2567 .force_comptime = case_scope.force_comptime,
2568 .instructions = .{},
2569 };
2570 defer else_scope.instructions.deinit(mod.gpa);
2571
2572 // Now generate all but the special cases.
2573 var special_case: ?ast.full.SwitchCase = null;
2574 var items_index: usize = 0;
2575 var case_index: usize = 0;
2576 for (case_nodes) |case_node| {
2577 const case = switch (node_tags[case_node]) {
2578 .switch_case_one => tree.switchCaseOne(case_node),
2579 .switch_case => tree.switchCase(case_node),
2580 else => unreachable,
2581 };
2582 const case_src = token_starts[main_tokens[case_node]];
2583 case_scope.instructions.shrinkRetainingCapacity(0);
2584
2585 // Check for else/_ prong, those are handled last.
2586 if (case.ast.values.len == 0) {
2587 special_case = case;
2588 continue;
2589 } else if (case.ast.values.len == 1 and
2590 node_tags[case.ast.values[0]] == .identifier and
2591 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
2592 {
2593 special_case = case;
2594 continue;
2595 }
2596
2597 // If this is a simple one item prong then it is handled by the switchbr.
2598 if (case.ast.values.len == 1 and
2599 getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
2600 {
2601 const item = items.items[items_index];
2602 items_index += 1;
2603 try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target);
2604
2605 cases[case_index] = .{
2606 .item = item,
2607 .body = .{ .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items) },
2608 };
2609 case_index += 1;
2610 continue;
2611 }
2612
2613 // Check if the target matches any of the items.
2614 // 1, 2, 3..6 will result in
2615 // target == 1 or target == 2 or (target >= 3 and target <= 6)
2616 // TODO handle multiple items as switch prongs rather than along with ranges.
2617 var any_ok: ?*zir.Inst = null;
2618 for (case.ast.values) |item| {
2619 if (getRangeNode(node_tags, node_datas, item)) |range| {
2620 const range_src = token_starts[main_tokens[range]];
2621 const range_inst = items.items[items_index].castTag(.switch_range).?;
2622 items_index += 1;
2623
2624 // target >= start and target <= end
2625 const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, range_inst.positionals.lhs);
2626 const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, range_inst.positionals.rhs);
2627 const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_and, range_start_ok, range_end_ok);
2628
2629 if (any_ok) |some| {
2630 any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_or, some, range_ok);
2631 } else {
2632 any_ok = range_ok;
2633 }
2634 continue;
2635 }
2636
2637 const item_inst = items.items[items_index];
2638 items_index += 1;
2639 const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst);
2640
2641 if (any_ok) |some| {
2642 any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .bool_or, some, cpm_ok);
2643 } else {
2644 any_ok = cpm_ok;
2645 }
2646 }
2647
2648 const condbr = try addZIRInstSpecial(mod, &case_scope.base, case_src, zir.Inst.CondBr, .{
2649 .condition = any_ok.?,
2650 .then_body = undefined, // populated below
2651 .else_body = undefined, // populated below
2652 }, .{});
2653 const cond_block = try addZIRInstBlock(mod, &else_scope.base, case_src, .block, .{
2654 .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items),
2655 });
2656
2657 // reset cond_scope for then_body
2658 case_scope.instructions.items.len = 0;
2659 try switchCaseExpr(mod, &case_scope.base, block_scope.break_result_loc, block, case, target);
2660 condbr.positionals.then_body = .{
2661 .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items),
2662 };
2663
2664 // reset cond_scope for else_body
2665 case_scope.instructions.items.len = 0;
2666 _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{
2667 .block = cond_block,
2668 }, .{});
2669 condbr.positionals.else_body = .{
2670 .instructions = try scope.arena().dupe(zir.Inst.Ref, case_scope.instructions.items),
2671 };
2672 }
2673
2674 // Finally generate else block or a break.
2675 if (special_case) |case| {
2676 try switchCaseExpr(mod, &else_scope.base, block_scope.break_result_loc, block, case, target);
2677 } else {
2678 // Not handling all possible cases is a compile error.
2679 _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe);
2680 }
2681 switch_inst.positionals.else_body = .{
2682 .instructions = try block_scope.arena.dupe(zir.Inst.Ref, else_scope.instructions.items),
2683 };
2684
2685 return &block.base;
2686}
2687
2688fn switchCaseExpr(
2689 mod: *Module,
2690 scope: *Scope,
2691 rl: ResultLoc,
2692 block: *zir.Inst.Block,
2693 case: ast.full.SwitchCase,
2694 target: zir.Inst.Ref,
2695) !void {
2696 const tree = scope.tree();
2697 const node_datas = tree.nodes.items(.data);
2698 const main_tokens = tree.nodes.items(.main_token);
2699 const token_tags = tree.tokens.items(.tag);
2700
2701 const case_src = token_starts[case.ast.arrow_token];
2702 const sub_scope = blk: {
2703 const payload_token = case.payload_token orelse break :blk scope;
2704 const ident = if (token_tags[payload_token] == .asterisk)
2705 payload_token + 1
2706 else
2707 payload_token;
2708 const is_ptr = ident != payload_token;
2709 const value_name = tree.tokenSlice(ident);
2710 if (mem.eql(u8, value_name, "_")) {
2711 if (is_ptr) {
2712 return mod.failTok(scope, payload_token, "pointer modifier invalid on discard", .{});
2713 }
2714 break :blk scope;
2715 }
2716 return mod.failTok(scope, ident, "TODO implement switch value payload", .{});
2717 };
2718
2719 const case_body = try expr(mod, sub_scope, rl, case.ast.target_expr);
2720 if (!case_body.tag.isNoReturn()) {
2721 _ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{
2722 .block = block,
2723 .operand = case_body,
2724 }, .{});
2725 }
2726}
2727
2728fn ret(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
2729 const tree = scope.tree();
2730 const node_datas = tree.nodes.items(.data);
2731 const main_tokens = tree.nodes.items(.main_token);
2732
2733 const operand_node = node_datas[node].lhs;
2734 const gz = scope.getGenZir();
2735 const operand: zir.Inst.Ref = if (operand_node != 0) operand: {
2736 const rl: ResultLoc = if (nodeMayNeedMemoryLocation(scope, operand_node)) .{
2737 .ptr = try gz.addNode(.ret_ptr, node),
2738 } else .{
2739 .ty = try gz.addNode(.ret_type, node),
2740 };
2741 break :operand try expr(mod, scope, rl, operand_node);
2742 } else .void_value;
2743 _ = try gz.addUnNode(.ret_node, operand, node);
2744 return zir.Inst.Ref.unreachable_value;
2745}
2746
2747fn identifier(
2748 mod: *Module,
2749 scope: *Scope,
2750 rl: ResultLoc,
2751 ident: ast.Node.Index,
2752) InnerError!zir.Inst.Ref {
2753 const tracy = trace(@src());
2754 defer tracy.end();
2755
2756 const tree = scope.tree();
2757 const main_tokens = tree.nodes.items(.main_token);
2758
2759 const gz = scope.getGenZir();
2760
2761 const ident_token = main_tokens[ident];
2762 const ident_name = try mod.identifierTokenString(scope, ident_token);
2763 if (mem.eql(u8, ident_name, "_")) {
2764 return mod.failNode(scope, ident, "TODO implement '_' identifier", .{});
2765 }
2766
2767 if (simple_types.get(ident_name)) |zir_const_ref| {
2768 return rvalue(mod, scope, rl, zir_const_ref, ident);
2769 }
2770
2771 if (ident_name.len >= 2) integer: {
2772 const first_c = ident_name[0];
2773 if (first_c == 'i' or first_c == 'u') {
2774 const signedness: std.builtin.Signedness = switch (first_c == 'i') {
2775 true => .signed,
2776 false => .unsigned,
2777 };
2778 const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
2779 error.Overflow => return mod.failNode(
2780 scope,
2781 ident,
2782 "primitive integer type '{s}' exceeds maximum bit width of 65535",
2783 .{ident_name},
2784 ),
2785 error.InvalidCharacter => break :integer,
2786 };
2787 const result = try gz.add(.{
2788 .tag = .int_type,
2789 .data = .{ .int_type = .{
2790 .src_node = gz.zir_code.decl.nodeIndexToRelative(ident),
2791 .signedness = signedness,
2792 .bit_count = bit_count,
2793 } },
2794 });
2795 return rvalue(mod, scope, rl, result, ident);
2796 }
2797 }
2798
2799 // Local variables, including function parameters.
2800 {
2801 var s = scope;
2802 while (true) switch (s.tag) {
2803 .local_val => {
2804 const local_val = s.cast(Scope.LocalVal).?;
2805 if (mem.eql(u8, local_val.name, ident_name)) {
2806 return rvalue(mod, scope, rl, local_val.inst, ident);
2807 }
2808 s = local_val.parent;
2809 },
2810 .local_ptr => {
2811 const local_ptr = s.cast(Scope.LocalPtr).?;
2812 if (mem.eql(u8, local_ptr.name, ident_name)) {
2813 if (rl == .ref) return local_ptr.ptr;
2814 const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident);
2815 return rvalue(mod, scope, rl, loaded, ident);
2816 }
2817 s = local_ptr.parent;
2818 },
2819 .gen_zir => s = s.cast(Scope.GenZir).?.parent,
2820 else => break,
2821 };
2822 }
2823
2824 const gop = try gz.zir_code.decl_map.getOrPut(mod.gpa, ident_name);
2825 if (!gop.found_existing) {
2826 const decl = mod.lookupDeclName(scope, ident_name) orelse
2827 return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name});
2828 try gz.zir_code.decls.append(mod.gpa, decl);
2829 }
2830 const decl_index = @intCast(u32, gop.index);
2831 switch (rl) {
2832 .ref => return gz.addDecl(.decl_ref, decl_index, ident),
2833 else => return rvalue(mod, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident),
2834 }
2835}
2836
2837fn stringLiteral(
2838 mod: *Module,
2839 scope: *Scope,
2840 rl: ResultLoc,
2841 node: ast.Node.Index,
2842) InnerError!zir.Inst.Ref {
2843 const tree = scope.tree();
2844 const main_tokens = tree.nodes.items(.main_token);
2845 const gz = scope.getGenZir();
2846 const string_bytes = &gz.zir_code.string_bytes;
2847 const str_index = string_bytes.items.len;
2848 const str_lit_token = main_tokens[node];
2849 const token_bytes = tree.tokenSlice(str_lit_token);
2850 try mod.parseStrLit(scope, str_lit_token, string_bytes, token_bytes, 0);
2851 const str_len = string_bytes.items.len - str_index;
2852 const result = try gz.add(.{
2853 .tag = .str,
2854 .data = .{ .str = .{
2855 .start = @intCast(u32, str_index),
2856 .len = @intCast(u32, str_len),
2857 } },
2858 });
2859 return rvalue(mod, scope, rl, result, node);
2860}
2861
2862fn multilineStringLiteral(
2863 mod: *Module,
2864 scope: *Scope,
2865 rl: ResultLoc,
2866 node: ast.Node.Index,
2867) InnerError!zir.Inst.Ref {
2868 const gz = scope.getGenZir();
2869 const tree = gz.tree();
2870 const node_datas = tree.nodes.items(.data);
2871 const main_tokens = tree.nodes.items(.main_token);
2872
2873 const start = node_datas[node].lhs;
2874 const end = node_datas[node].rhs;
2875 const string_bytes = &gz.zir_code.string_bytes;
2876 const str_index = string_bytes.items.len;
2877
2878 // First line: do not append a newline.
2879 var tok_i = start;
2880 {
2881 const slice = tree.tokenSlice(tok_i);
2882 const line_bytes = slice[2 .. slice.len - 1];
2883 try string_bytes.appendSlice(mod.gpa, line_bytes);
2884 tok_i += 1;
2885 }
2886 // Following lines: each line prepends a newline.
2887 while (tok_i <= end) : (tok_i += 1) {
2888 const slice = tree.tokenSlice(tok_i);
2889 const line_bytes = slice[2 .. slice.len - 1];
2890 try string_bytes.ensureCapacity(mod.gpa, string_bytes.items.len + line_bytes.len + 1);
2891 string_bytes.appendAssumeCapacity('\n');
2892 string_bytes.appendSliceAssumeCapacity(line_bytes);
2893 }
2894 const result = try gz.add(.{
2895 .tag = .str,
2896 .data = .{ .str = .{
2897 .start = @intCast(u32, str_index),
2898 .len = @intCast(u32, string_bytes.items.len - str_index),
2899 } },
2900 });
2901 return rvalue(mod, scope, rl, result, node);
2902}
2903
2904fn charLiteral(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) !zir.Inst.Ref {
2905 const gz = scope.getGenZir();
2906 const tree = gz.tree();
2907 const main_tokens = tree.nodes.items(.main_token);
2908 const main_token = main_tokens[node];
2909 const slice = tree.tokenSlice(main_token);
2910
2911 var bad_index: usize = undefined;
2912 const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {
2913 error.InvalidCharacter => {
2914 const bad_byte = slice[bad_index];
2915 const token_starts = tree.tokens.items(.start);
2916 const src_off = @intCast(u32, token_starts[main_token] + bad_index);
2917 return mod.failOff(scope, src_off, "invalid character: '{c}'\n", .{bad_byte});
2918 },
2919 };
2920 const result = try gz.addInt(value);
2921 return rvalue(mod, scope, rl, result, node);
2922}
2923
2924fn integerLiteral(
2925 mod: *Module,
2926 scope: *Scope,
2927 rl: ResultLoc,
2928 node: ast.Node.Index,
2929) InnerError!zir.Inst.Ref {
2930 const tree = scope.tree();
2931 const main_tokens = tree.nodes.items(.main_token);
2932 const int_token = main_tokens[node];
2933 const prefixed_bytes = tree.tokenSlice(int_token);
2934 const gz = scope.getGenZir();
2935 if (std.fmt.parseInt(u64, prefixed_bytes, 0)) |small_int| {
2936 const result: zir.Inst.Ref = switch (small_int) {
2937 0 => .zero,
2938 1 => .one,
2939 else => try gz.addInt(small_int),
2940 };
2941 return rvalue(mod, scope, rl, result, node);
2942 } else |err| {
2943 return mod.failNode(scope, node, "TODO implement int literals that don't fit in a u64", .{});
2944 }
2945}
2946
2947fn floatLiteral(
2948 mod: *Module,
2949 scope: *Scope,
2950 rl: ResultLoc,
2951 node: ast.Node.Index,
2952) InnerError!zir.Inst.Ref {
2953 const arena = scope.arena();
2954 const tree = scope.tree();
2955 const main_tokens = tree.nodes.items(.main_token);
2956 const gz = scope.getGenZir();
2957
2958 const main_token = main_tokens[node];
2959 const bytes = tree.tokenSlice(main_token);
2960 if (bytes.len > 2 and bytes[1] == 'x') {
2961 assert(bytes[0] == '0'); // validated by tokenizer
2962 return mod.failTok(scope, main_token, "TODO implement hex floats", .{});
2963 }
2964 const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
2965 error.InvalidCharacter => unreachable, // validated by tokenizer
2966 };
2967 const typed_value = try arena.create(TypedValue);
2968 typed_value.* = .{
2969 .ty = Type.initTag(.comptime_float),
2970 .val = try Value.Tag.float_128.create(arena, float_number),
2971 };
2972 const result = try gz.add(.{
2973 .tag = .@"const",
2974 .data = .{ .@"const" = typed_value },
2975 });
2976 return rvalue(mod, scope, rl, result, node);
2977}
2978
2979fn asmExpr(
2980 mod: *Module,
2981 scope: *Scope,
2982 rl: ResultLoc,
2983 node: ast.Node.Index,
2984 full: ast.full.Asm,
2985) InnerError!zir.Inst.Ref {
2986 const arena = scope.arena();
2987 const tree = scope.tree();
2988 const main_tokens = tree.nodes.items(.main_token);
2989 const node_datas = tree.nodes.items(.data);
2990 const gz = scope.getGenZir();
2991
2992 const asm_source = try expr(mod, scope, .{ .ty = .const_slice_u8_type }, full.ast.template);
2993
2994 if (full.outputs.len != 0) {
2995 return mod.failTok(scope, full.ast.asm_token, "TODO implement asm with an output", .{});
2996 }
2997
2998 const constraints = try arena.alloc(u32, full.inputs.len);
2999 const args = try arena.alloc(zir.Inst.Ref, full.inputs.len);
3000
3001 for (full.inputs) |input, i| {
3002 const constraint_token = main_tokens[input] + 2;
3003 const string_bytes = &gz.zir_code.string_bytes;
3004 constraints[i] = @intCast(u32, string_bytes.items.len);
3005 const token_bytes = tree.tokenSlice(constraint_token);
3006 try mod.parseStrLit(scope, constraint_token, string_bytes, token_bytes, 0);
3007 try string_bytes.append(mod.gpa, 0);
3008
3009 args[i] = try expr(mod, scope, .{ .ty = .usize_type }, node_datas[input].lhs);
3010 }
3011
3012 const tag: zir.Inst.Tag = if (full.volatile_token != null) .asm_volatile else .@"asm";
3013 const result = try gz.addPlNode(tag, node, zir.Inst.Asm{
3014 .asm_source = asm_source,
3015 .return_type = .void_type,
3016 .output = .none,
3017 .args_len = @intCast(u32, full.inputs.len),
3018 .clobbers_len = 0, // TODO implement asm clobbers
3019 });
3020
3021 try gz.zir_code.extra.ensureCapacity(mod.gpa, gz.zir_code.extra.items.len +
3022 args.len + constraints.len);
3023 gz.zir_code.appendRefsAssumeCapacity(args);
3024 gz.zir_code.extra.appendSliceAssumeCapacity(constraints);
3025
3026 return rvalue(mod, scope, rl, result, node);
3027}
3028
3029fn as(
3030 mod: *Module,
3031 scope: *Scope,
3032 rl: ResultLoc,
3033 builtin_token: ast.TokenIndex,
3034 node: ast.Node.Index,
3035 lhs: ast.Node.Index,
3036 rhs: ast.Node.Index,
3037) InnerError!zir.Inst.Ref {
3038 const dest_type = try typeExpr(mod, scope, lhs);
3039 switch (rl) {
3040 .none, .discard, .ref, .ty => {
3041 const result = try expr(mod, scope, .{ .ty = dest_type }, rhs);
3042 return rvalue(mod, scope, rl, result, node);
3043 },
3044
3045 .ptr => |result_ptr| {
3046 return asRlPtr(mod, scope, rl, result_ptr, rhs, dest_type);
3047 },
3048 .block_ptr => |block_scope| {
3049 return asRlPtr(mod, scope, rl, block_scope.rl_ptr, rhs, dest_type);
3050 },
3051
3052 .bitcasted_ptr => |bitcasted_ptr| {
3053 // TODO here we should be able to resolve the inference; we now have a type for the result.
3054 return mod.failTok(scope, builtin_token, "TODO implement @as with result location @bitCast", .{});
3055 },
3056 .inferred_ptr => |result_alloc| {
3057 // TODO here we should be able to resolve the inference; we now have a type for the result.
3058 return mod.failTok(scope, builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
3059 },
3060 }
3061}
3062
3063fn asRlPtr(
3064 mod: *Module,
3065 scope: *Scope,
3066 rl: ResultLoc,
3067 result_ptr: zir.Inst.Ref,
3068 operand_node: ast.Node.Index,
3069 dest_type: zir.Inst.Ref,
3070) InnerError!zir.Inst.Ref {
3071 // Detect whether this expr() call goes into rvalue() to store the result into the
3072 // result location. If it does, elide the coerce_result_ptr instruction
3073 // as well as the store instruction, instead passing the result as an rvalue.
3074 const parent_gz = scope.getGenZir();
3075 const wzc = parent_gz.zir_code;
3076
3077 var as_scope: Scope.GenZir = .{
3078 .parent = scope,
3079 .zir_code = wzc,
3080 .force_comptime = parent_gz.force_comptime,
3081 .instructions = .{},
3082 };
3083 defer as_scope.instructions.deinit(mod.gpa);
3084
3085 as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
3086 const result = try expr(mod, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node);
3087 const parent_zir = &parent_gz.instructions;
3088 if (as_scope.rvalue_rl_count == 1) {
3089 // Busted! This expression didn't actually need a pointer.
3090 const zir_tags = wzc.instructions.items(.tag);
3091 const zir_datas = wzc.instructions.items(.data);
3092 const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2;
3093 try parent_zir.ensureCapacity(mod.gpa, expected_len);
3094 for (as_scope.instructions.items) |src_inst| {
3095 if (wzc.indexToRef(src_inst) == as_scope.rl_ptr) continue;
3096 if (zir_tags[src_inst] == .store_to_block_ptr) {
3097 if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
3098 }
3099 parent_zir.appendAssumeCapacity(src_inst);
3100 }
3101 assert(parent_zir.items.len == expected_len);
3102 const casted_result = try parent_gz.addBin(.as, dest_type, result);
3103 return rvalue(mod, scope, rl, casted_result, operand_node);
3104 } else {
3105 try parent_zir.appendSlice(mod.gpa, as_scope.instructions.items);
3106 return result;
3107 }
3108}
3109
3110fn bitCast(
3111 mod: *Module,
3112 scope: *Scope,
3113 rl: ResultLoc,
3114 builtin_token: ast.TokenIndex,
3115 node: ast.Node.Index,
3116 lhs: ast.Node.Index,
3117 rhs: ast.Node.Index,
3118) InnerError!zir.Inst.Ref {
3119 if (true) @panic("TODO update for zir-memory-layout");
3120 const dest_type = try typeExpr(mod, scope, lhs);
3121 switch (rl) {
3122 .none => {
3123 const operand = try expr(mod, scope, .none, rhs);
3124 return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
3125 },
3126 .discard => {
3127 const operand = try expr(mod, scope, .none, rhs);
3128 const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
3129 _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
3130 return result;
3131 },
3132 .ref => {
3133 const operand = try expr(mod, scope, .ref, rhs);
3134 const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
3135 return result;
3136 },
3137 .ty => |result_ty| {
3138 const result = try expr(mod, scope, .none, rhs);
3139 const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
3140 return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
3141 },
3142 .ptr => |result_ptr| {
3143 const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
3144 return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, rhs);
3145 },
3146 .bitcasted_ptr => |bitcasted_ptr| {
3147 return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
3148 },
3149 .block_ptr => |block_ptr| {
3150 return mod.failTok(scope, builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
3151 },
3152 .inferred_ptr => |result_alloc| {
3153 // TODO here we should be able to resolve the inference; we now have a type for the result.
3154 return mod.failTok(scope, builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
3155 },
3156 }
3157}
3158
3159fn typeOf(
3160 mod: *Module,
3161 scope: *Scope,
3162 rl: ResultLoc,
3163 builtin_token: ast.TokenIndex,
3164 node: ast.Node.Index,
3165 params: []const ast.Node.Index,
3166) InnerError!zir.Inst.Ref {
3167 if (params.len < 1) {
3168 return mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{});
3169 }
3170 const gz = scope.getGenZir();
3171 if (params.len == 1) {
3172 return rvalue(
3173 mod,
3174 scope,
3175 rl,
3176 try gz.addUnTok(.typeof, try expr(mod, scope, .none, params[0]), node),
3177 node,
3178 );
3179 }
3180 const arena = scope.arena();
3181 var items = try arena.alloc(zir.Inst.Ref, params.len);
3182 for (params) |param, param_i| {
3183 items[param_i] = try expr(mod, scope, .none, param);
3184 }
3185
3186 const result = try gz.addPlNode(.typeof_peer, node, zir.Inst.MultiOp{
3187 .operands_len = @intCast(u32, params.len),
3188 });
3189 try gz.zir_code.appendRefs(items);
3190
3191 return rvalue(mod, scope, rl, result, node);
3192}
3193
3194fn builtinCall(
3195 mod: *Module,
3196 scope: *Scope,
3197 rl: ResultLoc,
3198 node: ast.Node.Index,
3199 params: []const ast.Node.Index,
3200) InnerError!zir.Inst.Ref {
3201 const tree = scope.tree();
3202 const main_tokens = tree.nodes.items(.main_token);
3203
3204 const builtin_token = main_tokens[node];
3205 const builtin_name = tree.tokenSlice(builtin_token);
3206
3207 // We handle the different builtins manually because they have different semantics depending
3208 // on the function. For example, `@as` and others participate in result location semantics,
3209 // and `@cImport` creates a special scope that collects a .c source code text buffer.
3210 // Also, some builtins have a variable number of parameters.
3211
3212 const info = BuiltinFn.list.get(builtin_name) orelse {
3213 return mod.failTok(scope, builtin_token, "invalid builtin function: '{s}'", .{
3214 builtin_name,
3215 });
3216 };
3217 if (info.param_count) |expected| {
3218 if (expected != params.len) {
3219 const s = if (expected == 1) "" else "s";
3220 return mod.failTok(scope, builtin_token, "expected {d} parameter{s}, found {d}", .{
3221 expected, s, params.len,
3222 });
3223 }
3224 }
3225
3226 const gz = scope.getGenZir();
3227
3228 switch (info.tag) {
3229 .ptr_to_int => {
3230 const operand = try expr(mod, scope, .none, params[0]);
3231 const result = try gz.addUnNode(.ptrtoint, operand, node);
3232 return rvalue(mod, scope, rl, result, node);
3233 },
3234 .float_cast => {
3235 const dest_type = try typeExpr(mod, scope, params[0]);
3236 const rhs = try expr(mod, scope, .none, params[1]);
3237 const result = try gz.addPlNode(.floatcast, node, zir.Inst.Bin{
3238 .lhs = dest_type,
3239 .rhs = rhs,
3240 });
3241 return rvalue(mod, scope, rl, result, node);
3242 },
3243 .int_cast => {
3244 const dest_type = try typeExpr(mod, scope, params[0]);
3245 const rhs = try expr(mod, scope, .none, params[1]);
3246 const result = try gz.addPlNode(.intcast, node, zir.Inst.Bin{
3247 .lhs = dest_type,
3248 .rhs = rhs,
3249 });
3250 return rvalue(mod, scope, rl, result, node);
3251 },
3252 .breakpoint => {
3253 const result = try gz.add(.{
3254 .tag = .breakpoint,
3255 .data = .{ .node = gz.zir_code.decl.nodeIndexToRelative(node) },
3256 });
3257 return rvalue(mod, scope, rl, result, node);
3258 },
3259 .import => {
3260 const target = try expr(mod, scope, .none, params[0]);
3261 const result = try gz.addUnNode(.import, target, node);
3262 return rvalue(mod, scope, rl, result, node);
3263 },
3264 .compile_error => {
3265 const target = try expr(mod, scope, .none, params[0]);
3266 const result = try gz.addUnNode(.compile_error, target, node);
3267 return rvalue(mod, scope, rl, result, node);
3268 },
3269 .set_eval_branch_quota => {
3270 const quota = try expr(mod, scope, .{ .ty = .u32_type }, params[0]);
3271 const result = try gz.addUnNode(.set_eval_branch_quota, quota, node);
3272 return rvalue(mod, scope, rl, result, node);
3273 },
3274 .compile_log => {
3275 const arg_refs = try mod.gpa.alloc(zir.Inst.Ref, params.len);
3276 defer mod.gpa.free(arg_refs);
3277
3278 for (params) |param, i| arg_refs[i] = try expr(mod, scope, .none, param);
3279
3280 const result = try gz.addPlNode(.compile_log, node, zir.Inst.MultiOp{
3281 .operands_len = @intCast(u32, params.len),
3282 });
3283 try gz.zir_code.appendRefs(arg_refs);
3284 return rvalue(mod, scope, rl, result, node);
3285 },
3286 .field => {
3287 const field_name = try comptimeExpr(mod, scope, .{ .ty = .const_slice_u8_type }, params[1]);
3288 if (rl == .ref) {
3289 return try gz.addPlNode(.field_ptr_named, node, zir.Inst.FieldNamed{
3290 .lhs = try expr(mod, scope, .ref, params[0]),
3291 .field_name = field_name,
3292 });
3293 }
3294 const result = try gz.addPlNode(.field_val_named, node, zir.Inst.FieldNamed{
3295 .lhs = try expr(mod, scope, .none, params[0]),
3296 .field_name = field_name,
3297 });
3298 return rvalue(mod, scope, rl, result, node);
3299 },
3300 .as => return as(mod, scope, rl, builtin_token, node, params[0], params[1]),
3301 .bit_cast => return bitCast(mod, scope, rl, builtin_token, node, params[0], params[1]),
3302 .TypeOf => return typeOf(mod, scope, rl, builtin_token, node, params),
3303
3304 .add_with_overflow,
3305 .align_cast,
3306 .align_of,
3307 .atomic_load,
3308 .atomic_rmw,
3309 .atomic_store,
3310 .bit_offset_of,
3311 .bool_to_int,
3312 .bit_size_of,
3313 .mul_add,
3314 .byte_swap,
3315 .bit_reverse,
3316 .byte_offset_of,
3317 .call,
3318 .c_define,
3319 .c_import,
3320 .c_include,
3321 .clz,
3322 .cmpxchg_strong,
3323 .cmpxchg_weak,
3324 .ctz,
3325 .c_undef,
3326 .div_exact,
3327 .div_floor,
3328 .div_trunc,
3329 .embed_file,
3330 .enum_to_int,
3331 .error_name,
3332 .error_return_trace,
3333 .error_to_int,
3334 .err_set_cast,
3335 .@"export",
3336 .fence,
3337 .field_parent_ptr,
3338 .float_to_int,
3339 .has_decl,
3340 .has_field,
3341 .int_to_enum,
3342 .int_to_error,
3343 .int_to_float,
3344 .int_to_ptr,
3345 .memcpy,
3346 .memset,
3347 .wasm_memory_size,
3348 .wasm_memory_grow,
3349 .mod,
3350 .mul_with_overflow,
3351 .panic,
3352 .pop_count,
3353 .ptr_cast,
3354 .rem,
3355 .return_address,
3356 .set_align_stack,
3357 .set_cold,
3358 .set_float_mode,
3359 .set_runtime_safety,
3360 .shl_exact,
3361 .shl_with_overflow,
3362 .shr_exact,
3363 .shuffle,
3364 .size_of,
3365 .splat,
3366 .reduce,
3367 .src,
3368 .sqrt,
3369 .sin,
3370 .cos,
3371 .exp,
3372 .exp2,
3373 .log,
3374 .log2,
3375 .log10,
3376 .fabs,
3377 .floor,
3378 .ceil,
3379 .trunc,
3380 .round,
3381 .sub_with_overflow,
3382 .tag_name,
3383 .This,
3384 .truncate,
3385 .Type,
3386 .type_info,
3387 .type_name,
3388 .union_init,
3389 => return mod.failTok(scope, builtin_token, "TODO: implement builtin function {s}", .{
3390 builtin_name,
3391 }),
3392
3393 .async_call,
3394 .frame,
3395 .Frame,
3396 .frame_address,
3397 .frame_size,
3398 => return mod.failTok(scope, builtin_token, "async and related features are not yet supported", .{}),
3399 }
3400}
3401
3402fn callExpr(
3403 mod: *Module,
3404 scope: *Scope,
3405 rl: ResultLoc,
3406 node: ast.Node.Index,
3407 call: ast.full.Call,
3408) InnerError!zir.Inst.Ref {
3409 if (call.async_token) |async_token| {
3410 return mod.failTok(scope, async_token, "async and related features are not yet supported", .{});
3411 }
3412 const lhs = try expr(mod, scope, .none, call.ast.fn_expr);
3413
3414 const args = try mod.gpa.alloc(zir.Inst.Ref, call.ast.params.len);
3415 defer mod.gpa.free(args);
3416
3417 const gz = scope.getGenZir();
3418 for (call.ast.params) |param_node, i| {
3419 const param_type = try gz.add(.{
3420 .tag = .param_type,
3421 .data = .{ .param_type = .{
3422 .callee = lhs,
3423 .param_index = @intCast(u32, i),
3424 } },
3425 });
3426 args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);
3427 }
3428
3429 const modifier: std.builtin.CallOptions.Modifier = switch (call.async_token != null) {
3430 true => .async_kw,
3431 false => .auto,
3432 };
3433 const result: zir.Inst.Ref = res: {
3434 const tag: zir.Inst.Tag = switch (modifier) {
3435 .auto => switch (args.len == 0) {
3436 true => break :res try gz.addUnNode(.call_none, lhs, node),
3437 false => .call,
3438 },
3439 .async_kw => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
3440 .never_tail => unreachable,
3441 .never_inline => unreachable,
3442 .no_async => return mod.failNode(scope, node, "async and related features are not yet supported", .{}),
3443 .always_tail => unreachable,
3444 .always_inline => unreachable,
3445 .compile_time => .call_compile_time,
3446 };
3447 break :res try gz.addCall(tag, lhs, args, node);
3448 };
3449 return rvalue(mod, scope, rl, result, node); // TODO function call with result location
3450}
3451
3452pub const simple_types = std.ComptimeStringMap(zir.Inst.Ref, .{
3453 .{ "u8", .u8_type },
3454 .{ "i8", .i8_type },
3455 .{ "u16", .u16_type },
3456 .{ "i16", .i16_type },
3457 .{ "u32", .u32_type },
3458 .{ "i32", .i32_type },
3459 .{ "u64", .u64_type },
3460 .{ "i64", .i64_type },
3461 .{ "usize", .usize_type },
3462 .{ "isize", .isize_type },
3463 .{ "c_short", .c_short_type },
3464 .{ "c_ushort", .c_ushort_type },
3465 .{ "c_int", .c_int_type },
3466 .{ "c_uint", .c_uint_type },
3467 .{ "c_long", .c_long_type },
3468 .{ "c_ulong", .c_ulong_type },
3469 .{ "c_longlong", .c_longlong_type },
3470 .{ "c_ulonglong", .c_ulonglong_type },
3471 .{ "c_longdouble", .c_longdouble_type },
3472 .{ "f16", .f16_type },
3473 .{ "f32", .f32_type },
3474 .{ "f64", .f64_type },
3475 .{ "f128", .f128_type },
3476 .{ "c_void", .c_void_type },
3477 .{ "bool", .bool_type },
3478 .{ "void", .void_type },
3479 .{ "type", .type_type },
3480 .{ "anyerror", .anyerror_type },
3481 .{ "comptime_int", .comptime_int_type },
3482 .{ "comptime_float", .comptime_float_type },
3483 .{ "noreturn", .noreturn_type },
3484 .{ "null", .null_type },
3485 .{ "undefined", .undefined_type },
3486 .{ "undefined", .undef },
3487 .{ "null", .null_value },
3488 .{ "true", .bool_true },
3489 .{ "false", .bool_false },
3490});
3491
3492fn nodeMayNeedMemoryLocation(scope: *Scope, start_node: ast.Node.Index) bool {
3493 const tree = scope.tree();
3494 const node_tags = tree.nodes.items(.tag);
3495 const node_datas = tree.nodes.items(.data);
3496 const main_tokens = tree.nodes.items(.main_token);
3497 const token_tags = tree.tokens.items(.tag);
3498
3499 var node = start_node;
3500 while (true) {
3501 switch (node_tags[node]) {
3502 .root,
3503 .@"usingnamespace",
3504 .test_decl,
3505 .switch_case,
3506 .switch_case_one,
3507 .container_field_init,
3508 .container_field_align,
3509 .container_field,
3510 .asm_output,
3511 .asm_input,
3512 => unreachable,
3513
3514 .@"return",
3515 .@"break",
3516 .@"continue",
3517 .bit_not,
3518 .bool_not,
3519 .global_var_decl,
3520 .local_var_decl,
3521 .simple_var_decl,
3522 .aligned_var_decl,
3523 .@"defer",
3524 .@"errdefer",
3525 .address_of,
3526 .optional_type,
3527 .negation,
3528 .negation_wrap,
3529 .@"resume",
3530 .array_type,
3531 .array_type_sentinel,
3532 .ptr_type_aligned,
3533 .ptr_type_sentinel,
3534 .ptr_type,
3535 .ptr_type_bit_range,
3536 .@"suspend",
3537 .@"anytype",
3538 .fn_proto_simple,
3539 .fn_proto_multi,
3540 .fn_proto_one,
3541 .fn_proto,
3542 .fn_decl,
3543 .anyframe_type,
3544 .anyframe_literal,
3545 .integer_literal,
3546 .float_literal,
3547 .enum_literal,
3548 .string_literal,
3549 .multiline_string_literal,
3550 .char_literal,
3551 .true_literal,
3552 .false_literal,
3553 .null_literal,
3554 .undefined_literal,
3555 .unreachable_literal,
3556 .identifier,
3557 .error_set_decl,
3558 .container_decl,
3559 .container_decl_trailing,
3560 .container_decl_two,
3561 .container_decl_two_trailing,
3562 .container_decl_arg,
3563 .container_decl_arg_trailing,
3564 .tagged_union,
3565 .tagged_union_trailing,
3566 .tagged_union_two,
3567 .tagged_union_two_trailing,
3568 .tagged_union_enum_tag,
3569 .tagged_union_enum_tag_trailing,
3570 .@"asm",
3571 .asm_simple,
3572 .add,
3573 .add_wrap,
3574 .array_cat,
3575 .array_mult,
3576 .assign,
3577 .assign_bit_and,
3578 .assign_bit_or,
3579 .assign_bit_shift_left,
3580 .assign_bit_shift_right,
3581 .assign_bit_xor,
3582 .assign_div,
3583 .assign_sub,
3584 .assign_sub_wrap,
3585 .assign_mod,
3586 .assign_add,
3587 .assign_add_wrap,
3588 .assign_mul,
3589 .assign_mul_wrap,
3590 .bang_equal,
3591 .bit_and,
3592 .bit_or,
3593 .bit_shift_left,
3594 .bit_shift_right,
3595 .bit_xor,
3596 .bool_and,
3597 .bool_or,
3598 .div,
3599 .equal_equal,
3600 .error_union,
3601 .greater_or_equal,
3602 .greater_than,
3603 .less_or_equal,
3604 .less_than,
3605 .merge_error_sets,
3606 .mod,
3607 .mul,
3608 .mul_wrap,
3609 .switch_range,
3610 .field_access,
3611 .sub,
3612 .sub_wrap,
3613 .slice,
3614 .slice_open,
3615 .slice_sentinel,
3616 .deref,
3617 .array_access,
3618 .error_value,
3619 .while_simple, // This variant cannot have an else expression.
3620 .while_cont, // This variant cannot have an else expression.
3621 .for_simple, // This variant cannot have an else expression.
3622 .if_simple, // This variant cannot have an else expression.
3623 => return false,
3624
3625 // Forward the question to the LHS sub-expression.
3626 .grouped_expression,
3627 .@"try",
3628 .@"await",
3629 .@"comptime",
3630 .@"nosuspend",
3631 .unwrap_optional,
3632 => node = node_datas[node].lhs,
3633
3634 // Forward the question to the RHS sub-expression.
3635 .@"catch",
3636 .@"orelse",
3637 => node = node_datas[node].rhs,
3638
3639 // True because these are exactly the expressions we need memory locations for.
3640 .array_init_one,
3641 .array_init_one_comma,
3642 .array_init_dot_two,
3643 .array_init_dot_two_comma,
3644 .array_init_dot,
3645 .array_init_dot_comma,
3646 .array_init,
3647 .array_init_comma,
3648 .struct_init_one,
3649 .struct_init_one_comma,
3650 .struct_init_dot_two,
3651 .struct_init_dot_two_comma,
3652 .struct_init_dot,
3653 .struct_init_dot_comma,
3654 .struct_init,
3655 .struct_init_comma,
3656 => return true,
3657
3658 // True because depending on comptime conditions, sub-expressions
3659 // may be the kind that need memory locations.
3660 .@"while", // This variant always has an else expression.
3661 .@"if", // This variant always has an else expression.
3662 .@"for", // This variant always has an else expression.
3663 .@"switch",
3664 .switch_comma,
3665 .call_one,
3666 .call_one_comma,
3667 .async_call_one,
3668 .async_call_one_comma,
3669 .call,
3670 .call_comma,
3671 .async_call,
3672 .async_call_comma,
3673 => return true,
3674
3675 .block_two,
3676 .block_two_semicolon,
3677 .block,
3678 .block_semicolon,
3679 => {
3680 const lbrace = main_tokens[node];
3681 if (token_tags[lbrace - 1] == .colon) {
3682 // Labeled blocks may need a memory location to forward
3683 // to their break statements.
3684 return true;
3685 } else {
3686 return false;
3687 }
3688 },
3689
3690 .builtin_call,
3691 .builtin_call_comma,
3692 .builtin_call_two,
3693 .builtin_call_two_comma,
3694 => {
3695 const builtin_token = main_tokens[node];
3696 const builtin_name = tree.tokenSlice(builtin_token);
3697 // If the builtin is an invalid name, we don't cause an error here; instead
3698 // let it pass, and the error will be "invalid builtin function" later.
3699 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
3700 return builtin_info.needs_mem_loc;
3701 },
3702 }
3703 }
3704}
3705
3706/// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of
3707/// result locations must call this function on their result.
3708/// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
3709/// If the `ResultLoc` is `ty`, it will coerce the result to the type.
3710fn rvalue(
3711 mod: *Module,
3712 scope: *Scope,
3713 rl: ResultLoc,
3714 result: zir.Inst.Ref,
3715 src_node: ast.Node.Index,
3716) InnerError!zir.Inst.Ref {
3717 const gz = scope.getGenZir();
3718 switch (rl) {
3719 .none => return result,
3720 .discard => {
3721 // Emit a compile error for discarding error values.
3722 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
3723 return result;
3724 },
3725 .ref => {
3726 // We need a pointer but we have a value.
3727 const tree = scope.tree();
3728 const src_token = tree.firstToken(src_node);
3729 return gz.addUnTok(.ref, result, src_token);
3730 },
3731 .ty => |ty_inst| {
3732 // Quickly eliminate some common, unnecessary type coercion.
3733 const as_ty = @as(u64, @enumToInt(zir.Inst.Ref.type_type)) << 32;
3734 const as_comptime_int = @as(u64, @enumToInt(zir.Inst.Ref.comptime_int_type)) << 32;
3735 const as_bool = @as(u64, @enumToInt(zir.Inst.Ref.bool_type)) << 32;
3736 const as_usize = @as(u64, @enumToInt(zir.Inst.Ref.usize_type)) << 32;
3737 const as_void = @as(u64, @enumToInt(zir.Inst.Ref.void_type)) << 32;
3738 switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) {
3739 as_ty | @enumToInt(zir.Inst.Ref.u8_type),
3740 as_ty | @enumToInt(zir.Inst.Ref.i8_type),
3741 as_ty | @enumToInt(zir.Inst.Ref.u16_type),
3742 as_ty | @enumToInt(zir.Inst.Ref.i16_type),
3743 as_ty | @enumToInt(zir.Inst.Ref.u32_type),
3744 as_ty | @enumToInt(zir.Inst.Ref.i32_type),
3745 as_ty | @enumToInt(zir.Inst.Ref.u64_type),
3746 as_ty | @enumToInt(zir.Inst.Ref.i64_type),
3747 as_ty | @enumToInt(zir.Inst.Ref.usize_type),
3748 as_ty | @enumToInt(zir.Inst.Ref.isize_type),
3749 as_ty | @enumToInt(zir.Inst.Ref.c_short_type),
3750 as_ty | @enumToInt(zir.Inst.Ref.c_ushort_type),
3751 as_ty | @enumToInt(zir.Inst.Ref.c_int_type),
3752 as_ty | @enumToInt(zir.Inst.Ref.c_uint_type),
3753 as_ty | @enumToInt(zir.Inst.Ref.c_long_type),
3754 as_ty | @enumToInt(zir.Inst.Ref.c_ulong_type),
3755 as_ty | @enumToInt(zir.Inst.Ref.c_longlong_type),
3756 as_ty | @enumToInt(zir.Inst.Ref.c_ulonglong_type),
3757 as_ty | @enumToInt(zir.Inst.Ref.c_longdouble_type),
3758 as_ty | @enumToInt(zir.Inst.Ref.f16_type),
3759 as_ty | @enumToInt(zir.Inst.Ref.f32_type),
3760 as_ty | @enumToInt(zir.Inst.Ref.f64_type),
3761 as_ty | @enumToInt(zir.Inst.Ref.f128_type),
3762 as_ty | @enumToInt(zir.Inst.Ref.c_void_type),
3763 as_ty | @enumToInt(zir.Inst.Ref.bool_type),
3764 as_ty | @enumToInt(zir.Inst.Ref.void_type),
3765 as_ty | @enumToInt(zir.Inst.Ref.type_type),
3766 as_ty | @enumToInt(zir.Inst.Ref.anyerror_type),
3767 as_ty | @enumToInt(zir.Inst.Ref.comptime_int_type),
3768 as_ty | @enumToInt(zir.Inst.Ref.comptime_float_type),
3769 as_ty | @enumToInt(zir.Inst.Ref.noreturn_type),
3770 as_ty | @enumToInt(zir.Inst.Ref.null_type),
3771 as_ty | @enumToInt(zir.Inst.Ref.undefined_type),
3772 as_ty | @enumToInt(zir.Inst.Ref.fn_noreturn_no_args_type),
3773 as_ty | @enumToInt(zir.Inst.Ref.fn_void_no_args_type),
3774 as_ty | @enumToInt(zir.Inst.Ref.fn_naked_noreturn_no_args_type),
3775 as_ty | @enumToInt(zir.Inst.Ref.fn_ccc_void_no_args_type),
3776 as_ty | @enumToInt(zir.Inst.Ref.single_const_pointer_to_comptime_int_type),
3777 as_ty | @enumToInt(zir.Inst.Ref.const_slice_u8_type),
3778 as_ty | @enumToInt(zir.Inst.Ref.enum_literal_type),
3779 as_comptime_int | @enumToInt(zir.Inst.Ref.zero),
3780 as_comptime_int | @enumToInt(zir.Inst.Ref.one),
3781 as_bool | @enumToInt(zir.Inst.Ref.bool_true),
3782 as_bool | @enumToInt(zir.Inst.Ref.bool_false),
3783 as_usize | @enumToInt(zir.Inst.Ref.zero_usize),
3784 as_usize | @enumToInt(zir.Inst.Ref.one_usize),
3785 as_void | @enumToInt(zir.Inst.Ref.void_value),
3786 => return result, // type of result is already correct
3787
3788 // Need an explicit type coercion instruction.
3789 else => return gz.addPlNode(.as_node, src_node, zir.Inst.As{
3790 .dest_type = ty_inst,
3791 .operand = result,
3792 }),
3793 }
3794 },
3795 .ptr => |ptr_inst| {
3796 _ = try gz.addPlNode(.store_node, src_node, zir.Inst.Bin{
3797 .lhs = ptr_inst,
3798 .rhs = result,
3799 });
3800 return result;
3801 },
3802 .bitcasted_ptr => |bitcasted_ptr| {
3803 return mod.failNode(scope, src_node, "TODO implement rvalue .bitcasted_ptr", .{});
3804 },
3805 .inferred_ptr => |alloc| {
3806 _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
3807 return result;
3808 },
3809 .block_ptr => |block_scope| {
3810 block_scope.rvalue_rl_count += 1;
3811 _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result);
3812 return result;
3813 },
3814 }
3815}
3816
3817fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZir) ResultLoc.Strategy {
3818 var elide_store_to_block_ptr_instructions = false;
3819 switch (rl) {
3820 // In this branch there will not be any store_to_block_ptr instructions.
3821 .discard, .none, .ty, .ref => return .{
3822 .tag = .break_operand,
3823 .elide_store_to_block_ptr_instructions = false,
3824 },
3825 // The pointer got passed through to the sub-expressions, so we will use
3826 // break_void here.
3827 // In this branch there will not be any store_to_block_ptr instructions.
3828 .ptr => return .{
3829 .tag = .break_void,
3830 .elide_store_to_block_ptr_instructions = false,
3831 },
3832 .inferred_ptr, .bitcasted_ptr, .block_ptr => {
3833 if (block_scope.rvalue_rl_count == block_scope.break_count) {
3834 // Neither prong of the if consumed the result location, so we can
3835 // use break instructions to create an rvalue.
3836 return .{
3837 .tag = .break_operand,
3838 .elide_store_to_block_ptr_instructions = true,
3839 };
3840 } else {
3841 // Allow the store_to_block_ptr instructions to remain so that
3842 // semantic analysis can turn them into bitcasts.
3843 return .{
3844 .tag = .break_void,
3845 .elide_store_to_block_ptr_instructions = false,
3846 };
3847 }
3848 },
3849 }
3850}
3851
3852fn setBlockResultLoc(block_scope: *Scope.GenZir, parent_rl: ResultLoc) void {
3853 // Depending on whether the result location is a pointer or value, different
3854 // ZIR needs to be generated. In the former case we rely on storing to the
3855 // pointer to communicate the result, and use breakvoid; in the latter case
3856 // the block break instructions will have the result values.
3857 // One more complication: when the result location is a pointer, we detect
3858 // the scenario where the result location is not consumed. In this case
3859 // we emit ZIR for the block break instructions to have the result values,
3860 // and then rvalue() on that to pass the value to the result location.
3861 switch (parent_rl) {
3862 .discard, .none, .ty, .ptr, .ref => {
3863 block_scope.break_result_loc = parent_rl;
3864 },
3865
3866 .inferred_ptr => |ptr| {
3867 block_scope.rl_ptr = ptr;
3868 block_scope.break_result_loc = .{ .block_ptr = block_scope };
3869 },
3870
3871 .bitcasted_ptr => |ptr| {
3872 block_scope.rl_ptr = ptr;
3873 block_scope.break_result_loc = .{ .block_ptr = block_scope };
3874 },
3875
3876 .block_ptr => |parent_block_scope| {
3877 block_scope.rl_ptr = parent_block_scope.rl_ptr;
3878 block_scope.break_result_loc = .{ .block_ptr = block_scope };
3879 },
3880 }
3881}
src/translate_c.zig+1-1
......@@ -4266,7 +4266,7 @@ fn isZigPrimitiveType(name: []const u8) bool {
42664266 }
42674267 return true;
42684268 }
4269 return @import("astgen.zig").simple_types.has(name);
4269 return @import("Astgen.zig").simple_types.has(name);
42704270}
42714271
42724272const MacroCtx = struct {
src/zir.zig+2-2
......@@ -1,4 +1,4 @@
1//! Zig Intermediate Representation. astgen.zig converts AST nodes to these
1//! Zig Intermediate Representation. Astgen.zig converts AST nodes to these
22//! untyped IR instructions. Next, Sema.zig processes these into TZIR.
33
44const std = @import("std");
......@@ -491,7 +491,7 @@ pub const Inst = struct {
491491 store_to_block_ptr,
492492 /// Same as `store` but the type of the value being stored will be used to infer
493493 /// the pointer type.
494 /// Uses the `bin` union field - astgen.zig depends on the ability to change
494 /// Uses the `bin` union field - Astgen.zig depends on the ability to change
495495 /// the tag of an instruction from `store_to_block_ptr` to `store_to_inferred_ptr`
496496 /// without changing the data.
497497 store_to_inferred_ptr,