authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-27 11:03:08-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-02-27 11:03:08-08:00
log6f7354a04151bc0da7f661b46c5b5b3afed96112
tree78bc876eb5d68f9bde247add51e4e19ecc3a3b2b
parent27f589dea1dae6ec0033e1ad2902fb5dadfa562b
parent97f2a8b5cb4c882e05add16a69c7a55f7fe46794
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #19102 from ziglang/decouple-zir

JIT `zig fmt` and `zig reduce`

37 files changed, 20981 insertions(+), 20854 deletions(-)

CMakeLists.txt+2-2
...@@ -505,6 +505,7 @@ set(ZIG_STAGE2_SOURCES...@@ -505,6 +505,7 @@ set(ZIG_STAGE2_SOURCES
505 "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig"505 "${CMAKE_SOURCE_DIR}/lib/std/unicode.zig"
506 "${CMAKE_SOURCE_DIR}/lib/std/zig.zig"506 "${CMAKE_SOURCE_DIR}/lib/std/zig.zig"
507 "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig"507 "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig"
508 "${CMAKE_SOURCE_DIR}/lib/std/zig/AstGen.zig"
508 "${CMAKE_SOURCE_DIR}/lib/std/zig/AstRlAnnotate.zig"509 "${CMAKE_SOURCE_DIR}/lib/std/zig/AstRlAnnotate.zig"
509 "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"510 "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
510 "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig"511 "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig"
...@@ -515,8 +516,8 @@ set(ZIG_STAGE2_SOURCES...@@ -515,8 +516,8 @@ set(ZIG_STAGE2_SOURCES
515 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"516 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"
516 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig"517 "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig"
517 "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"518 "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"
519 "${CMAKE_SOURCE_DIR}/lib/std/zig/Zir.zig"
518 "${CMAKE_SOURCE_DIR}/src/Air.zig"520 "${CMAKE_SOURCE_DIR}/src/Air.zig"
519 "${CMAKE_SOURCE_DIR}/src/AstGen.zig"
520 "${CMAKE_SOURCE_DIR}/src/Compilation.zig"521 "${CMAKE_SOURCE_DIR}/src/Compilation.zig"
521 "${CMAKE_SOURCE_DIR}/src/Compilation/Config.zig"522 "${CMAKE_SOURCE_DIR}/src/Compilation/Config.zig"
522 "${CMAKE_SOURCE_DIR}/src/Liveness.zig"523 "${CMAKE_SOURCE_DIR}/src/Liveness.zig"
...@@ -527,7 +528,6 @@ set(ZIG_STAGE2_SOURCES...@@ -527,7 +528,6 @@ set(ZIG_STAGE2_SOURCES
527 "${CMAKE_SOURCE_DIR}/src/Sema.zig"528 "${CMAKE_SOURCE_DIR}/src/Sema.zig"
528 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"529 "${CMAKE_SOURCE_DIR}/src/TypedValue.zig"
529 "${CMAKE_SOURCE_DIR}/src/Value.zig"530 "${CMAKE_SOURCE_DIR}/src/Value.zig"
530 "${CMAKE_SOURCE_DIR}/src/Zir.zig"
531 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig"531 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/CodeGen.zig"
532 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig"532 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Emit.zig"
533 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Mir.zig"533 "${CMAKE_SOURCE_DIR}/src/arch/aarch64/Mir.zig"
build.zig-4
...@@ -34,7 +34,6 @@ pub fn build(b: *std.Build) !void {...@@ -34,7 +34,6 @@ pub fn build(b: *std.Build) !void {
34 const skip_install_langref = b.option(bool, "no-langref", "skip copying of langref to the installation prefix") orelse skip_install_lib_files;34 const skip_install_langref = b.option(bool, "no-langref", "skip copying of langref to the installation prefix") orelse skip_install_lib_files;
35 const skip_install_autodocs = b.option(bool, "no-autodocs", "skip copying of standard library autodocs to the installation prefix") orelse skip_install_lib_files;35 const skip_install_autodocs = b.option(bool, "no-autodocs", "skip copying of standard library autodocs to the installation prefix") orelse skip_install_lib_files;
36 const no_bin = b.option(bool, "no-bin", "skip emitting compiler binary") orelse false;36 const no_bin = b.option(bool, "no-bin", "skip emitting compiler binary") orelse false;
37 const only_reduce = b.option(bool, "only-reduce", "only build zig reduce") orelse false;
3837
39 const docgen_exe = b.addExecutable(.{38 const docgen_exe = b.addExecutable(.{
40 .name = "docgen",39 .name = "docgen",
...@@ -245,7 +244,6 @@ pub fn build(b: *std.Build) !void {...@@ -245,7 +244,6 @@ pub fn build(b: *std.Build) !void {
245 exe_options.addOption(bool, "force_gpa", force_gpa);244 exe_options.addOption(bool, "force_gpa", force_gpa);
246 exe_options.addOption(bool, "only_c", only_c);245 exe_options.addOption(bool, "only_c", only_c);
247 exe_options.addOption(bool, "only_core_functionality", only_c);246 exe_options.addOption(bool, "only_core_functionality", only_c);
248 exe_options.addOption(bool, "only_reduce", only_reduce);
249247
250 if (link_libc) {248 if (link_libc) {
251 exe.linkLibC();249 exe.linkLibC();
...@@ -407,7 +405,6 @@ pub fn build(b: *std.Build) !void {...@@ -407,7 +405,6 @@ pub fn build(b: *std.Build) !void {
407 test_cases_options.addOption(bool, "force_gpa", force_gpa);405 test_cases_options.addOption(bool, "force_gpa", force_gpa);
408 test_cases_options.addOption(bool, "only_c", only_c);406 test_cases_options.addOption(bool, "only_c", only_c);
409 test_cases_options.addOption(bool, "only_core_functionality", true);407 test_cases_options.addOption(bool, "only_core_functionality", true);
410 test_cases_options.addOption(bool, "only_reduce", false);
411 test_cases_options.addOption(bool, "enable_qemu", b.enable_qemu);408 test_cases_options.addOption(bool, "enable_qemu", b.enable_qemu);
412 test_cases_options.addOption(bool, "enable_wine", b.enable_wine);409 test_cases_options.addOption(bool, "enable_wine", b.enable_wine);
413 test_cases_options.addOption(bool, "enable_wasmtime", b.enable_wasmtime);410 test_cases_options.addOption(bool, "enable_wasmtime", b.enable_wasmtime);
...@@ -599,7 +596,6 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {...@@ -599,7 +596,6 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
599 exe_options.addOption(bool, "enable_tracy_allocation", false);596 exe_options.addOption(bool, "enable_tracy_allocation", false);
600 exe_options.addOption(bool, "value_tracing", false);597 exe_options.addOption(bool, "value_tracing", false);
601 exe_options.addOption(bool, "only_core_functionality", true);598 exe_options.addOption(bool, "only_core_functionality", true);
602 exe_options.addOption(bool, "only_reduce", false);
603599
604 const run_opt = b.addSystemCommand(&.{600 const run_opt = b.addSystemCommand(&.{
605 "wasm-opt",601 "wasm-opt",
lib/build_runner.zig+1-1
...@@ -13,7 +13,7 @@ const Step = std.Build.Step;...@@ -13,7 +13,7 @@ const Step = std.Build.Step;
13pub const dependencies = @import("@dependencies");13pub const dependencies = @import("@dependencies");
1414
15pub fn main() !void {15pub fn main() !void {
16 // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived,16 // Here we use an ArenaAllocator backed by a page allocator because a build is a short-lived,
17 // one shot program. We don't need to waste time freeing memory and finding places to squish17 // one shot program. We don't need to waste time freeing memory and finding places to squish
18 // bytes into. So we free everything all at once at the very end.18 // bytes into. So we free everything all at once at the very end.
19 var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);19 var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
lib/compiler/fmt.zig created+342
...@@ -0,0 +1,342 @@
1const std = @import("std");
2const mem = std.mem;
3const fs = std.fs;
4const process = std.process;
5const Allocator = std.mem.Allocator;
6const warn = std.log.warn;
7const Color = std.zig.Color;
8
9const usage_fmt =
10 \\Usage: zig fmt [file]...
11 \\
12 \\ Formats the input files and modifies them in-place.
13 \\ Arguments can be files or directories, which are searched
14 \\ recursively.
15 \\
16 \\Options:
17 \\ -h, --help Print this help and exit
18 \\ --color [auto|off|on] Enable or disable colored error messages
19 \\ --stdin Format code from stdin; output to stdout
20 \\ --check List non-conforming files and exit with an error
21 \\ if the list is non-empty
22 \\ --ast-check Run zig ast-check on every file
23 \\ --exclude [file] Exclude file or directory from formatting
24 \\
25 \\
26;
27
28const Fmt = struct {
29 seen: SeenMap,
30 any_error: bool,
31 check_ast: bool,
32 color: Color,
33 gpa: Allocator,
34 arena: Allocator,
35 out_buffer: std.ArrayList(u8),
36
37 const SeenMap = std.AutoHashMap(fs.File.INode, void);
38};
39
40pub fn main() !void {
41 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
42 defer arena_instance.deinit();
43 const arena = arena_instance.allocator();
44 const gpa = arena;
45
46 const args = try process.argsAlloc(arena);
47
48 var color: Color = .auto;
49 var stdin_flag: bool = false;
50 var check_flag: bool = false;
51 var check_ast_flag: bool = false;
52 var input_files = std.ArrayList([]const u8).init(gpa);
53 defer input_files.deinit();
54 var excluded_files = std.ArrayList([]const u8).init(gpa);
55 defer excluded_files.deinit();
56
57 {
58 var i: usize = 1;
59 while (i < args.len) : (i += 1) {
60 const arg = args[i];
61 if (mem.startsWith(u8, arg, "-")) {
62 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
63 const stdout = std.io.getStdOut().writer();
64 try stdout.writeAll(usage_fmt);
65 return process.cleanExit();
66 } else if (mem.eql(u8, arg, "--color")) {
67 if (i + 1 >= args.len) {
68 fatal("expected [auto|on|off] after --color", .{});
69 }
70 i += 1;
71 const next_arg = args[i];
72 color = std.meta.stringToEnum(Color, next_arg) orelse {
73 fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
74 };
75 } else if (mem.eql(u8, arg, "--stdin")) {
76 stdin_flag = true;
77 } else if (mem.eql(u8, arg, "--check")) {
78 check_flag = true;
79 } else if (mem.eql(u8, arg, "--ast-check")) {
80 check_ast_flag = true;
81 } else if (mem.eql(u8, arg, "--exclude")) {
82 if (i + 1 >= args.len) {
83 fatal("expected parameter after --exclude", .{});
84 }
85 i += 1;
86 const next_arg = args[i];
87 try excluded_files.append(next_arg);
88 } else {
89 fatal("unrecognized parameter: '{s}'", .{arg});
90 }
91 } else {
92 try input_files.append(arg);
93 }
94 }
95 }
96
97 if (stdin_flag) {
98 if (input_files.items.len != 0) {
99 fatal("cannot use --stdin with positional arguments", .{});
100 }
101
102 const stdin = std.io.getStdIn();
103 const source_code = std.zig.readSourceFileToEndAlloc(gpa, stdin, null) catch |err| {
104 fatal("unable to read stdin: {}", .{err});
105 };
106 defer gpa.free(source_code);
107
108 var tree = std.zig.Ast.parse(gpa, source_code, .zig) catch |err| {
109 fatal("error parsing stdin: {}", .{err});
110 };
111 defer tree.deinit(gpa);
112
113 if (check_ast_flag) {
114 var zir = try std.zig.AstGen.generate(gpa, tree);
115
116 if (zir.hasCompileErrors()) {
117 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
118 try wip_errors.init(gpa);
119 defer wip_errors.deinit();
120 try wip_errors.addZirErrorMessages(zir, tree, source_code, "<stdin>");
121 var error_bundle = try wip_errors.toOwnedBundle("");
122 defer error_bundle.deinit(gpa);
123 error_bundle.renderToStdErr(color.renderOptions());
124 process.exit(2);
125 }
126 } else if (tree.errors.len != 0) {
127 try std.zig.printAstErrorsToStderr(gpa, tree, "<stdin>", color);
128 process.exit(2);
129 }
130 const formatted = try tree.render(gpa);
131 defer gpa.free(formatted);
132
133 if (check_flag) {
134 const code: u8 = @intFromBool(mem.eql(u8, formatted, source_code));
135 process.exit(code);
136 }
137
138 return std.io.getStdOut().writeAll(formatted);
139 }
140
141 if (input_files.items.len == 0) {
142 fatal("expected at least one source file argument", .{});
143 }
144
145 var fmt = Fmt{
146 .gpa = gpa,
147 .arena = arena,
148 .seen = Fmt.SeenMap.init(gpa),
149 .any_error = false,
150 .check_ast = check_ast_flag,
151 .color = color,
152 .out_buffer = std.ArrayList(u8).init(gpa),
153 };
154 defer fmt.seen.deinit();
155 defer fmt.out_buffer.deinit();
156
157 // Mark any excluded files/directories as already seen,
158 // so that they are skipped later during actual processing
159 for (excluded_files.items) |file_path| {
160 const stat = fs.cwd().statFile(file_path) catch |err| switch (err) {
161 error.FileNotFound => continue,
162 // On Windows, statFile does not work for directories
163 error.IsDir => dir: {
164 var dir = try fs.cwd().openDir(file_path, .{});
165 defer dir.close();
166 break :dir try dir.stat();
167 },
168 else => |e| return e,
169 };
170 try fmt.seen.put(stat.inode, {});
171 }
172
173 for (input_files.items) |file_path| {
174 try fmtPath(&fmt, file_path, check_flag, fs.cwd(), file_path);
175 }
176 if (fmt.any_error) {
177 process.exit(1);
178 }
179}
180
181const FmtError = error{
182 SystemResources,
183 OperationAborted,
184 IoPending,
185 BrokenPipe,
186 Unexpected,
187 WouldBlock,
188 FileClosed,
189 DestinationAddressRequired,
190 DiskQuota,
191 FileTooBig,
192 InputOutput,
193 NoSpaceLeft,
194 AccessDenied,
195 OutOfMemory,
196 RenameAcrossMountPoints,
197 ReadOnlyFileSystem,
198 LinkQuotaExceeded,
199 FileBusy,
200 EndOfStream,
201 Unseekable,
202 NotOpenForWriting,
203 UnsupportedEncoding,
204 ConnectionResetByPeer,
205 SocketNotConnected,
206 LockViolation,
207 NetNameDeleted,
208 InvalidArgument,
209} || fs.File.OpenError;
210
211fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
212 fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
213 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
214 else => {
215 warn("unable to format '{s}': {s}", .{ file_path, @errorName(err) });
216 fmt.any_error = true;
217 return;
218 },
219 };
220}
221
222fn fmtPathDir(
223 fmt: *Fmt,
224 file_path: []const u8,
225 check_mode: bool,
226 parent_dir: fs.Dir,
227 parent_sub_path: []const u8,
228) FmtError!void {
229 var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
230 defer dir.close();
231
232 const stat = try dir.stat();
233 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
234
235 var dir_it = dir.iterate();
236 while (try dir_it.next()) |entry| {
237 const is_dir = entry.kind == .directory;
238
239 if (is_dir and (mem.eql(u8, entry.name, "zig-cache") or mem.eql(u8, entry.name, "zig-out"))) continue;
240
241 if (is_dir or entry.kind == .file and (mem.endsWith(u8, entry.name, ".zig") or mem.endsWith(u8, entry.name, ".zon"))) {
242 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
243 defer fmt.gpa.free(full_path);
244
245 if (is_dir) {
246 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
247 } else {
248 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
249 warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) });
250 fmt.any_error = true;
251 return;
252 };
253 }
254 }
255 }
256}
257
258fn fmtPathFile(
259 fmt: *Fmt,
260 file_path: []const u8,
261 check_mode: bool,
262 dir: fs.Dir,
263 sub_path: []const u8,
264) FmtError!void {
265 const source_file = try dir.openFile(sub_path, .{});
266 var file_closed = false;
267 errdefer if (!file_closed) source_file.close();
268
269 const stat = try source_file.stat();
270
271 if (stat.kind == .directory)
272 return error.IsDir;
273
274 const gpa = fmt.gpa;
275 const source_code = try std.zig.readSourceFileToEndAlloc(
276 gpa,
277 source_file,
278 std.math.cast(usize, stat.size) orelse return error.FileTooBig,
279 );
280 defer gpa.free(source_code);
281
282 source_file.close();
283 file_closed = true;
284
285 // Add to set after no longer possible to get error.IsDir.
286 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
287
288 var tree = try std.zig.Ast.parse(gpa, source_code, .zig);
289 defer tree.deinit(gpa);
290
291 if (tree.errors.len != 0) {
292 try std.zig.printAstErrorsToStderr(gpa, tree, file_path, fmt.color);
293 fmt.any_error = true;
294 return;
295 }
296
297 if (fmt.check_ast) {
298 if (stat.size > std.zig.max_src_size)
299 return error.FileTooBig;
300
301 var zir = try std.zig.AstGen.generate(gpa, tree);
302 defer zir.deinit(gpa);
303
304 if (zir.hasCompileErrors()) {
305 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
306 try wip_errors.init(gpa);
307 defer wip_errors.deinit();
308 try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path);
309 var error_bundle = try wip_errors.toOwnedBundle("");
310 defer error_bundle.deinit(gpa);
311 error_bundle.renderToStdErr(fmt.color.renderOptions());
312 fmt.any_error = true;
313 }
314 }
315
316 // As a heuristic, we make enough capacity for the same as the input source.
317 fmt.out_buffer.shrinkRetainingCapacity(0);
318 try fmt.out_buffer.ensureTotalCapacity(source_code.len);
319
320 try tree.renderToArrayList(&fmt.out_buffer, .{});
321 if (mem.eql(u8, fmt.out_buffer.items, source_code))
322 return;
323
324 if (check_mode) {
325 const stdout = std.io.getStdOut().writer();
326 try stdout.print("{s}\n", .{file_path});
327 fmt.any_error = true;
328 } else {
329 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
330 defer af.deinit();
331
332 try af.file.writeAll(fmt.out_buffer.items);
333 try af.finish();
334 const stdout = std.io.getStdOut().writer();
335 try stdout.print("{s}\n", .{file_path});
336 }
337}
338
339fn fatal(comptime format: []const u8, args: anytype) noreturn {
340 std.log.err(format, args);
341 process.exit(1);
342}
lib/compiler/reduce.zig created+426
...@@ -0,0 +1,426 @@
1const std = @import("std");
2const mem = std.mem;
3const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;
5const Ast = std.zig.Ast;
6const Walk = @import("reduce/Walk.zig");
7const AstGen = std.zig.AstGen;
8const Zir = std.zig.Zir;
9
10const usage =
11 \\zig reduce [options] ./checker root_source_file.zig [-- [argv]]
12 \\
13 \\root_source_file.zig is relative to --main-mod-path.
14 \\
15 \\checker:
16 \\ An executable that communicates interestingness by returning these exit codes:
17 \\ exit(0): interesting
18 \\ exit(1): unknown (infinite loop or other mishap)
19 \\ exit(other): not interesting
20 \\
21 \\options:
22 \\ --seed [integer] Override the random seed. Defaults to 0
23 \\ --skip-smoke-test Skip interestingness check smoke test
24 \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name
25 \\ deps: [dep],[dep],...
26 \\ dep: [[import=]name]
27 \\ --deps [dep],[dep],... Set dependency names for the root package
28 \\ dep: [[import=]name]
29 \\ --main-mod-path Set the directory of the root module
30 \\
31 \\argv:
32 \\ Forwarded directly to the interestingness script.
33 \\
34;
35
36const Interestingness = enum { interesting, unknown, boring };
37
38// Roadmap:
39// - add thread pool
40// - add support for parsing the module flags
41// - more fancy transformations
42// - @import inlining of modules
43// - removing statements or blocks of code
44// - replacing operands of `and` and `or` with `true` and `false`
45// - replacing if conditions with `true` and `false`
46// - reduce flags sent to the compiler
47// - integrate with the build system?
48
49pub fn main() !void {
50 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
51 defer arena_instance.deinit();
52 const arena = arena_instance.allocator();
53
54 var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .{};
55 const gpa = general_purpose_allocator.allocator();
56
57 const args = try std.process.argsAlloc(arena);
58
59 var opt_checker_path: ?[]const u8 = null;
60 var opt_root_source_file_path: ?[]const u8 = null;
61 var argv: []const []const u8 = &.{};
62 var seed: u32 = 0;
63 var skip_smoke_test = false;
64
65 {
66 var i: usize = 1;
67 while (i < args.len) : (i += 1) {
68 const arg = args[i];
69 if (mem.startsWith(u8, arg, "-")) {
70 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
71 const stdout = std.io.getStdOut().writer();
72 try stdout.writeAll(usage);
73 return std.process.cleanExit();
74 } else if (mem.eql(u8, arg, "--")) {
75 argv = args[i + 1 ..];
76 break;
77 } else if (mem.eql(u8, arg, "--skip-smoke-test")) {
78 skip_smoke_test = true;
79 } else if (mem.eql(u8, arg, "--main-mod-path")) {
80 @panic("TODO: implement --main-mod-path");
81 } else if (mem.eql(u8, arg, "--mod")) {
82 @panic("TODO: implement --mod");
83 } else if (mem.eql(u8, arg, "--deps")) {
84 @panic("TODO: implement --deps");
85 } else if (mem.eql(u8, arg, "--seed")) {
86 i += 1;
87 if (i >= args.len) fatal("expected 32-bit integer after {s}", .{arg});
88 const next_arg = args[i];
89 seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| {
90 fatal("unable to parse seed '{s}' as 32-bit integer: {s}", .{
91 next_arg, @errorName(err),
92 });
93 };
94 } else {
95 fatal("unrecognized parameter: '{s}'", .{arg});
96 }
97 } else if (opt_checker_path == null) {
98 opt_checker_path = arg;
99 } else if (opt_root_source_file_path == null) {
100 opt_root_source_file_path = arg;
101 } else {
102 fatal("unexpected extra parameter: '{s}'", .{arg});
103 }
104 }
105 }
106
107 const checker_path = opt_checker_path orelse
108 fatal("missing interestingness checker argument; see -h for usage", .{});
109 const root_source_file_path = opt_root_source_file_path orelse
110 fatal("missing root source file path argument; see -h for usage", .{});
111
112 var interestingness_argv: std.ArrayListUnmanaged([]const u8) = .{};
113 try interestingness_argv.ensureUnusedCapacity(arena, argv.len + 1);
114 interestingness_argv.appendAssumeCapacity(checker_path);
115 interestingness_argv.appendSliceAssumeCapacity(argv);
116
117 var rendered = std.ArrayList(u8).init(gpa);
118 defer rendered.deinit();
119
120 var astgen_input = std.ArrayList(u8).init(gpa);
121 defer astgen_input.deinit();
122
123 var tree = try parse(gpa, root_source_file_path);
124 defer {
125 gpa.free(tree.source);
126 tree.deinit(gpa);
127 }
128
129 if (!skip_smoke_test) {
130 std.debug.print("smoke testing the interestingness check...\n", .{});
131 switch (try runCheck(arena, interestingness_argv.items)) {
132 .interesting => {},
133 .boring, .unknown => |t| {
134 fatal("interestingness check returned {s} for unmodified input\n", .{
135 @tagName(t),
136 });
137 },
138 }
139 }
140
141 var fixups: Ast.Fixups = .{};
142 defer fixups.deinit(gpa);
143
144 var more_fixups: Ast.Fixups = .{};
145 defer more_fixups.deinit(gpa);
146
147 var rng = std.Random.DefaultPrng.init(seed);
148
149 // 1. Walk the AST of the source file looking for independent
150 // reductions and collecting them all into an array list.
151 // 2. Randomize the list of transformations. A future enhancement will add
152 // priority weights to the sorting but for now they are completely
153 // shuffled.
154 // 3. Apply a subset consisting of 1/2 of the transformations and check for
155 // interestingness.
156 // 4. If not interesting, half the subset size again and check again.
157 // 5. Repeat until the subset size is 1, then march the transformation
158 // index forward by 1 with each non-interesting attempt.
159 //
160 // At any point if a subset of transformations succeeds in producing an interesting
161 // result, restart the whole process, reparsing the AST and re-generating the list
162 // of all possible transformations and shuffling it again.
163
164 var transformations = std.ArrayList(Walk.Transformation).init(gpa);
165 defer transformations.deinit();
166 try Walk.findTransformations(arena, &tree, &transformations);
167 sortTransformations(transformations.items, rng.random());
168
169 fresh: while (transformations.items.len > 0) {
170 std.debug.print("found {d} possible transformations\n", .{
171 transformations.items.len,
172 });
173 var subset_size: usize = transformations.items.len;
174 var start_index: usize = 0;
175
176 while (start_index < transformations.items.len) {
177 const prev_subset_size = subset_size;
178 subset_size = @max(1, subset_size * 3 / 4);
179 if (prev_subset_size > 1 and subset_size == 1)
180 start_index = 0;
181
182 const this_set = transformations.items[start_index..][0..subset_size];
183 std.debug.print("trying {d} random transformations: ", .{subset_size});
184 for (this_set[0..@min(this_set.len, 20)]) |t| {
185 std.debug.print("{s} ", .{@tagName(t)});
186 }
187 std.debug.print("\n", .{});
188 try transformationsToFixups(gpa, arena, root_source_file_path, this_set, &fixups);
189
190 rendered.clearRetainingCapacity();
191 try tree.renderToArrayList(&rendered, fixups);
192
193 // The transformations we applied may have resulted in unused locals,
194 // in which case we would like to add the respective discards.
195 {
196 try astgen_input.resize(rendered.items.len);
197 @memcpy(astgen_input.items, rendered.items);
198 try astgen_input.append(0);
199 const source_with_null = astgen_input.items[0 .. astgen_input.items.len - 1 :0];
200 var astgen_tree = try Ast.parse(gpa, source_with_null, .zig);
201 defer astgen_tree.deinit(gpa);
202 if (astgen_tree.errors.len != 0) {
203 @panic("syntax errors occurred");
204 }
205 var zir = try AstGen.generate(gpa, astgen_tree);
206 defer zir.deinit(gpa);
207
208 if (zir.hasCompileErrors()) {
209 more_fixups.clearRetainingCapacity();
210 const payload_index = zir.extra[@intFromEnum(Zir.ExtraIndex.compile_errors)];
211 assert(payload_index != 0);
212 const header = zir.extraData(Zir.Inst.CompileErrors, payload_index);
213 var extra_index = header.end;
214 for (0..header.data.items_len) |_| {
215 const item = zir.extraData(Zir.Inst.CompileErrors.Item, extra_index);
216 extra_index = item.end;
217 const msg = zir.nullTerminatedString(item.data.msg);
218 if (mem.eql(u8, msg, "unused local constant") or
219 mem.eql(u8, msg, "unused local variable") or
220 mem.eql(u8, msg, "unused function parameter") or
221 mem.eql(u8, msg, "unused capture"))
222 {
223 const ident_token = item.data.token;
224 try more_fixups.unused_var_decls.put(gpa, ident_token, {});
225 } else {
226 std.debug.print("found other ZIR error: '{s}'\n", .{msg});
227 }
228 }
229 if (more_fixups.count() != 0) {
230 rendered.clearRetainingCapacity();
231 try astgen_tree.renderToArrayList(&rendered, more_fixups);
232 }
233 }
234 }
235
236 try std.fs.cwd().writeFile(root_source_file_path, rendered.items);
237 // std.debug.print("trying this code:\n{s}\n", .{rendered.items});
238
239 const interestingness = try runCheck(arena, interestingness_argv.items);
240 std.debug.print("{d} random transformations: {s}. {d}/{d}\n", .{
241 subset_size, @tagName(interestingness), start_index, transformations.items.len,
242 });
243 switch (interestingness) {
244 .interesting => {
245 const new_tree = try parse(gpa, root_source_file_path);
246 gpa.free(tree.source);
247 tree.deinit(gpa);
248 tree = new_tree;
249
250 try Walk.findTransformations(arena, &tree, &transformations);
251 sortTransformations(transformations.items, rng.random());
252
253 continue :fresh;
254 },
255 .unknown, .boring => {
256 // Continue to try the next set of transformations.
257 // If we tested only one transformation, move on to the next one.
258 if (subset_size == 1) {
259 start_index += 1;
260 } else {
261 start_index += subset_size;
262 if (start_index + subset_size > transformations.items.len) {
263 start_index = 0;
264 }
265 }
266 },
267 }
268 }
269 std.debug.print("all {d} remaining transformations are uninteresting\n", .{
270 transformations.items.len,
271 });
272
273 // Revert the source back to not be transformed.
274 fixups.clearRetainingCapacity();
275 rendered.clearRetainingCapacity();
276 try tree.renderToArrayList(&rendered, fixups);
277 try std.fs.cwd().writeFile(root_source_file_path, rendered.items);
278
279 return std.process.cleanExit();
280 }
281 std.debug.print("no more transformations found\n", .{});
282 return std.process.cleanExit();
283}
284
285fn sortTransformations(transformations: []Walk.Transformation, rng: std.Random) void {
286 rng.shuffle(Walk.Transformation, transformations);
287 // Stable sort based on priority to keep randomness as the secondary sort.
288 // TODO: introduce transformation priorities
289 // std.mem.sort(transformations);
290}
291
292fn termToInteresting(term: std.process.Child.Term) Interestingness {
293 return switch (term) {
294 .Exited => |code| switch (code) {
295 0 => .interesting,
296 1 => .unknown,
297 else => .boring,
298 },
299 else => b: {
300 std.debug.print("interestingness check aborted unexpectedly\n", .{});
301 break :b .boring;
302 },
303 };
304}
305
306fn runCheck(arena: std.mem.Allocator, argv: []const []const u8) !Interestingness {
307 const result = try std.process.Child.run(.{
308 .allocator = arena,
309 .argv = argv,
310 });
311 if (result.stderr.len != 0)
312 std.debug.print("{s}", .{result.stderr});
313 return termToInteresting(result.term);
314}
315
316fn transformationsToFixups(
317 gpa: Allocator,
318 arena: Allocator,
319 root_source_file_path: []const u8,
320 transforms: []const Walk.Transformation,
321 fixups: *Ast.Fixups,
322) !void {
323 fixups.clearRetainingCapacity();
324
325 for (transforms) |t| switch (t) {
326 .gut_function => |fn_decl_node| {
327 try fixups.gut_functions.put(gpa, fn_decl_node, {});
328 },
329 .delete_node => |decl_node| {
330 try fixups.omit_nodes.put(gpa, decl_node, {});
331 },
332 .delete_var_decl => |delete_var_decl| {
333 try fixups.omit_nodes.put(gpa, delete_var_decl.var_decl_node, {});
334 for (delete_var_decl.references.items) |ident_node| {
335 try fixups.replace_nodes_with_string.put(gpa, ident_node, "undefined");
336 }
337 },
338 .replace_with_undef => |node| {
339 try fixups.replace_nodes_with_string.put(gpa, node, "undefined");
340 },
341 .replace_with_true => |node| {
342 try fixups.replace_nodes_with_string.put(gpa, node, "true");
343 },
344 .replace_with_false => |node| {
345 try fixups.replace_nodes_with_string.put(gpa, node, "false");
346 },
347 .replace_node => |r| {
348 try fixups.replace_nodes_with_node.put(gpa, r.to_replace, r.replacement);
349 },
350 .inline_imported_file => |inline_imported_file| {
351 const full_imported_path = try std.fs.path.join(gpa, &.{
352 std.fs.path.dirname(root_source_file_path) orelse ".",
353 inline_imported_file.imported_string,
354 });
355 defer gpa.free(full_imported_path);
356 var other_file_ast = try parse(gpa, full_imported_path);
357 defer {
358 gpa.free(other_file_ast.source);
359 other_file_ast.deinit(gpa);
360 }
361
362 var inlined_fixups: Ast.Fixups = .{};
363 defer inlined_fixups.deinit(gpa);
364 if (std.fs.path.dirname(inline_imported_file.imported_string)) |dirname| {
365 inlined_fixups.rebase_imported_paths = dirname;
366 }
367 for (inline_imported_file.in_scope_names.keys()) |name| {
368 // This name needs to be mangled in order to not cause an
369 // ambiguous reference error.
370 var i: u32 = 2;
371 const mangled = while (true) : (i += 1) {
372 const mangled = try std.fmt.allocPrint(gpa, "{s}{d}", .{ name, i });
373 if (!inline_imported_file.in_scope_names.contains(mangled))
374 break mangled;
375 gpa.free(mangled);
376 };
377 try inlined_fixups.rename_identifiers.put(gpa, name, mangled);
378 }
379 defer {
380 for (inlined_fixups.rename_identifiers.values()) |v| {
381 gpa.free(v);
382 }
383 }
384
385 var other_source = std.ArrayList(u8).init(gpa);
386 defer other_source.deinit();
387 try other_source.appendSlice("struct {\n");
388 try other_file_ast.renderToArrayList(&other_source, inlined_fixups);
389 try other_source.appendSlice("}");
390
391 try fixups.replace_nodes_with_string.put(
392 gpa,
393 inline_imported_file.builtin_call_node,
394 try arena.dupe(u8, other_source.items),
395 );
396 },
397 };
398}
399
400fn parse(gpa: Allocator, file_path: []const u8) !Ast {
401 const source_code = std.fs.cwd().readFileAllocOptions(
402 gpa,
403 file_path,
404 std.math.maxInt(u32),
405 null,
406 1,
407 0,
408 ) catch |err| {
409 fatal("unable to open '{s}': {s}", .{ file_path, @errorName(err) });
410 };
411 errdefer gpa.free(source_code);
412
413 var tree = try Ast.parse(gpa, source_code, .zig);
414 errdefer tree.deinit(gpa);
415
416 if (tree.errors.len != 0) {
417 @panic("syntax errors occurred");
418 }
419
420 return tree;
421}
422
423fn fatal(comptime format: []const u8, args: anytype) noreturn {
424 std.log.err(format, args);
425 std.process.exit(1);
426}
lib/compiler/reduce/Walk.zig created+1102
...@@ -0,0 +1,1102 @@
1const std = @import("std");
2const Ast = std.zig.Ast;
3const Walk = @This();
4const assert = std.debug.assert;
5const BuiltinFn = std.zig.BuiltinFn;
6
7ast: *const Ast,
8transformations: *std.ArrayList(Transformation),
9unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index),
10in_scope_names: std.StringArrayHashMapUnmanaged(u32),
11replace_names: std.StringArrayHashMapUnmanaged(u32),
12gpa: std.mem.Allocator,
13arena: std.mem.Allocator,
14
15pub const Transformation = union(enum) {
16 /// Replace the fn decl AST Node with one whose body is only `@trap()` with
17 /// discarded parameters.
18 gut_function: Ast.Node.Index,
19 /// Omit a global declaration.
20 delete_node: Ast.Node.Index,
21 /// Delete a local variable declaration and replace all of its references
22 /// with `undefined`.
23 delete_var_decl: struct {
24 var_decl_node: Ast.Node.Index,
25 /// Identifier nodes that reference the variable.
26 references: std.ArrayListUnmanaged(Ast.Node.Index),
27 },
28 /// Replace an expression with `undefined`.
29 replace_with_undef: Ast.Node.Index,
30 /// Replace an expression with `true`.
31 replace_with_true: Ast.Node.Index,
32 /// Replace an expression with `false`.
33 replace_with_false: Ast.Node.Index,
34 /// Replace a node with another node.
35 replace_node: struct {
36 to_replace: Ast.Node.Index,
37 replacement: Ast.Node.Index,
38 },
39 /// Replace an `@import` with the imported file contents wrapped in a struct.
40 inline_imported_file: InlineImportedFile,
41
42 pub const InlineImportedFile = struct {
43 builtin_call_node: Ast.Node.Index,
44 imported_string: []const u8,
45 /// Identifier names that must be renamed in the inlined code or else
46 /// will cause ambiguous reference errors.
47 in_scope_names: std.StringArrayHashMapUnmanaged(void),
48 };
49};
50
51pub const Error = error{OutOfMemory};
52
53/// The result will be priority shuffled.
54pub fn findTransformations(
55 arena: std.mem.Allocator,
56 ast: *const Ast,
57 transformations: *std.ArrayList(Transformation),
58) !void {
59 transformations.clearRetainingCapacity();
60
61 var walk: Walk = .{
62 .ast = ast,
63 .transformations = transformations,
64 .gpa = transformations.allocator,
65 .arena = arena,
66 .unreferenced_globals = .{},
67 .in_scope_names = .{},
68 .replace_names = .{},
69 };
70 defer {
71 walk.unreferenced_globals.deinit(walk.gpa);
72 walk.in_scope_names.deinit(walk.gpa);
73 walk.replace_names.deinit(walk.gpa);
74 }
75
76 try walkMembers(&walk, walk.ast.rootDecls());
77
78 const unreferenced_globals = walk.unreferenced_globals.values();
79 try transformations.ensureUnusedCapacity(unreferenced_globals.len);
80 for (unreferenced_globals) |node| {
81 transformations.appendAssumeCapacity(.{ .delete_node = node });
82 }
83}
84
85fn walkMembers(w: *Walk, members: []const Ast.Node.Index) Error!void {
86 // First we scan for globals so that we can delete them while walking.
87 try scanDecls(w, members, .add);
88
89 for (members) |member| {
90 try walkMember(w, member);
91 }
92
93 try scanDecls(w, members, .remove);
94}
95
96const ScanDeclsAction = enum { add, remove };
97
98fn scanDecls(w: *Walk, members: []const Ast.Node.Index, action: ScanDeclsAction) Error!void {
99 const ast = w.ast;
100 const gpa = w.gpa;
101 const node_tags = ast.nodes.items(.tag);
102 const main_tokens = ast.nodes.items(.main_token);
103 const token_tags = ast.tokens.items(.tag);
104
105 for (members) |member_node| {
106 const name_token = switch (node_tags[member_node]) {
107 .global_var_decl,
108 .local_var_decl,
109 .simple_var_decl,
110 .aligned_var_decl,
111 => main_tokens[member_node] + 1,
112
113 .fn_proto_simple,
114 .fn_proto_multi,
115 .fn_proto_one,
116 .fn_proto,
117 .fn_decl,
118 => main_tokens[member_node] + 1,
119
120 else => continue,
121 };
122
123 assert(token_tags[name_token] == .identifier);
124 const name_bytes = ast.tokenSlice(name_token);
125
126 switch (action) {
127 .add => {
128 try w.unreferenced_globals.put(gpa, name_bytes, member_node);
129
130 const gop = try w.in_scope_names.getOrPut(gpa, name_bytes);
131 if (!gop.found_existing) gop.value_ptr.* = 0;
132 gop.value_ptr.* += 1;
133 },
134 .remove => {
135 const entry = w.in_scope_names.getEntry(name_bytes).?;
136 if (entry.value_ptr.* <= 1) {
137 assert(w.in_scope_names.swapRemove(name_bytes));
138 } else {
139 entry.value_ptr.* -= 1;
140 }
141 },
142 }
143 }
144}
145
146fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {
147 const ast = w.ast;
148 const datas = ast.nodes.items(.data);
149 switch (ast.nodes.items(.tag)[decl]) {
150 .fn_decl => {
151 const fn_proto = datas[decl].lhs;
152 try walkExpression(w, fn_proto);
153 const body_node = datas[decl].rhs;
154 if (!isFnBodyGutted(ast, body_node)) {
155 w.replace_names.clearRetainingCapacity();
156 try w.transformations.append(.{ .gut_function = decl });
157 try walkExpression(w, body_node);
158 }
159 },
160 .fn_proto_simple,
161 .fn_proto_multi,
162 .fn_proto_one,
163 .fn_proto,
164 => {
165 try walkExpression(w, decl);
166 },
167
168 .@"usingnamespace" => {
169 try w.transformations.append(.{ .delete_node = decl });
170 const expr = datas[decl].lhs;
171 try walkExpression(w, expr);
172 },
173
174 .global_var_decl,
175 .local_var_decl,
176 .simple_var_decl,
177 .aligned_var_decl,
178 => try walkGlobalVarDecl(w, decl, ast.fullVarDecl(decl).?),
179
180 .test_decl => {
181 try w.transformations.append(.{ .delete_node = decl });
182 try walkExpression(w, datas[decl].rhs);
183 },
184
185 .container_field_init,
186 .container_field_align,
187 .container_field,
188 => {
189 try w.transformations.append(.{ .delete_node = decl });
190 try walkContainerField(w, ast.fullContainerField(decl).?);
191 },
192
193 .@"comptime" => {
194 try w.transformations.append(.{ .delete_node = decl });
195 try walkExpression(w, decl);
196 },
197
198 .root => unreachable,
199 else => unreachable,
200 }
201}
202
203fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
204 const ast = w.ast;
205 const token_tags = ast.tokens.items(.tag);
206 const main_tokens = ast.nodes.items(.main_token);
207 const node_tags = ast.nodes.items(.tag);
208 const datas = ast.nodes.items(.data);
209 switch (node_tags[node]) {
210 .identifier => {
211 const name_ident = main_tokens[node];
212 assert(token_tags[name_ident] == .identifier);
213 const name_bytes = ast.tokenSlice(name_ident);
214 _ = w.unreferenced_globals.swapRemove(name_bytes);
215 if (w.replace_names.get(name_bytes)) |index| {
216 try w.transformations.items[index].delete_var_decl.references.append(w.arena, node);
217 }
218 },
219
220 .number_literal,
221 .char_literal,
222 .unreachable_literal,
223 .anyframe_literal,
224 .string_literal,
225 => {},
226
227 .multiline_string_literal => {},
228
229 .error_value => {},
230
231 .block_two,
232 .block_two_semicolon,
233 => {
234 const statements = [2]Ast.Node.Index{ datas[node].lhs, datas[node].rhs };
235 if (datas[node].lhs == 0) {
236 return walkBlock(w, node, statements[0..0]);
237 } else if (datas[node].rhs == 0) {
238 return walkBlock(w, node, statements[0..1]);
239 } else {
240 return walkBlock(w, node, statements[0..2]);
241 }
242 },
243 .block,
244 .block_semicolon,
245 => {
246 const statements = ast.extra_data[datas[node].lhs..datas[node].rhs];
247 return walkBlock(w, node, statements);
248 },
249
250 .@"errdefer" => {
251 const expr = datas[node].rhs;
252 return walkExpression(w, expr);
253 },
254
255 .@"defer" => {
256 const expr = datas[node].rhs;
257 return walkExpression(w, expr);
258 },
259 .@"comptime", .@"nosuspend" => {
260 const block = datas[node].lhs;
261 return walkExpression(w, block);
262 },
263
264 .@"suspend" => {
265 const body = datas[node].lhs;
266 return walkExpression(w, body);
267 },
268
269 .@"catch" => {
270 try walkExpression(w, datas[node].lhs); // target
271 try walkExpression(w, datas[node].rhs); // fallback
272 },
273
274 .field_access => {
275 const field_access = datas[node];
276 try walkExpression(w, field_access.lhs);
277 },
278
279 .error_union,
280 .switch_range,
281 => {
282 const infix = datas[node];
283 try walkExpression(w, infix.lhs);
284 return walkExpression(w, infix.rhs);
285 },
286 .for_range => {
287 const infix = datas[node];
288 try walkExpression(w, infix.lhs);
289 if (infix.rhs != 0) {
290 return walkExpression(w, infix.rhs);
291 }
292 },
293
294 .add,
295 .add_wrap,
296 .add_sat,
297 .array_cat,
298 .array_mult,
299 .assign,
300 .assign_bit_and,
301 .assign_bit_or,
302 .assign_shl,
303 .assign_shl_sat,
304 .assign_shr,
305 .assign_bit_xor,
306 .assign_div,
307 .assign_sub,
308 .assign_sub_wrap,
309 .assign_sub_sat,
310 .assign_mod,
311 .assign_add,
312 .assign_add_wrap,
313 .assign_add_sat,
314 .assign_mul,
315 .assign_mul_wrap,
316 .assign_mul_sat,
317 .bang_equal,
318 .bit_and,
319 .bit_or,
320 .shl,
321 .shl_sat,
322 .shr,
323 .bit_xor,
324 .bool_and,
325 .bool_or,
326 .div,
327 .equal_equal,
328 .greater_or_equal,
329 .greater_than,
330 .less_or_equal,
331 .less_than,
332 .merge_error_sets,
333 .mod,
334 .mul,
335 .mul_wrap,
336 .mul_sat,
337 .sub,
338 .sub_wrap,
339 .sub_sat,
340 .@"orelse",
341 => {
342 const infix = datas[node];
343 try walkExpression(w, infix.lhs);
344 try walkExpression(w, infix.rhs);
345 },
346
347 .assign_destructure => {
348 const lhs_count = ast.extra_data[datas[node].lhs];
349 assert(lhs_count > 1);
350 const lhs_exprs = ast.extra_data[datas[node].lhs + 1 ..][0..lhs_count];
351 const rhs = datas[node].rhs;
352
353 for (lhs_exprs) |lhs_node| {
354 switch (node_tags[lhs_node]) {
355 .global_var_decl,
356 .local_var_decl,
357 .simple_var_decl,
358 .aligned_var_decl,
359 => try walkLocalVarDecl(w, ast.fullVarDecl(lhs_node).?),
360
361 else => try walkExpression(w, lhs_node),
362 }
363 }
364 return walkExpression(w, rhs);
365 },
366
367 .bit_not,
368 .bool_not,
369 .negation,
370 .negation_wrap,
371 .optional_type,
372 .address_of,
373 => {
374 return walkExpression(w, datas[node].lhs);
375 },
376
377 .@"try",
378 .@"resume",
379 .@"await",
380 => {
381 return walkExpression(w, datas[node].lhs);
382 },
383
384 .array_type,
385 .array_type_sentinel,
386 => {},
387
388 .ptr_type_aligned,
389 .ptr_type_sentinel,
390 .ptr_type,
391 .ptr_type_bit_range,
392 => {},
393
394 .array_init_one,
395 .array_init_one_comma,
396 .array_init_dot_two,
397 .array_init_dot_two_comma,
398 .array_init_dot,
399 .array_init_dot_comma,
400 .array_init,
401 .array_init_comma,
402 => {
403 var elements: [2]Ast.Node.Index = undefined;
404 return walkArrayInit(w, ast.fullArrayInit(&elements, node).?);
405 },
406
407 .struct_init_one,
408 .struct_init_one_comma,
409 .struct_init_dot_two,
410 .struct_init_dot_two_comma,
411 .struct_init_dot,
412 .struct_init_dot_comma,
413 .struct_init,
414 .struct_init_comma,
415 => {
416 var buf: [2]Ast.Node.Index = undefined;
417 return walkStructInit(w, node, ast.fullStructInit(&buf, node).?);
418 },
419
420 .call_one,
421 .call_one_comma,
422 .async_call_one,
423 .async_call_one_comma,
424 .call,
425 .call_comma,
426 .async_call,
427 .async_call_comma,
428 => {
429 var buf: [1]Ast.Node.Index = undefined;
430 return walkCall(w, ast.fullCall(&buf, node).?);
431 },
432
433 .array_access => {
434 const suffix = datas[node];
435 try walkExpression(w, suffix.lhs);
436 try walkExpression(w, suffix.rhs);
437 },
438
439 .slice_open, .slice, .slice_sentinel => return walkSlice(w, node, ast.fullSlice(node).?),
440
441 .deref => {
442 try walkExpression(w, datas[node].lhs);
443 },
444
445 .unwrap_optional => {
446 try walkExpression(w, datas[node].lhs);
447 },
448
449 .@"break" => {
450 const label_token = datas[node].lhs;
451 const target = datas[node].rhs;
452 if (label_token == 0 and target == 0) {
453 // no expressions
454 } else if (label_token == 0 and target != 0) {
455 try walkExpression(w, target);
456 } else if (label_token != 0 and target == 0) {
457 try walkIdentifier(w, label_token);
458 } else if (label_token != 0 and target != 0) {
459 try walkExpression(w, target);
460 }
461 },
462
463 .@"continue" => {
464 const label = datas[node].lhs;
465 if (label != 0) {
466 return walkIdentifier(w, label); // label
467 }
468 },
469
470 .@"return" => {
471 if (datas[node].lhs != 0) {
472 try walkExpression(w, datas[node].lhs);
473 }
474 },
475
476 .grouped_expression => {
477 try walkExpression(w, datas[node].lhs);
478 },
479
480 .container_decl,
481 .container_decl_trailing,
482 .container_decl_arg,
483 .container_decl_arg_trailing,
484 .container_decl_two,
485 .container_decl_two_trailing,
486 .tagged_union,
487 .tagged_union_trailing,
488 .tagged_union_enum_tag,
489 .tagged_union_enum_tag_trailing,
490 .tagged_union_two,
491 .tagged_union_two_trailing,
492 => {
493 var buf: [2]Ast.Node.Index = undefined;
494 return walkContainerDecl(w, node, ast.fullContainerDecl(&buf, node).?);
495 },
496
497 .error_set_decl => {
498 const error_token = main_tokens[node];
499 const lbrace = error_token + 1;
500 const rbrace = datas[node].rhs;
501
502 var i = lbrace + 1;
503 while (i < rbrace) : (i += 1) {
504 switch (token_tags[i]) {
505 .doc_comment => unreachable, // TODO
506 .identifier => try walkIdentifier(w, i),
507 .comma => {},
508 else => unreachable,
509 }
510 }
511 },
512
513 .builtin_call_two, .builtin_call_two_comma => {
514 if (datas[node].lhs == 0) {
515 return walkBuiltinCall(w, node, &.{});
516 } else if (datas[node].rhs == 0) {
517 return walkBuiltinCall(w, node, &.{datas[node].lhs});
518 } else {
519 return walkBuiltinCall(w, node, &.{ datas[node].lhs, datas[node].rhs });
520 }
521 },
522 .builtin_call, .builtin_call_comma => {
523 const params = ast.extra_data[datas[node].lhs..datas[node].rhs];
524 return walkBuiltinCall(w, node, params);
525 },
526
527 .fn_proto_simple,
528 .fn_proto_multi,
529 .fn_proto_one,
530 .fn_proto,
531 => {
532 var buf: [1]Ast.Node.Index = undefined;
533 return walkFnProto(w, ast.fullFnProto(&buf, node).?);
534 },
535
536 .anyframe_type => {
537 if (datas[node].rhs != 0) {
538 return walkExpression(w, datas[node].rhs);
539 }
540 },
541
542 .@"switch",
543 .switch_comma,
544 => {
545 const condition = datas[node].lhs;
546 const extra = ast.extraData(datas[node].rhs, Ast.Node.SubRange);
547 const cases = ast.extra_data[extra.start..extra.end];
548
549 try walkExpression(w, condition); // condition expression
550 try walkExpressions(w, cases);
551 },
552
553 .switch_case_one,
554 .switch_case_inline_one,
555 .switch_case,
556 .switch_case_inline,
557 => return walkSwitchCase(w, ast.fullSwitchCase(node).?),
558
559 .while_simple,
560 .while_cont,
561 .@"while",
562 => return walkWhile(w, node, ast.fullWhile(node).?),
563
564 .for_simple,
565 .@"for",
566 => return walkFor(w, ast.fullFor(node).?),
567
568 .if_simple,
569 .@"if",
570 => return walkIf(w, node, ast.fullIf(node).?),
571
572 .asm_simple,
573 .@"asm",
574 => return walkAsm(w, ast.fullAsm(node).?),
575
576 .enum_literal => {
577 return walkIdentifier(w, main_tokens[node]); // name
578 },
579
580 .fn_decl => unreachable,
581 .container_field => unreachable,
582 .container_field_init => unreachable,
583 .container_field_align => unreachable,
584 .root => unreachable,
585 .global_var_decl => unreachable,
586 .local_var_decl => unreachable,
587 .simple_var_decl => unreachable,
588 .aligned_var_decl => unreachable,
589 .@"usingnamespace" => unreachable,
590 .test_decl => unreachable,
591 .asm_output => unreachable,
592 .asm_input => unreachable,
593 }
594}
595
596fn walkGlobalVarDecl(w: *Walk, decl_node: Ast.Node.Index, var_decl: Ast.full.VarDecl) Error!void {
597 _ = decl_node;
598
599 if (var_decl.ast.type_node != 0) {
600 try walkExpression(w, var_decl.ast.type_node);
601 }
602
603 if (var_decl.ast.align_node != 0) {
604 try walkExpression(w, var_decl.ast.align_node);
605 }
606
607 if (var_decl.ast.addrspace_node != 0) {
608 try walkExpression(w, var_decl.ast.addrspace_node);
609 }
610
611 if (var_decl.ast.section_node != 0) {
612 try walkExpression(w, var_decl.ast.section_node);
613 }
614
615 if (var_decl.ast.init_node != 0) {
616 if (!isUndefinedIdent(w.ast, var_decl.ast.init_node)) {
617 try w.transformations.append(.{ .replace_with_undef = var_decl.ast.init_node });
618 }
619 try walkExpression(w, var_decl.ast.init_node);
620 }
621}
622
623fn walkLocalVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void {
624 try walkIdentifierNew(w, var_decl.ast.mut_token + 1); // name
625
626 if (var_decl.ast.type_node != 0) {
627 try walkExpression(w, var_decl.ast.type_node);
628 }
629
630 if (var_decl.ast.align_node != 0) {
631 try walkExpression(w, var_decl.ast.align_node);
632 }
633
634 if (var_decl.ast.addrspace_node != 0) {
635 try walkExpression(w, var_decl.ast.addrspace_node);
636 }
637
638 if (var_decl.ast.section_node != 0) {
639 try walkExpression(w, var_decl.ast.section_node);
640 }
641
642 if (var_decl.ast.init_node != 0) {
643 if (!isUndefinedIdent(w.ast, var_decl.ast.init_node)) {
644 try w.transformations.append(.{ .replace_with_undef = var_decl.ast.init_node });
645 }
646 try walkExpression(w, var_decl.ast.init_node);
647 }
648}
649
650fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void {
651 if (field.ast.type_expr != 0) {
652 try walkExpression(w, field.ast.type_expr); // type
653 }
654 if (field.ast.align_expr != 0) {
655 try walkExpression(w, field.ast.align_expr); // alignment
656 }
657 if (field.ast.value_expr != 0) {
658 try walkExpression(w, field.ast.value_expr); // value
659 }
660}
661
662fn walkBlock(
663 w: *Walk,
664 block_node: Ast.Node.Index,
665 statements: []const Ast.Node.Index,
666) Error!void {
667 _ = block_node;
668 const ast = w.ast;
669 const node_tags = ast.nodes.items(.tag);
670
671 for (statements) |stmt| {
672 switch (node_tags[stmt]) {
673 .global_var_decl,
674 .local_var_decl,
675 .simple_var_decl,
676 .aligned_var_decl,
677 => {
678 const var_decl = ast.fullVarDecl(stmt).?;
679 if (var_decl.ast.init_node != 0 and
680 isUndefinedIdent(w.ast, var_decl.ast.init_node))
681 {
682 try w.transformations.append(.{ .delete_var_decl = .{
683 .var_decl_node = stmt,
684 .references = .{},
685 } });
686 const name_tok = var_decl.ast.mut_token + 1;
687 const name_bytes = ast.tokenSlice(name_tok);
688 try w.replace_names.put(w.gpa, name_bytes, @intCast(w.transformations.items.len - 1));
689 } else {
690 try walkLocalVarDecl(w, var_decl);
691 }
692 },
693
694 else => {
695 switch (categorizeStmt(ast, stmt)) {
696 // Don't try to remove `_ = foo;` discards; those are handled separately.
697 .discard_identifier => {},
698 // definitely try to remove `_ = undefined;` though.
699 .discard_undefined, .trap_call, .other => {
700 try w.transformations.append(.{ .delete_node = stmt });
701 },
702 }
703 try walkExpression(w, stmt);
704 },
705 }
706 }
707}
708
709fn walkArrayType(w: *Walk, array_type: Ast.full.ArrayType) Error!void {
710 try walkExpression(w, array_type.ast.elem_count);
711 if (array_type.ast.sentinel != 0) {
712 try walkExpression(w, array_type.ast.sentinel);
713 }
714 return walkExpression(w, array_type.ast.elem_type);
715}
716
717fn walkArrayInit(w: *Walk, array_init: Ast.full.ArrayInit) Error!void {
718 if (array_init.ast.type_expr != 0) {
719 try walkExpression(w, array_init.ast.type_expr); // T
720 }
721 for (array_init.ast.elements) |elem_init| {
722 try walkExpression(w, elem_init);
723 }
724}
725
726fn walkStructInit(
727 w: *Walk,
728 struct_node: Ast.Node.Index,
729 struct_init: Ast.full.StructInit,
730) Error!void {
731 _ = struct_node;
732 if (struct_init.ast.type_expr != 0) {
733 try walkExpression(w, struct_init.ast.type_expr); // T
734 }
735 for (struct_init.ast.fields) |field_init| {
736 try walkExpression(w, field_init);
737 }
738}
739
740fn walkCall(w: *Walk, call: Ast.full.Call) Error!void {
741 try walkExpression(w, call.ast.fn_expr);
742 try walkParamList(w, call.ast.params);
743}
744
745fn walkSlice(
746 w: *Walk,
747 slice_node: Ast.Node.Index,
748 slice: Ast.full.Slice,
749) Error!void {
750 _ = slice_node;
751 try walkExpression(w, slice.ast.sliced);
752 try walkExpression(w, slice.ast.start);
753 if (slice.ast.end != 0) {
754 try walkExpression(w, slice.ast.end);
755 }
756 if (slice.ast.sentinel != 0) {
757 try walkExpression(w, slice.ast.sentinel);
758 }
759}
760
761fn walkIdentifier(w: *Walk, name_ident: Ast.TokenIndex) Error!void {
762 const ast = w.ast;
763 const token_tags = ast.tokens.items(.tag);
764 assert(token_tags[name_ident] == .identifier);
765 const name_bytes = ast.tokenSlice(name_ident);
766 _ = w.unreferenced_globals.swapRemove(name_bytes);
767}
768
769fn walkIdentifierNew(w: *Walk, name_ident: Ast.TokenIndex) Error!void {
770 _ = w;
771 _ = name_ident;
772}
773
774fn walkContainerDecl(
775 w: *Walk,
776 container_decl_node: Ast.Node.Index,
777 container_decl: Ast.full.ContainerDecl,
778) Error!void {
779 _ = container_decl_node;
780 if (container_decl.ast.arg != 0) {
781 try walkExpression(w, container_decl.ast.arg);
782 }
783 try walkMembers(w, container_decl.ast.members);
784}
785
786fn walkBuiltinCall(
787 w: *Walk,
788 call_node: Ast.Node.Index,
789 params: []const Ast.Node.Index,
790) Error!void {
791 const ast = w.ast;
792 const main_tokens = ast.nodes.items(.main_token);
793 const builtin_token = main_tokens[call_node];
794 const builtin_name = ast.tokenSlice(builtin_token);
795 const info = BuiltinFn.list.get(builtin_name).?;
796 switch (info.tag) {
797 .import => {
798 const operand_node = params[0];
799 const str_lit_token = main_tokens[operand_node];
800 const token_bytes = ast.tokenSlice(str_lit_token);
801 if (std.mem.endsWith(u8, token_bytes, ".zig\"")) {
802 const imported_string = std.zig.string_literal.parseAlloc(w.arena, token_bytes) catch
803 unreachable;
804 try w.transformations.append(.{ .inline_imported_file = .{
805 .builtin_call_node = call_node,
806 .imported_string = imported_string,
807 .in_scope_names = try std.StringArrayHashMapUnmanaged(void).init(
808 w.arena,
809 w.in_scope_names.keys(),
810 &.{},
811 ),
812 } });
813 }
814 },
815 else => {},
816 }
817 for (params) |param_node| {
818 try walkExpression(w, param_node);
819 }
820}
821
822fn walkFnProto(w: *Walk, fn_proto: Ast.full.FnProto) Error!void {
823 const ast = w.ast;
824
825 {
826 var it = fn_proto.iterate(ast);
827 while (it.next()) |param| {
828 if (param.type_expr != 0) {
829 try walkExpression(w, param.type_expr);
830 }
831 }
832 }
833
834 if (fn_proto.ast.align_expr != 0) {
835 try walkExpression(w, fn_proto.ast.align_expr);
836 }
837
838 if (fn_proto.ast.addrspace_expr != 0) {
839 try walkExpression(w, fn_proto.ast.addrspace_expr);
840 }
841
842 if (fn_proto.ast.section_expr != 0) {
843 try walkExpression(w, fn_proto.ast.section_expr);
844 }
845
846 if (fn_proto.ast.callconv_expr != 0) {
847 try walkExpression(w, fn_proto.ast.callconv_expr);
848 }
849
850 try walkExpression(w, fn_proto.ast.return_type);
851}
852
853fn walkExpressions(w: *Walk, expressions: []const Ast.Node.Index) Error!void {
854 for (expressions) |expression| {
855 try walkExpression(w, expression);
856 }
857}
858
859fn walkSwitchCase(w: *Walk, switch_case: Ast.full.SwitchCase) Error!void {
860 for (switch_case.ast.values) |value_expr| {
861 try walkExpression(w, value_expr);
862 }
863 try walkExpression(w, switch_case.ast.target_expr);
864}
865
866fn walkWhile(w: *Walk, node_index: Ast.Node.Index, while_node: Ast.full.While) Error!void {
867 assert(while_node.ast.cond_expr != 0);
868 assert(while_node.ast.then_expr != 0);
869
870 // Perform these transformations in this priority order:
871 // 1. If the `else` expression is missing or an empty block, replace the condition with `if (true)` if it is not already.
872 // 2. If the `then` block is empty, replace the condition with `if (false)` if it is not already.
873 // 3. If the condition is `if (true)`, replace the `if` expression with the contents of the `then` expression.
874 // 4. If the condition is `if (false)`, replace the `if` expression with the contents of the `else` expression.
875 if (!isTrueIdent(w.ast, while_node.ast.cond_expr) and
876 (while_node.ast.else_expr == 0 or isEmptyBlock(w.ast, while_node.ast.else_expr)))
877 {
878 try w.transformations.ensureUnusedCapacity(1);
879 w.transformations.appendAssumeCapacity(.{ .replace_with_true = while_node.ast.cond_expr });
880 } else if (!isFalseIdent(w.ast, while_node.ast.cond_expr) and isEmptyBlock(w.ast, while_node.ast.then_expr)) {
881 try w.transformations.ensureUnusedCapacity(1);
882 w.transformations.appendAssumeCapacity(.{ .replace_with_false = while_node.ast.cond_expr });
883 } else if (isTrueIdent(w.ast, while_node.ast.cond_expr)) {
884 try w.transformations.ensureUnusedCapacity(1);
885 w.transformations.appendAssumeCapacity(.{ .replace_node = .{
886 .to_replace = node_index,
887 .replacement = while_node.ast.then_expr,
888 } });
889 } else if (isFalseIdent(w.ast, while_node.ast.cond_expr)) {
890 try w.transformations.ensureUnusedCapacity(1);
891 w.transformations.appendAssumeCapacity(.{ .replace_node = .{
892 .to_replace = node_index,
893 .replacement = while_node.ast.else_expr,
894 } });
895 }
896
897 try walkExpression(w, while_node.ast.cond_expr); // condition
898
899 if (while_node.ast.cont_expr != 0) {
900 try walkExpression(w, while_node.ast.cont_expr);
901 }
902
903 if (while_node.ast.then_expr != 0) {
904 try walkExpression(w, while_node.ast.then_expr);
905 }
906 if (while_node.ast.else_expr != 0) {
907 try walkExpression(w, while_node.ast.else_expr);
908 }
909}
910
911fn walkFor(w: *Walk, for_node: Ast.full.For) Error!void {
912 try walkParamList(w, for_node.ast.inputs);
913 if (for_node.ast.then_expr != 0) {
914 try walkExpression(w, for_node.ast.then_expr);
915 }
916 if (for_node.ast.else_expr != 0) {
917 try walkExpression(w, for_node.ast.else_expr);
918 }
919}
920
921fn walkIf(w: *Walk, node_index: Ast.Node.Index, if_node: Ast.full.If) Error!void {
922 assert(if_node.ast.cond_expr != 0);
923 assert(if_node.ast.then_expr != 0);
924
925 // Perform these transformations in this priority order:
926 // 1. If the `else` expression is missing or an empty block, replace the condition with `if (true)` if it is not already.
927 // 2. If the `then` block is empty, replace the condition with `if (false)` if it is not already.
928 // 3. If the condition is `if (true)`, replace the `if` expression with the contents of the `then` expression.
929 // 4. If the condition is `if (false)`, replace the `if` expression with the contents of the `else` expression.
930 if (!isTrueIdent(w.ast, if_node.ast.cond_expr) and
931 (if_node.ast.else_expr == 0 or isEmptyBlock(w.ast, if_node.ast.else_expr)))
932 {
933 try w.transformations.ensureUnusedCapacity(1);
934 w.transformations.appendAssumeCapacity(.{ .replace_with_true = if_node.ast.cond_expr });
935 } else if (!isFalseIdent(w.ast, if_node.ast.cond_expr) and isEmptyBlock(w.ast, if_node.ast.then_expr)) {
936 try w.transformations.ensureUnusedCapacity(1);
937 w.transformations.appendAssumeCapacity(.{ .replace_with_false = if_node.ast.cond_expr });
938 } else if (isTrueIdent(w.ast, if_node.ast.cond_expr)) {
939 try w.transformations.ensureUnusedCapacity(1);
940 w.transformations.appendAssumeCapacity(.{ .replace_node = .{
941 .to_replace = node_index,
942 .replacement = if_node.ast.then_expr,
943 } });
944 } else if (isFalseIdent(w.ast, if_node.ast.cond_expr)) {
945 try w.transformations.ensureUnusedCapacity(1);
946 w.transformations.appendAssumeCapacity(.{ .replace_node = .{
947 .to_replace = node_index,
948 .replacement = if_node.ast.else_expr,
949 } });
950 }
951
952 try walkExpression(w, if_node.ast.cond_expr); // condition
953
954 if (if_node.ast.then_expr != 0) {
955 try walkExpression(w, if_node.ast.then_expr);
956 }
957 if (if_node.ast.else_expr != 0) {
958 try walkExpression(w, if_node.ast.else_expr);
959 }
960}
961
962fn walkAsm(w: *Walk, asm_node: Ast.full.Asm) Error!void {
963 try walkExpression(w, asm_node.ast.template);
964 for (asm_node.ast.items) |item| {
965 try walkExpression(w, item);
966 }
967}
968
969fn walkParamList(w: *Walk, params: []const Ast.Node.Index) Error!void {
970 for (params) |param_node| {
971 try walkExpression(w, param_node);
972 }
973}
974
975/// Check if it is already gutted (i.e. its body replaced with `@trap()`).
976fn isFnBodyGutted(ast: *const Ast, body_node: Ast.Node.Index) bool {
977 // skip over discards
978 const node_tags = ast.nodes.items(.tag);
979 const datas = ast.nodes.items(.data);
980 var statements_buf: [2]Ast.Node.Index = undefined;
981 const statements = switch (node_tags[body_node]) {
982 .block_two,
983 .block_two_semicolon,
984 => blk: {
985 statements_buf[0..2].* = .{ datas[body_node].lhs, datas[body_node].rhs };
986 break :blk if (datas[body_node].lhs == 0)
987 statements_buf[0..0]
988 else if (datas[body_node].rhs == 0)
989 statements_buf[0..1]
990 else
991 statements_buf[0..2];
992 },
993
994 .block,
995 .block_semicolon,
996 => ast.extra_data[datas[body_node].lhs..datas[body_node].rhs],
997
998 else => return false,
999 };
1000 var i: usize = 0;
1001 while (i < statements.len) : (i += 1) {
1002 switch (categorizeStmt(ast, statements[i])) {
1003 .discard_identifier => continue,
1004 .trap_call => return i + 1 == statements.len,
1005 else => return false,
1006 }
1007 }
1008 return false;
1009}
1010
1011const StmtCategory = enum {
1012 discard_undefined,
1013 discard_identifier,
1014 trap_call,
1015 other,
1016};
1017
1018fn categorizeStmt(ast: *const Ast, stmt: Ast.Node.Index) StmtCategory {
1019 const node_tags = ast.nodes.items(.tag);
1020 const datas = ast.nodes.items(.data);
1021 const main_tokens = ast.nodes.items(.main_token);
1022 switch (node_tags[stmt]) {
1023 .builtin_call_two, .builtin_call_two_comma => {
1024 if (datas[stmt].lhs == 0) {
1025 return categorizeBuiltinCall(ast, main_tokens[stmt], &.{});
1026 } else if (datas[stmt].rhs == 0) {
1027 return categorizeBuiltinCall(ast, main_tokens[stmt], &.{datas[stmt].lhs});
1028 } else {
1029 return categorizeBuiltinCall(ast, main_tokens[stmt], &.{ datas[stmt].lhs, datas[stmt].rhs });
1030 }
1031 },
1032 .builtin_call, .builtin_call_comma => {
1033 const params = ast.extra_data[datas[stmt].lhs..datas[stmt].rhs];
1034 return categorizeBuiltinCall(ast, main_tokens[stmt], params);
1035 },
1036 .assign => {
1037 const infix = datas[stmt];
1038 if (isDiscardIdent(ast, infix.lhs) and node_tags[infix.rhs] == .identifier) {
1039 const name_bytes = ast.tokenSlice(main_tokens[infix.rhs]);
1040 if (std.mem.eql(u8, name_bytes, "undefined")) {
1041 return .discard_undefined;
1042 } else {
1043 return .discard_identifier;
1044 }
1045 }
1046 return .other;
1047 },
1048 else => return .other,
1049 }
1050}
1051
1052fn categorizeBuiltinCall(
1053 ast: *const Ast,
1054 builtin_token: Ast.TokenIndex,
1055 params: []const Ast.Node.Index,
1056) StmtCategory {
1057 if (params.len != 0) return .other;
1058 const name_bytes = ast.tokenSlice(builtin_token);
1059 if (std.mem.eql(u8, name_bytes, "@trap"))
1060 return .trap_call;
1061 return .other;
1062}
1063
1064fn isDiscardIdent(ast: *const Ast, node: Ast.Node.Index) bool {
1065 return isMatchingIdent(ast, node, "_");
1066}
1067
1068fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool {
1069 return isMatchingIdent(ast, node, "undefined");
1070}
1071
1072fn isTrueIdent(ast: *const Ast, node: Ast.Node.Index) bool {
1073 return isMatchingIdent(ast, node, "true");
1074}
1075
1076fn isFalseIdent(ast: *const Ast, node: Ast.Node.Index) bool {
1077 return isMatchingIdent(ast, node, "false");
1078}
1079
1080fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bool {
1081 const node_tags = ast.nodes.items(.tag);
1082 const main_tokens = ast.nodes.items(.main_token);
1083 switch (node_tags[node]) {
1084 .identifier => {
1085 const token_index = main_tokens[node];
1086 const name_bytes = ast.tokenSlice(token_index);
1087 return std.mem.eql(u8, name_bytes, string);
1088 },
1089 else => return false,
1090 }
1091}
1092
1093fn isEmptyBlock(ast: *const Ast, node: Ast.Node.Index) bool {
1094 const node_tags = ast.nodes.items(.tag);
1095 const node_data = ast.nodes.items(.data);
1096 switch (node_tags[node]) {
1097 .block_two => {
1098 return node_data[node].lhs == 0 and node_data[node].rhs == 0;
1099 },
1100 else => return false,
1101 }
1102}
lib/compiler/test_runner.zig created+249
...@@ -0,0 +1,249 @@
1//! Default test runner for unit tests.
2const std = @import("std");
3const io = std.io;
4const builtin = @import("builtin");
5
6pub const std_options = .{
7 .logFn = log,
8};
9
10var log_err_count: usize = 0;
11var cmdline_buffer: [4096]u8 = undefined;
12var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
13
14pub fn main() void {
15 if (builtin.zig_backend == .stage2_aarch64) {
16 return mainSimple() catch @panic("test failure");
17 }
18
19 const args = std.process.argsAlloc(fba.allocator()) catch
20 @panic("unable to parse command line args");
21
22 var listen = false;
23
24 for (args[1..]) |arg| {
25 if (std.mem.eql(u8, arg, "--listen=-")) {
26 listen = true;
27 } else {
28 @panic("unrecognized command line argument");
29 }
30 }
31
32 if (listen) {
33 return mainServer() catch @panic("internal test runner failure");
34 } else {
35 return mainTerminal();
36 }
37}
38
39fn mainServer() !void {
40 var server = try std.zig.Server.init(.{
41 .gpa = fba.allocator(),
42 .in = std.io.getStdIn(),
43 .out = std.io.getStdOut(),
44 .zig_version = builtin.zig_version_string,
45 });
46 defer server.deinit();
47
48 while (true) {
49 const hdr = try server.receiveMessage();
50 switch (hdr.tag) {
51 .exit => {
52 return std.process.exit(0);
53 },
54 .query_test_metadata => {
55 std.testing.allocator_instance = .{};
56 defer if (std.testing.allocator_instance.deinit() == .leak) {
57 @panic("internal test runner memory leak");
58 };
59
60 var string_bytes: std.ArrayListUnmanaged(u8) = .{};
61 defer string_bytes.deinit(std.testing.allocator);
62 try string_bytes.append(std.testing.allocator, 0); // Reserve 0 for null.
63
64 const test_fns = builtin.test_functions;
65 const names = try std.testing.allocator.alloc(u32, test_fns.len);
66 defer std.testing.allocator.free(names);
67 const expected_panic_msgs = try std.testing.allocator.alloc(u32, test_fns.len);
68 defer std.testing.allocator.free(expected_panic_msgs);
69
70 for (test_fns, names, expected_panic_msgs) |test_fn, *name, *expected_panic_msg| {
71 name.* = @as(u32, @intCast(string_bytes.items.len));
72 try string_bytes.ensureUnusedCapacity(std.testing.allocator, test_fn.name.len + 1);
73 string_bytes.appendSliceAssumeCapacity(test_fn.name);
74 string_bytes.appendAssumeCapacity(0);
75 expected_panic_msg.* = 0;
76 }
77
78 try server.serveTestMetadata(.{
79 .names = names,
80 .expected_panic_msgs = expected_panic_msgs,
81 .string_bytes = string_bytes.items,
82 });
83 },
84
85 .run_test => {
86 std.testing.allocator_instance = .{};
87 log_err_count = 0;
88 const index = try server.receiveBody_u32();
89 const test_fn = builtin.test_functions[index];
90 var fail = false;
91 var skip = false;
92 var leak = false;
93 test_fn.func() catch |err| switch (err) {
94 error.SkipZigTest => skip = true,
95 else => {
96 fail = true;
97 if (@errorReturnTrace()) |trace| {
98 std.debug.dumpStackTrace(trace.*);
99 }
100 },
101 };
102 leak = std.testing.allocator_instance.deinit() == .leak;
103 try server.serveTestResults(.{
104 .index = index,
105 .flags = .{
106 .fail = fail,
107 .skip = skip,
108 .leak = leak,
109 .log_err_count = std.math.lossyCast(std.meta.FieldType(
110 std.zig.Server.Message.TestResults.Flags,
111 .log_err_count,
112 ), log_err_count),
113 },
114 });
115 },
116
117 else => {
118 std.debug.print("unsupported message: {x}", .{@intFromEnum(hdr.tag)});
119 std.process.exit(1);
120 },
121 }
122 }
123}
124
125fn mainTerminal() void {
126 const test_fn_list = builtin.test_functions;
127 var ok_count: usize = 0;
128 var skip_count: usize = 0;
129 var fail_count: usize = 0;
130 var progress = std.Progress{
131 .dont_print_on_dumb = true,
132 };
133 const root_node = progress.start("Test", test_fn_list.len);
134 const have_tty = progress.terminal != null and
135 (progress.supports_ansi_escape_codes or progress.is_windows_terminal);
136
137 var async_frame_buffer: []align(builtin.target.stackAlignment()) u8 = undefined;
138 // TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly
139 // ignores the alignment of the slice.
140 async_frame_buffer = &[_]u8{};
141
142 var leaks: usize = 0;
143 for (test_fn_list, 0..) |test_fn, i| {
144 std.testing.allocator_instance = .{};
145 defer {
146 if (std.testing.allocator_instance.deinit() == .leak) {
147 leaks += 1;
148 }
149 }
150 std.testing.log_level = .warn;
151
152 var test_node = root_node.start(test_fn.name, 0);
153 test_node.activate();
154 progress.refresh();
155 if (!have_tty) {
156 std.debug.print("{d}/{d} {s}... ", .{ i + 1, test_fn_list.len, test_fn.name });
157 }
158 if (test_fn.func()) |_| {
159 ok_count += 1;
160 test_node.end();
161 if (!have_tty) std.debug.print("OK\n", .{});
162 } else |err| switch (err) {
163 error.SkipZigTest => {
164 skip_count += 1;
165 progress.log("SKIP\n", .{});
166 test_node.end();
167 },
168 else => {
169 fail_count += 1;
170 progress.log("FAIL ({s})\n", .{@errorName(err)});
171 if (@errorReturnTrace()) |trace| {
172 std.debug.dumpStackTrace(trace.*);
173 }
174 test_node.end();
175 },
176 }
177 }
178 root_node.end();
179 if (ok_count == test_fn_list.len) {
180 std.debug.print("All {d} tests passed.\n", .{ok_count});
181 } else {
182 std.debug.print("{d} passed; {d} skipped; {d} failed.\n", .{ ok_count, skip_count, fail_count });
183 }
184 if (log_err_count != 0) {
185 std.debug.print("{d} errors were logged.\n", .{log_err_count});
186 }
187 if (leaks != 0) {
188 std.debug.print("{d} tests leaked memory.\n", .{leaks});
189 }
190 if (leaks != 0 or log_err_count != 0 or fail_count != 0) {
191 std.process.exit(1);
192 }
193}
194
195pub fn log(
196 comptime message_level: std.log.Level,
197 comptime scope: @Type(.EnumLiteral),
198 comptime format: []const u8,
199 args: anytype,
200) void {
201 if (@intFromEnum(message_level) <= @intFromEnum(std.log.Level.err)) {
202 log_err_count +|= 1;
203 }
204 if (@intFromEnum(message_level) <= @intFromEnum(std.testing.log_level)) {
205 std.debug.print(
206 "[" ++ @tagName(scope) ++ "] (" ++ @tagName(message_level) ++ "): " ++ format ++ "\n",
207 args,
208 );
209 }
210}
211
212/// Simpler main(), exercising fewer language features, so that
213/// work-in-progress backends can handle it.
214pub fn mainSimple() anyerror!void {
215 const enable_print = false;
216 const print_all = false;
217
218 var passed: u64 = 0;
219 var skipped: u64 = 0;
220 var failed: u64 = 0;
221 const stderr = if (enable_print) std.io.getStdErr() else {};
222 for (builtin.test_functions) |test_fn| {
223 if (enable_print and print_all) {
224 stderr.writeAll(test_fn.name) catch {};
225 stderr.writeAll("... ") catch {};
226 }
227 test_fn.func() catch |err| {
228 if (enable_print and !print_all) {
229 stderr.writeAll(test_fn.name) catch {};
230 stderr.writeAll("... ") catch {};
231 }
232 if (err != error.SkipZigTest) {
233 if (enable_print) stderr.writeAll("FAIL\n") catch {};
234 failed += 1;
235 if (!enable_print) return err;
236 continue;
237 }
238 if (enable_print) stderr.writeAll("SKIP\n") catch {};
239 skipped += 1;
240 continue;
241 };
242 if (enable_print and print_all) stderr.writeAll("PASS\n") catch {};
243 passed += 1;
244 }
245 if (enable_print) {
246 stderr.writer().print("{} passed, {} skipped, {} failed\n", .{ passed, skipped, failed }) catch {};
247 if (failed != 0) std.process.exit(1);
248 }
249}
lib/std/std.zig+3-1
...@@ -193,7 +193,9 @@ pub const valgrind = @import("valgrind.zig");...@@ -193,7 +193,9 @@ pub const valgrind = @import("valgrind.zig");
193/// Constants and types representing the Wasm binary format.193/// Constants and types representing the Wasm binary format.
194pub const wasm = @import("wasm.zig");194pub const wasm = @import("wasm.zig");
195195
196/// Tokenizing and parsing of Zig code and other Zig-specific language tooling.196/// Builds of the Zig compiler are distributed partly in source form. That
197/// source lives here. These APIs are provided as-is and have absolutely no API
198/// guarantees whatsoever.
197pub const zig = @import("zig.zig");199pub const zig = @import("zig.zig");
198200
199pub const start = @import("start.zig");201pub const start = @import("start.zig");
lib/std/zig.zig+595-6
...@@ -1,17 +1,14 @@...@@ -1,17 +1,14 @@
1pub const fmt = @import("zig/fmt.zig");
2
3pub const ErrorBundle = @import("zig/ErrorBundle.zig");1pub const ErrorBundle = @import("zig/ErrorBundle.zig");
4pub const Server = @import("zig/Server.zig");2pub const Server = @import("zig/Server.zig");
5pub const Client = @import("zig/Client.zig");3pub const Client = @import("zig/Client.zig");
6pub const Token = tokenizer.Token;4pub const Token = tokenizer.Token;
7pub const Tokenizer = tokenizer.Tokenizer;5pub const Tokenizer = tokenizer.Tokenizer;
8pub const fmtId = fmt.fmtId;
9pub const fmtEscapes = fmt.fmtEscapes;
10pub const isValidId = fmt.isValidId;
11pub const string_literal = @import("zig/string_literal.zig");6pub const string_literal = @import("zig/string_literal.zig");
12pub const number_literal = @import("zig/number_literal.zig");7pub const number_literal = @import("zig/number_literal.zig");
13pub const primitives = @import("zig/primitives.zig");8pub const primitives = @import("zig/primitives.zig");
14pub const Ast = @import("zig/Ast.zig");9pub const Ast = @import("zig/Ast.zig");
10pub const AstGen = @import("zig/AstGen.zig");
11pub const Zir = @import("zig/Zir.zig");
15pub const system = @import("zig/system.zig");12pub const system = @import("zig/system.zig");
16/// Deprecated: use `std.Target.Query`.13/// Deprecated: use `std.Target.Query`.
17pub const CrossTarget = std.Target.Query;14pub const CrossTarget = std.Target.Query;
...@@ -30,6 +27,36 @@ pub const c_translation = @import("zig/c_translation.zig");...@@ -30,6 +27,36 @@ pub const c_translation = @import("zig/c_translation.zig");
30pub const SrcHasher = std.crypto.hash.Blake3;27pub const SrcHasher = std.crypto.hash.Blake3;
31pub const SrcHash = [16]u8;28pub const SrcHash = [16]u8;
3229
30pub const Color = enum {
31 /// Determine whether stderr is a terminal or not automatically.
32 auto,
33 /// Assume stderr is not a terminal.
34 off,
35 /// Assume stderr is a terminal.
36 on,
37
38 pub fn get_tty_conf(color: Color) std.io.tty.Config {
39 return switch (color) {
40 .auto => std.io.tty.detectConfig(std.io.getStdErr()),
41 .on => .escape_codes,
42 .off => .no_color,
43 };
44 }
45
46 pub fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions {
47 const ttyconf = get_tty_conf(color);
48 return .{
49 .ttyconf = ttyconf,
50 .include_source_line = ttyconf != .no_color,
51 .include_reference_trace = ttyconf != .no_color,
52 };
53 }
54};
55
56/// There are many assumptions in the entire codebase that Zig source files can
57/// be byte-indexed with a u32 integer.
58pub const max_src_size = std.math.maxInt(u32);
59
33pub fn hashSrc(src: []const u8) SrcHash {60pub fn hashSrc(src: []const u8) SrcHash {
34 var out: SrcHash = undefined;61 var out: SrcHash = undefined;
35 SrcHasher.hash(src, &out, .{});62 SrcHasher.hash(src, &out, .{});
...@@ -315,11 +342,573 @@ pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![...@@ -315,11 +342,573 @@ pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![
315 return buffer.toOwnedSlice();342 return buffer.toOwnedSlice();
316}343}
317344
345pub const DeclIndex = enum(u32) {
346 _,
347
348 pub fn toOptional(i: DeclIndex) OptionalDeclIndex {
349 return @enumFromInt(@intFromEnum(i));
350 }
351};
352
353pub const OptionalDeclIndex = enum(u32) {
354 none = std.math.maxInt(u32),
355 _,
356
357 pub fn init(oi: ?DeclIndex) OptionalDeclIndex {
358 return @enumFromInt(@intFromEnum(oi orelse return .none));
359 }
360
361 pub fn unwrap(oi: OptionalDeclIndex) ?DeclIndex {
362 if (oi == .none) return null;
363 return @enumFromInt(@intFromEnum(oi));
364 }
365};
366
367/// Resolving a source location into a byte offset may require doing work
368/// that we would rather not do unless the error actually occurs.
369/// Therefore we need a data structure that contains the information necessary
370/// to lazily produce a `SrcLoc` as required.
371/// Most of the offsets in this data structure are relative to the containing Decl.
372/// This makes the source location resolve properly even when a Decl gets
373/// shifted up or down in the file, as long as the Decl's contents itself
374/// do not change.
375pub const LazySrcLoc = union(enum) {
376 /// When this tag is set, the code that constructed this `LazySrcLoc` is asserting
377 /// that all code paths which would need to resolve the source location are
378 /// unreachable. If you are debugging this tag incorrectly being this value,
379 /// look into using reverse-continue with a memory watchpoint to see where the
380 /// value is being set to this tag.
381 unneeded,
382 /// Means the source location points to an entire file; not any particular
383 /// location within the file. `file_scope` union field will be active.
384 entire_file,
385 /// The source location points to a byte offset within a source file,
386 /// offset from 0. The source file is determined contextually.
387 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
388 byte_abs: u32,
389 /// The source location points to a token within a source file,
390 /// offset from 0. The source file is determined contextually.
391 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
392 token_abs: u32,
393 /// The source location points to an AST node within a source file,
394 /// offset from 0. The source file is determined contextually.
395 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
396 node_abs: u32,
397 /// The source location points to a byte offset within a source file,
398 /// offset from the byte offset of the Decl within the file.
399 /// The Decl is determined contextually.
400 byte_offset: u32,
401 /// This data is the offset into the token list from the Decl token.
402 /// The Decl is determined contextually.
403 token_offset: u32,
404 /// The source location points to an AST node, which is this value offset
405 /// from its containing Decl node AST index.
406 /// The Decl is determined contextually.
407 node_offset: TracedOffset,
408 /// The source location points to the main token of an AST node, found
409 /// by taking this AST node index offset from the containing Decl AST node.
410 /// The Decl is determined contextually.
411 node_offset_main_token: i32,
412 /// The source location points to the beginning of a struct initializer.
413 /// The Decl is determined contextually.
414 node_offset_initializer: i32,
415 /// The source location points to a variable declaration type expression,
416 /// found by taking this AST node index offset from the containing
417 /// Decl AST node, which points to a variable declaration AST node. Next, navigate
418 /// to the type expression.
419 /// The Decl is determined contextually.
420 node_offset_var_decl_ty: i32,
421 /// The source location points to the alignment expression of a var decl.
422 /// The Decl is determined contextually.
423 node_offset_var_decl_align: i32,
424 /// The source location points to the linksection expression of a var decl.
425 /// The Decl is determined contextually.
426 node_offset_var_decl_section: i32,
427 /// The source location points to the addrspace expression of a var decl.
428 /// The Decl is determined contextually.
429 node_offset_var_decl_addrspace: i32,
430 /// The source location points to the initializer of a var decl.
431 /// The Decl is determined contextually.
432 node_offset_var_decl_init: i32,
433 /// The source location points to the first parameter of a builtin
434 /// function call, found by taking this AST node index offset from the containing
435 /// Decl AST node, which points to a builtin call AST node. Next, navigate
436 /// to the first parameter.
437 /// The Decl is determined contextually.
438 node_offset_builtin_call_arg0: i32,
439 /// Same as `node_offset_builtin_call_arg0` except arg index 1.
440 node_offset_builtin_call_arg1: i32,
441 node_offset_builtin_call_arg2: i32,
442 node_offset_builtin_call_arg3: i32,
443 node_offset_builtin_call_arg4: i32,
444 node_offset_builtin_call_arg5: i32,
445 /// Like `node_offset_builtin_call_arg0` but recurses through arbitrarily many calls
446 /// to pointer cast builtins.
447 node_offset_ptrcast_operand: i32,
448 /// The source location points to the index expression of an array access
449 /// expression, found by taking this AST node index offset from the containing
450 /// Decl AST node, which points to an array access AST node. Next, navigate
451 /// to the index expression.
452 /// The Decl is determined contextually.
453 node_offset_array_access_index: i32,
454 /// The source location points to the LHS of a slice expression
455 /// expression, found by taking this AST node index offset from the containing
456 /// Decl AST node, which points to a slice AST node. Next, navigate
457 /// to the sentinel expression.
458 /// The Decl is determined contextually.
459 node_offset_slice_ptr: i32,
460 /// The source location points to start expression of a slice expression
461 /// expression, found by taking this AST node index offset from the containing
462 /// Decl AST node, which points to a slice AST node. Next, navigate
463 /// to the sentinel expression.
464 /// The Decl is determined contextually.
465 node_offset_slice_start: i32,
466 /// The source location points to the end expression of a slice
467 /// expression, found by taking this AST node index offset from the containing
468 /// Decl AST node, which points to a slice AST node. Next, navigate
469 /// to the sentinel expression.
470 /// The Decl is determined contextually.
471 node_offset_slice_end: i32,
472 /// The source location points to the sentinel expression of a slice
473 /// expression, found by taking this AST node index offset from the containing
474 /// Decl AST node, which points to a slice AST node. Next, navigate
475 /// to the sentinel expression.
476 /// The Decl is determined contextually.
477 node_offset_slice_sentinel: i32,
478 /// The source location points to the callee expression of a function
479 /// call expression, found by taking this AST node index offset from the containing
480 /// Decl AST node, which points to a function call AST node. Next, navigate
481 /// to the callee expression.
482 /// The Decl is determined contextually.
483 node_offset_call_func: i32,
484 /// The payload is offset from the containing Decl AST node.
485 /// The source location points to the field name of:
486 /// * a field access expression (`a.b`), or
487 /// * the callee of a method call (`a.b()`)
488 /// The Decl is determined contextually.
489 node_offset_field_name: i32,
490 /// The payload is offset from the containing Decl AST node.
491 /// The source location points to the field name of the operand ("b" node)
492 /// of a field initialization expression (`.a = b`)
493 /// The Decl is determined contextually.
494 node_offset_field_name_init: i32,
495 /// The source location points to the pointer of a pointer deref expression,
496 /// found by taking this AST node index offset from the containing
497 /// Decl AST node, which points to a pointer deref AST node. Next, navigate
498 /// to the pointer expression.
499 /// The Decl is determined contextually.
500 node_offset_deref_ptr: i32,
501 /// The source location points to the assembly source code of an inline assembly
502 /// expression, found by taking this AST node index offset from the containing
503 /// Decl AST node, which points to inline assembly AST node. Next, navigate
504 /// to the asm template source code.
505 /// The Decl is determined contextually.
506 node_offset_asm_source: i32,
507 /// The source location points to the return type of an inline assembly
508 /// expression, found by taking this AST node index offset from the containing
509 /// Decl AST node, which points to inline assembly AST node. Next, navigate
510 /// to the return type expression.
511 /// The Decl is determined contextually.
512 node_offset_asm_ret_ty: i32,
513 /// The source location points to the condition expression of an if
514 /// expression, found by taking this AST node index offset from the containing
515 /// Decl AST node, which points to an if expression AST node. Next, navigate
516 /// to the condition expression.
517 /// The Decl is determined contextually.
518 node_offset_if_cond: i32,
519 /// The source location points to a binary expression, such as `a + b`, found
520 /// by taking this AST node index offset from the containing Decl AST node.
521 /// The Decl is determined contextually.
522 node_offset_bin_op: i32,
523 /// The source location points to the LHS of a binary expression, found
524 /// by taking this AST node index offset from the containing Decl AST node,
525 /// which points to a binary expression AST node. Next, navigate to the LHS.
526 /// The Decl is determined contextually.
527 node_offset_bin_lhs: i32,
528 /// The source location points to the RHS of a binary expression, found
529 /// by taking this AST node index offset from the containing Decl AST node,
530 /// which points to a binary expression AST node. Next, navigate to the RHS.
531 /// The Decl is determined contextually.
532 node_offset_bin_rhs: i32,
533 /// The source location points to the operand of a switch expression, found
534 /// by taking this AST node index offset from the containing Decl AST node,
535 /// which points to a switch expression AST node. Next, navigate to the operand.
536 /// The Decl is determined contextually.
537 node_offset_switch_operand: i32,
538 /// The source location points to the else/`_` prong of a switch expression, found
539 /// by taking this AST node index offset from the containing Decl AST node,
540 /// which points to a switch expression AST node. Next, navigate to the else/`_` prong.
541 /// The Decl is determined contextually.
542 node_offset_switch_special_prong: i32,
543 /// The source location points to all the ranges of a switch expression, found
544 /// by taking this AST node index offset from the containing Decl AST node,
545 /// which points to a switch expression AST node. Next, navigate to any of the
546 /// range nodes. The error applies to all of them.
547 /// The Decl is determined contextually.
548 node_offset_switch_range: i32,
549 /// The source location points to the capture of a switch_prong.
550 /// The Decl is determined contextually.
551 node_offset_switch_prong_capture: i32,
552 /// The source location points to the tag capture of a switch_prong.
553 /// The Decl is determined contextually.
554 node_offset_switch_prong_tag_capture: i32,
555 /// The source location points to the align expr of a function type
556 /// expression, found by taking this AST node index offset from the containing
557 /// Decl AST node, which points to a function type AST node. Next, navigate to
558 /// the calling convention node.
559 /// The Decl is determined contextually.
560 node_offset_fn_type_align: i32,
561 /// The source location points to the addrspace expr of a function type
562 /// expression, found by taking this AST node index offset from the containing
563 /// Decl AST node, which points to a function type AST node. Next, navigate to
564 /// the calling convention node.
565 /// The Decl is determined contextually.
566 node_offset_fn_type_addrspace: i32,
567 /// The source location points to the linksection expr of a function type
568 /// expression, found by taking this AST node index offset from the containing
569 /// Decl AST node, which points to a function type AST node. Next, navigate to
570 /// the calling convention node.
571 /// The Decl is determined contextually.
572 node_offset_fn_type_section: i32,
573 /// The source location points to the calling convention of a function type
574 /// expression, found by taking this AST node index offset from the containing
575 /// Decl AST node, which points to a function type AST node. Next, navigate to
576 /// the calling convention node.
577 /// The Decl is determined contextually.
578 node_offset_fn_type_cc: i32,
579 /// The source location points to the return type of a function type
580 /// expression, found by taking this AST node index offset from the containing
581 /// Decl AST node, which points to a function type AST node. Next, navigate to
582 /// the return type node.
583 /// The Decl is determined contextually.
584 node_offset_fn_type_ret_ty: i32,
585 node_offset_param: i32,
586 token_offset_param: i32,
587 /// The source location points to the type expression of an `anyframe->T`
588 /// expression, found by taking this AST node index offset from the containing
589 /// Decl AST node, which points to a `anyframe->T` expression AST node. Next, navigate
590 /// to the type expression.
591 /// The Decl is determined contextually.
592 node_offset_anyframe_type: i32,
593 /// The source location points to the string literal of `extern "foo"`, found
594 /// by taking this AST node index offset from the containing
595 /// Decl AST node, which points to a function prototype or variable declaration
596 /// expression AST node. Next, navigate to the string literal of the `extern "foo"`.
597 /// The Decl is determined contextually.
598 node_offset_lib_name: i32,
599 /// The source location points to the len expression of an `[N:S]T`
600 /// expression, found by taking this AST node index offset from the containing
601 /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate
602 /// to the len expression.
603 /// The Decl is determined contextually.
604 node_offset_array_type_len: i32,
605 /// The source location points to the sentinel expression of an `[N:S]T`
606 /// expression, found by taking this AST node index offset from the containing
607 /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate
608 /// to the sentinel expression.
609 /// The Decl is determined contextually.
610 node_offset_array_type_sentinel: i32,
611 /// The source location points to the elem expression of an `[N:S]T`
612 /// expression, found by taking this AST node index offset from the containing
613 /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate
614 /// to the elem expression.
615 /// The Decl is determined contextually.
616 node_offset_array_type_elem: i32,
617 /// The source location points to the operand of an unary expression.
618 /// The Decl is determined contextually.
619 node_offset_un_op: i32,
620 /// The source location points to the elem type of a pointer.
621 /// The Decl is determined contextually.
622 node_offset_ptr_elem: i32,
623 /// The source location points to the sentinel of a pointer.
624 /// The Decl is determined contextually.
625 node_offset_ptr_sentinel: i32,
626 /// The source location points to the align expr of a pointer.
627 /// The Decl is determined contextually.
628 node_offset_ptr_align: i32,
629 /// The source location points to the addrspace expr of a pointer.
630 /// The Decl is determined contextually.
631 node_offset_ptr_addrspace: i32,
632 /// The source location points to the bit-offset of a pointer.
633 /// The Decl is determined contextually.
634 node_offset_ptr_bitoffset: i32,
635 /// The source location points to the host size of a pointer.
636 /// The Decl is determined contextually.
637 node_offset_ptr_hostsize: i32,
638 /// The source location points to the tag type of an union or an enum.
639 /// The Decl is determined contextually.
640 node_offset_container_tag: i32,
641 /// The source location points to the default value of a field.
642 /// The Decl is determined contextually.
643 node_offset_field_default: i32,
644 /// The source location points to the type of an array or struct initializer.
645 /// The Decl is determined contextually.
646 node_offset_init_ty: i32,
647 /// The source location points to the LHS of an assignment.
648 /// The Decl is determined contextually.
649 node_offset_store_ptr: i32,
650 /// The source location points to the RHS of an assignment.
651 /// The Decl is determined contextually.
652 node_offset_store_operand: i32,
653 /// The source location points to the operand of a `return` statement, or
654 /// the `return` itself if there is no explicit operand.
655 /// The Decl is determined contextually.
656 node_offset_return_operand: i32,
657 /// The source location points to a for loop input.
658 /// The Decl is determined contextually.
659 for_input: struct {
660 /// Points to the for loop AST node.
661 for_node_offset: i32,
662 /// Picks one of the inputs from the condition.
663 input_index: u32,
664 },
665 /// The source location points to one of the captures of a for loop, found
666 /// by taking this AST node index offset from the containing
667 /// Decl AST node, which points to one of the input nodes of a for loop.
668 /// Next, navigate to the corresponding capture.
669 /// The Decl is determined contextually.
670 for_capture_from_input: i32,
671 /// The source location points to the argument node of a function call.
672 call_arg: struct {
673 decl: DeclIndex,
674 /// Points to the function call AST node.
675 call_node_offset: i32,
676 /// The index of the argument the source location points to.
677 arg_index: u32,
678 },
679 fn_proto_param: struct {
680 decl: DeclIndex,
681 /// Points to the function prototype AST node.
682 fn_proto_node_offset: i32,
683 /// The index of the parameter the source location points to.
684 param_index: u32,
685 },
686 array_cat_lhs: ArrayCat,
687 array_cat_rhs: ArrayCat,
688
689 const ArrayCat = struct {
690 /// Points to the array concat AST node.
691 array_cat_offset: i32,
692 /// The index of the element the source location points to.
693 elem_index: u32,
694 };
695
696 pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease;
697
698 noinline fn nodeOffsetDebug(node_offset: i32) LazySrcLoc {
699 var result: LazySrcLoc = .{ .node_offset = .{ .x = node_offset } };
700 result.node_offset.trace.addAddr(@returnAddress(), "init");
701 return result;
702 }
703
704 fn nodeOffsetRelease(node_offset: i32) LazySrcLoc {
705 return .{ .node_offset = .{ .x = node_offset } };
706 }
707
708 /// This wraps a simple integer in debug builds so that later on we can find out
709 /// where in semantic analysis the value got set.
710 pub const TracedOffset = struct {
711 x: i32,
712 trace: std.debug.Trace = .{},
713
714 const want_tracing = false;
715 };
716};
717
318const std = @import("std.zig");718const std = @import("std.zig");
319const tokenizer = @import("zig/tokenizer.zig");719const tokenizer = @import("zig/tokenizer.zig");
320const assert = std.debug.assert;720const assert = std.debug.assert;
321const Allocator = std.mem.Allocator;721const Allocator = std.mem.Allocator;
322722
723/// Return a Formatter for a Zig identifier
724pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) {
725 return .{ .data = bytes };
726}
727
728/// Print the string as a Zig identifier escaping it with @"" syntax if needed.
729fn formatId(
730 bytes: []const u8,
731 comptime unused_format_string: []const u8,
732 options: std.fmt.FormatOptions,
733 writer: anytype,
734) !void {
735 _ = unused_format_string;
736 if (isValidId(bytes)) {
737 return writer.writeAll(bytes);
738 }
739 try writer.writeAll("@\"");
740 try stringEscape(bytes, "", options, writer);
741 try writer.writeByte('"');
742}
743
744/// Return a Formatter for Zig Escapes of a double quoted string.
745/// The format specifier must be one of:
746/// * `{}` treats contents as a double-quoted string.
747/// * `{'}` treats contents as a single-quoted string.
748pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(stringEscape) {
749 return .{ .data = bytes };
750}
751
752test "escape invalid identifiers" {
753 const expectFmt = std.testing.expectFmt;
754 try expectFmt("@\"while\"", "{}", .{fmtId("while")});
755 try expectFmt("hello", "{}", .{fmtId("hello")});
756 try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")});
757 try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")});
758 try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")});
759 try expectFmt(
760 \\" \\ hi \x07 \x11 " derp \'"
761 , "\"{'}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
762 try expectFmt(
763 \\" \\ hi \x07 \x11 \" derp '"
764 , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
765}
766
767/// Print the string as escaped contents of a double quoted or single-quoted string.
768/// Format `{}` treats contents as a double-quoted string.
769/// Format `{'}` treats contents as a single-quoted string.
770pub fn stringEscape(
771 bytes: []const u8,
772 comptime f: []const u8,
773 options: std.fmt.FormatOptions,
774 writer: anytype,
775) !void {
776 _ = options;
777 for (bytes) |byte| switch (byte) {
778 '\n' => try writer.writeAll("\\n"),
779 '\r' => try writer.writeAll("\\r"),
780 '\t' => try writer.writeAll("\\t"),
781 '\\' => try writer.writeAll("\\\\"),
782 '"' => {
783 if (f.len == 1 and f[0] == '\'') {
784 try writer.writeByte('"');
785 } else if (f.len == 0) {
786 try writer.writeAll("\\\"");
787 } else {
788 @compileError("expected {} or {'}, found {" ++ f ++ "}");
789 }
790 },
791 '\'' => {
792 if (f.len == 1 and f[0] == '\'') {
793 try writer.writeAll("\\'");
794 } else if (f.len == 0) {
795 try writer.writeByte('\'');
796 } else {
797 @compileError("expected {} or {'}, found {" ++ f ++ "}");
798 }
799 },
800 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte),
801 // Use hex escapes for rest any unprintable characters.
802 else => {
803 try writer.writeAll("\\x");
804 try std.fmt.formatInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }, writer);
805 },
806 };
807}
808
809pub fn isValidId(bytes: []const u8) bool {
810 if (bytes.len == 0) return false;
811 if (std.mem.eql(u8, bytes, "_")) return false;
812 for (bytes, 0..) |c, i| {
813 switch (c) {
814 '_', 'a'...'z', 'A'...'Z' => {},
815 '0'...'9' => if (i == 0) return false,
816 else => return false,
817 }
818 }
819 return std.zig.Token.getKeyword(bytes) == null;
820}
821
822test isValidId {
823 try std.testing.expect(!isValidId(""));
824 try std.testing.expect(isValidId("foobar"));
825 try std.testing.expect(!isValidId("a b c"));
826 try std.testing.expect(!isValidId("3d"));
827 try std.testing.expect(!isValidId("enum"));
828 try std.testing.expect(isValidId("i386"));
829}
830
831pub fn readSourceFileToEndAlloc(
832 allocator: Allocator,
833 input: std.fs.File,
834 size_hint: ?usize,
835) ![:0]u8 {
836 const source_code = input.readToEndAllocOptions(
837 allocator,
838 max_src_size,
839 size_hint,
840 @alignOf(u16),
841 0,
842 ) catch |err| switch (err) {
843 error.ConnectionResetByPeer => unreachable,
844 error.ConnectionTimedOut => unreachable,
845 error.NotOpenForReading => unreachable,
846 else => |e| return e,
847 };
848 errdefer allocator.free(source_code);
849
850 // Detect unsupported file types with their Byte Order Mark
851 const unsupported_boms = [_][]const u8{
852 "\xff\xfe\x00\x00", // UTF-32 little endian
853 "\xfe\xff\x00\x00", // UTF-32 big endian
854 "\xfe\xff", // UTF-16 big endian
855 };
856 for (unsupported_boms) |bom| {
857 if (std.mem.startsWith(u8, source_code, bom)) {
858 return error.UnsupportedEncoding;
859 }
860 }
861
862 // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8
863 if (std.mem.startsWith(u8, source_code, "\xff\xfe")) {
864 const source_code_utf16_le = std.mem.bytesAsSlice(u16, source_code);
865 const source_code_utf8 = std.unicode.utf16LeToUtf8AllocZ(allocator, source_code_utf16_le) catch |err| switch (err) {
866 error.DanglingSurrogateHalf => error.UnsupportedEncoding,
867 error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding,
868 error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding,
869 else => |e| return e,
870 };
871
872 allocator.free(source_code);
873 return source_code_utf8;
874 }
875
876 return source_code;
877}
878
879pub fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void {
880 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
881 try wip_errors.init(gpa);
882 defer wip_errors.deinit();
883
884 try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors);
885
886 var error_bundle = try wip_errors.toOwnedBundle("");
887 defer error_bundle.deinit(gpa);
888 error_bundle.renderToStdErr(color.renderOptions());
889}
890
891pub fn putAstErrorsIntoBundle(
892 gpa: Allocator,
893 tree: Ast,
894 path: []const u8,
895 wip_errors: *std.zig.ErrorBundle.Wip,
896) Allocator.Error!void {
897 var zir = try AstGen.generate(gpa, tree);
898 defer zir.deinit(gpa);
899
900 try wip_errors.addZirErrorMessages(zir, tree, tree.source, path);
901}
902
323test {903test {
324 @import("std").testing.refAllDecls(@This());904 _ = Ast;
905 _ = AstRlAnnotate;
906 _ = BuiltinFn;
907 _ = Client;
908 _ = ErrorBundle;
909 _ = Server;
910 _ = number_literal;
911 _ = primitives;
912 _ = string_literal;
913 _ = system;
325}914}
lib/std/zig/Ast.zig+42-4
...@@ -32,6 +32,12 @@ pub const Location = struct {...@@ -32,6 +32,12 @@ pub const Location = struct {
32 line_end: usize,32 line_end: usize,
33};33};
3434
35pub const Span = struct {
36 start: u32,
37 end: u32,
38 main: u32,
39};
40
35pub fn deinit(tree: *Ast, gpa: Allocator) void {41pub fn deinit(tree: *Ast, gpa: Allocator) void {
36 tree.tokens.deinit(gpa);42 tree.tokens.deinit(gpa);
37 tree.nodes.deinit(gpa);43 tree.nodes.deinit(gpa);
...@@ -105,9 +111,7 @@ pub fn parse(gpa: Allocator, source: [:0]const u8, mode: Mode) Allocator.Error!A...@@ -105,9 +111,7 @@ pub fn parse(gpa: Allocator, source: [:0]const u8, mode: Mode) Allocator.Error!A
105 };111 };
106}112}
107113
108/// `gpa` is used for allocating the resulting formatted source code, as well as114/// `gpa` is used for allocating the resulting formatted source code.
109/// for allocating extra stack memory if needed, because this function utilizes recursion.
110/// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006.
111/// Caller owns the returned slice of bytes, allocated with `gpa`.115/// Caller owns the returned slice of bytes, allocated with `gpa`.
112pub fn render(tree: Ast, gpa: Allocator) RenderError![]u8 {116pub fn render(tree: Ast, gpa: Allocator) RenderError![]u8 {
113 var buffer = std.ArrayList(u8).init(gpa);117 var buffer = std.ArrayList(u8).init(gpa);
...@@ -3535,6 +3539,39 @@ pub const Node = struct {...@@ -3535,6 +3539,39 @@ pub const Node = struct {
3535 };3539 };
3536};3540};
35373541
3542pub fn nodeToSpan(tree: *const Ast, node: u32) Span {
3543 return tokensToSpan(
3544 tree,
3545 tree.firstToken(node),
3546 tree.lastToken(node),
3547 tree.nodes.items(.main_token)[node],
3548 );
3549}
3550
3551pub fn tokenToSpan(tree: *const Ast, token: Ast.TokenIndex) Span {
3552 return tokensToSpan(tree, token, token, token);
3553}
3554
3555pub fn tokensToSpan(tree: *const Ast, start: Ast.TokenIndex, end: Ast.TokenIndex, main: Ast.TokenIndex) Span {
3556 const token_starts = tree.tokens.items(.start);
3557 var start_tok = start;
3558 var end_tok = end;
3559
3560 if (tree.tokensOnSameLine(start, end)) {
3561 // do nothing
3562 } else if (tree.tokensOnSameLine(start, main)) {
3563 end_tok = main;
3564 } else if (tree.tokensOnSameLine(main, end)) {
3565 start_tok = main;
3566 } else {
3567 start_tok = main;
3568 end_tok = main;
3569 }
3570 const start_off = token_starts[start_tok];
3571 const end_off = token_starts[end_tok] + @as(u32, @intCast(tree.tokenSlice(end_tok).len));
3572 return Span{ .start = start_off, .end = end_off, .main = token_starts[main] };
3573}
3574
3538const std = @import("../std.zig");3575const std = @import("../std.zig");
3539const assert = std.debug.assert;3576const assert = std.debug.assert;
3540const testing = std.testing;3577const testing = std.testing;
...@@ -3546,5 +3583,6 @@ const Parse = @import("Parse.zig");...@@ -3546,5 +3583,6 @@ const Parse = @import("Parse.zig");
3546const private_render = @import("./render.zig");3583const private_render = @import("./render.zig");
35473584
3548test {3585test {
3549 testing.refAllDecls(@This());3586 _ = Parse;
3587 _ = private_render;
3550}3588}
lib/std/zig/AstGen.zig created+13661
...@@ -0,0 +1,13661 @@
1//! Ingests an AST and produces ZIR code.
2const AstGen = @This();
3
4const std = @import("std");
5const Ast = std.zig.Ast;
6const mem = std.mem;
7const Allocator = std.mem.Allocator;
8const assert = std.debug.assert;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const StringIndexAdapter = std.hash_map.StringIndexAdapter;
11const StringIndexContext = std.hash_map.StringIndexContext;
12
13const isPrimitive = std.zig.primitives.isPrimitive;
14
15const Zir = std.zig.Zir;
16const BuiltinFn = std.zig.BuiltinFn;
17const AstRlAnnotate = std.zig.AstRlAnnotate;
18
19gpa: Allocator,
20tree: *const Ast,
21/// The set of nodes which, given the choice, must expose a result pointer to
22/// sub-expressions. See `AstRlAnnotate` for details.
23nodes_need_rl: *const AstRlAnnotate.RlNeededSet,
24instructions: std.MultiArrayList(Zir.Inst) = .{},
25extra: ArrayListUnmanaged(u32) = .{},
26string_bytes: ArrayListUnmanaged(u8) = .{},
27/// Tracks the current byte offset within the source file.
28/// Used to populate line deltas in the ZIR. AstGen maintains
29/// this "cursor" throughout the entire AST lowering process in order
30/// to avoid starting over the line/column scan for every declaration, which
31/// would be O(N^2).
32source_offset: u32 = 0,
33/// Tracks the corresponding line of `source_offset`.
34/// This value is absolute.
35source_line: u32 = 0,
36/// Tracks the corresponding column of `source_offset`.
37/// This value is absolute.
38source_column: u32 = 0,
39/// Used for temporary allocations; freed after AstGen is complete.
40/// The resulting ZIR code has no references to anything in this arena.
41arena: Allocator,
42string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
43compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .{},
44/// The topmost block of the current function.
45fn_block: ?*GenZir = null,
46fn_var_args: bool = false,
47/// The return type of the current function. This may be a trivial `Ref`, or
48/// otherwise it refers to a `ret_type` instruction.
49fn_ret_ty: Zir.Inst.Ref = .none,
50/// Maps string table indexes to the first `@import` ZIR instruction
51/// that uses this string as the operand.
52imports: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .{},
53/// Used for temporary storage when building payloads.
54scratch: std.ArrayListUnmanaged(u32) = .{},
55/// Whenever a `ref` instruction is needed, it is created and saved in this
56/// table instead of being immediately appended to the current block body.
57/// Then, when the instruction is being added to the parent block (typically from
58/// setBlockBody), if it has a ref_table entry, then the ref instruction is added
59/// there. This makes sure two properties are upheld:
60/// 1. All pointers to the same locals return the same address. This is required
61/// to be compliant with the language specification.
62/// 2. `ref` instructions will dominate their uses. This is a required property
63/// of ZIR.
64/// The key is the ref operand; the value is the ref instruction.
65ref_table: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
66
67const InnerError = error{ OutOfMemory, AnalysisFail };
68
69fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 {
70 const fields = std.meta.fields(@TypeOf(extra));
71 try astgen.extra.ensureUnusedCapacity(astgen.gpa, fields.len);
72 return addExtraAssumeCapacity(astgen, extra);
73}
74
75fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
76 const fields = std.meta.fields(@TypeOf(extra));
77 const extra_index: u32 = @intCast(astgen.extra.items.len);
78 astgen.extra.items.len += fields.len;
79 setExtra(astgen, extra_index, extra);
80 return extra_index;
81}
82
83fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
84 const fields = std.meta.fields(@TypeOf(extra));
85 var i = index;
86 inline for (fields) |field| {
87 astgen.extra.items[i] = switch (field.type) {
88 u32 => @field(extra, field.name),
89
90 Zir.Inst.Ref,
91 Zir.Inst.Index,
92 Zir.Inst.Declaration.Name,
93 Zir.NullTerminatedString,
94 => @intFromEnum(@field(extra, field.name)),
95
96 i32,
97 Zir.Inst.Call.Flags,
98 Zir.Inst.BuiltinCall.Flags,
99 Zir.Inst.SwitchBlock.Bits,
100 Zir.Inst.SwitchBlockErrUnion.Bits,
101 Zir.Inst.FuncFancy.Bits,
102 Zir.Inst.Declaration.Flags,
103 => @bitCast(@field(extra, field.name)),
104
105 else => @compileError("bad field type"),
106 };
107 i += 1;
108 }
109}
110
111fn reserveExtra(astgen: *AstGen, size: usize) Allocator.Error!u32 {
112 const extra_index: u32 = @intCast(astgen.extra.items.len);
113 try astgen.extra.resize(astgen.gpa, extra_index + size);
114 return extra_index;
115}
116
117fn appendRefs(astgen: *AstGen, refs: []const Zir.Inst.Ref) !void {
118 return astgen.extra.appendSlice(astgen.gpa, @ptrCast(refs));
119}
120
121fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void {
122 astgen.extra.appendSliceAssumeCapacity(@ptrCast(refs));
123}
124
125pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
126 var arena = std.heap.ArenaAllocator.init(gpa);
127 defer arena.deinit();
128
129 var nodes_need_rl = try AstRlAnnotate.annotate(gpa, arena.allocator(), tree);
130 defer nodes_need_rl.deinit(gpa);
131
132 var astgen: AstGen = .{
133 .gpa = gpa,
134 .arena = arena.allocator(),
135 .tree = &tree,
136 .nodes_need_rl = &nodes_need_rl,
137 };
138 defer astgen.deinit(gpa);
139
140 // String table index 0 is reserved for `NullTerminatedString.empty`.
141 try astgen.string_bytes.append(gpa, 0);
142
143 // We expect at least as many ZIR instructions and extra data items
144 // as AST nodes.
145 try astgen.instructions.ensureTotalCapacity(gpa, tree.nodes.len);
146
147 // First few indexes of extra are reserved and set at the end.
148 const reserved_count = @typeInfo(Zir.ExtraIndex).Enum.fields.len;
149 try astgen.extra.ensureTotalCapacity(gpa, tree.nodes.len + reserved_count);
150 astgen.extra.items.len += reserved_count;
151
152 var top_scope: Scope.Top = .{};
153
154 var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
155 var gen_scope: GenZir = .{
156 .is_comptime = true,
157 .parent = &top_scope.base,
158 .anon_name_strategy = .parent,
159 .decl_node_index = 0,
160 .decl_line = 0,
161 .astgen = &astgen,
162 .instructions = &gz_instructions,
163 .instructions_top = 0,
164 };
165 defer gz_instructions.deinit(gpa);
166
167 // The AST -> ZIR lowering process assumes an AST that does not have any
168 // parse errors.
169 if (tree.errors.len == 0) {
170 if (AstGen.structDeclInner(
171 &gen_scope,
172 &gen_scope.base,
173 0,
174 tree.containerDeclRoot(),
175 .Auto,
176 0,
177 )) |struct_decl_ref| {
178 assert(struct_decl_ref.toIndex().? == .main_struct_inst);
179 } else |err| switch (err) {
180 error.OutOfMemory => return error.OutOfMemory,
181 error.AnalysisFail => {}, // Handled via compile_errors below.
182 }
183 } else {
184 try lowerAstErrors(&astgen);
185 }
186
187 const err_index = @intFromEnum(Zir.ExtraIndex.compile_errors);
188 if (astgen.compile_errors.items.len == 0) {
189 astgen.extra.items[err_index] = 0;
190 } else {
191 try astgen.extra.ensureUnusedCapacity(gpa, 1 + astgen.compile_errors.items.len *
192 @typeInfo(Zir.Inst.CompileErrors.Item).Struct.fields.len);
193
194 astgen.extra.items[err_index] = astgen.addExtraAssumeCapacity(Zir.Inst.CompileErrors{
195 .items_len = @intCast(astgen.compile_errors.items.len),
196 });
197
198 for (astgen.compile_errors.items) |item| {
199 _ = astgen.addExtraAssumeCapacity(item);
200 }
201 }
202
203 const imports_index = @intFromEnum(Zir.ExtraIndex.imports);
204 if (astgen.imports.count() == 0) {
205 astgen.extra.items[imports_index] = 0;
206 } else {
207 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Imports).Struct.fields.len +
208 astgen.imports.count() * @typeInfo(Zir.Inst.Imports.Item).Struct.fields.len);
209
210 astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{
211 .imports_len = @intCast(astgen.imports.count()),
212 });
213
214 var it = astgen.imports.iterator();
215 while (it.next()) |entry| {
216 _ = astgen.addExtraAssumeCapacity(Zir.Inst.Imports.Item{
217 .name = entry.key_ptr.*,
218 .token = entry.value_ptr.*,
219 });
220 }
221 }
222
223 return Zir{
224 .instructions = astgen.instructions.toOwnedSlice(),
225 .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
226 .extra = try astgen.extra.toOwnedSlice(gpa),
227 };
228}
229
230fn deinit(astgen: *AstGen, gpa: Allocator) void {
231 astgen.instructions.deinit(gpa);
232 astgen.extra.deinit(gpa);
233 astgen.string_table.deinit(gpa);
234 astgen.string_bytes.deinit(gpa);
235 astgen.compile_errors.deinit(gpa);
236 astgen.imports.deinit(gpa);
237 astgen.scratch.deinit(gpa);
238 astgen.ref_table.deinit(gpa);
239}
240
241const ResultInfo = struct {
242 /// The semantics requested for the result location
243 rl: Loc,
244
245 /// The "operator" consuming the result location
246 ctx: Context = .none,
247
248 /// Turns a `coerced_ty` back into a `ty`. Should be called at branch points
249 /// such as if and switch expressions.
250 fn br(ri: ResultInfo) ResultInfo {
251 return switch (ri.rl) {
252 .coerced_ty => |ty| .{
253 .rl = .{ .ty = ty },
254 .ctx = ri.ctx,
255 },
256 else => ri,
257 };
258 }
259
260 fn zirTag(ri: ResultInfo) Zir.Inst.Tag {
261 switch (ri.rl) {
262 .ty => return switch (ri.ctx) {
263 .shift_op => .as_shift_operand,
264 else => .as_node,
265 },
266 else => unreachable,
267 }
268 }
269
270 const Loc = union(enum) {
271 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
272 /// expression should be generated. The result instruction from the expression must
273 /// be ignored.
274 discard,
275 /// The expression has an inferred type, and it will be evaluated as an rvalue.
276 none,
277 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
278 ty: Zir.Inst.Ref,
279 /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion,
280 /// so no `as` instruction needs to be emitted.
281 coerced_ty: Zir.Inst.Ref,
282 /// The expression must generate a pointer rather than a value. For example, the left hand side
283 /// of an assignment uses this kind of result location.
284 ref,
285 /// The expression must generate a pointer rather than a value, and the pointer will be coerced
286 /// by other code to this type, which is guaranteed by earlier instructions to be a pointer type.
287 ref_coerced_ty: Zir.Inst.Ref,
288 /// The expression must store its result into this typed pointer. The result instruction
289 /// from the expression must be ignored.
290 ptr: PtrResultLoc,
291 /// The expression must store its result into this allocation, which has an inferred type.
292 /// The result instruction from the expression must be ignored.
293 /// Always an instruction with tag `alloc_inferred`.
294 inferred_ptr: Zir.Inst.Ref,
295 /// The expression has a sequence of pointers to store its results into due to a destructure
296 /// operation. Each of these pointers may or may not have an inferred type.
297 destructure: struct {
298 /// The AST node of the destructure operation itself.
299 src_node: Ast.Node.Index,
300 /// The pointers to store results into.
301 components: []const DestructureComponent,
302 },
303
304 const DestructureComponent = union(enum) {
305 typed_ptr: PtrResultLoc,
306 inferred_ptr: Zir.Inst.Ref,
307 discard,
308 };
309
310 const PtrResultLoc = struct {
311 inst: Zir.Inst.Ref,
312 src_node: ?Ast.Node.Index = null,
313 };
314
315 /// Find the result type for a cast builtin given the result location.
316 /// If the location does not have a known result type, emits an error on
317 /// the given node.
318 fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref {
319 return switch (rl) {
320 .discard, .none, .ref, .inferred_ptr, .destructure => null,
321 .ty, .coerced_ty => |ty_ref| ty_ref,
322 .ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node),
323 .ptr => |ptr| {
324 const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node);
325 return try gz.addUnNode(.elem_type, ptr_ty, node);
326 },
327 };
328 }
329
330 fn resultTypeForCast(rl: Loc, gz: *GenZir, node: Ast.Node.Index, builtin_name: []const u8) !Zir.Inst.Ref {
331 const astgen = gz.astgen;
332 if (try rl.resultType(gz, node)) |ty| return ty;
333 switch (rl) {
334 .destructure => |destructure| return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
335 try astgen.errNoteNode(destructure.src_node, "destructure expressions do not provide a single result type", .{}),
336 try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
337 }),
338 else => return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
339 try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
340 }),
341 }
342 }
343 };
344
345 const Context = enum {
346 /// The expression is the operand to a return expression.
347 @"return",
348 /// The expression is the input to an error-handling operator (if-else, try, or catch).
349 error_handling_expr,
350 /// The expression is the right-hand side of a shift operation.
351 shift_op,
352 /// The expression is an argument in a function call.
353 fn_arg,
354 /// The expression is the right-hand side of an initializer for a `const` variable
355 const_init,
356 /// The expression is the right-hand side of an assignment expression.
357 assignment,
358 /// No specific operator in particular.
359 none,
360 };
361};
362
363const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } };
364const coerced_addrspace_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .address_space_type } };
365const coerced_linksection_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .slice_const_u8_type } };
366const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } };
367const coerced_bool_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .bool_type } };
368
369fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
370 return comptimeExpr(gz, scope, coerced_type_ri, type_node);
371}
372
373fn reachableTypeExpr(
374 gz: *GenZir,
375 scope: *Scope,
376 type_node: Ast.Node.Index,
377 reachable_node: Ast.Node.Index,
378) InnerError!Zir.Inst.Ref {
379 return reachableExprComptime(gz, scope, coerced_type_ri, type_node, reachable_node, true);
380}
381
382/// Same as `expr` but fails with a compile error if the result type is `noreturn`.
383fn reachableExpr(
384 gz: *GenZir,
385 scope: *Scope,
386 ri: ResultInfo,
387 node: Ast.Node.Index,
388 reachable_node: Ast.Node.Index,
389) InnerError!Zir.Inst.Ref {
390 return reachableExprComptime(gz, scope, ri, node, reachable_node, false);
391}
392
393fn reachableExprComptime(
394 gz: *GenZir,
395 scope: *Scope,
396 ri: ResultInfo,
397 node: Ast.Node.Index,
398 reachable_node: Ast.Node.Index,
399 force_comptime: bool,
400) InnerError!Zir.Inst.Ref {
401 const result_inst = if (force_comptime)
402 try comptimeExpr(gz, scope, ri, node)
403 else
404 try expr(gz, scope, ri, node);
405
406 if (gz.refIsNoReturn(result_inst)) {
407 try gz.astgen.appendErrorNodeNotes(reachable_node, "unreachable code", .{}, &[_]u32{
408 try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}),
409 });
410 }
411 return result_inst;
412}
413
414fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
415 const astgen = gz.astgen;
416 const tree = astgen.tree;
417 const node_tags = tree.nodes.items(.tag);
418 const main_tokens = tree.nodes.items(.main_token);
419 switch (node_tags[node]) {
420 .root => unreachable,
421 .@"usingnamespace" => unreachable,
422 .test_decl => unreachable,
423 .global_var_decl => unreachable,
424 .local_var_decl => unreachable,
425 .simple_var_decl => unreachable,
426 .aligned_var_decl => unreachable,
427 .switch_case => unreachable,
428 .switch_case_inline => unreachable,
429 .switch_case_one => unreachable,
430 .switch_case_inline_one => unreachable,
431 .container_field_init => unreachable,
432 .container_field_align => unreachable,
433 .container_field => unreachable,
434 .asm_output => unreachable,
435 .asm_input => unreachable,
436
437 .assign,
438 .assign_destructure,
439 .assign_bit_and,
440 .assign_bit_or,
441 .assign_shl,
442 .assign_shl_sat,
443 .assign_shr,
444 .assign_bit_xor,
445 .assign_div,
446 .assign_sub,
447 .assign_sub_wrap,
448 .assign_sub_sat,
449 .assign_mod,
450 .assign_add,
451 .assign_add_wrap,
452 .assign_add_sat,
453 .assign_mul,
454 .assign_mul_wrap,
455 .assign_mul_sat,
456 .add,
457 .add_wrap,
458 .add_sat,
459 .sub,
460 .sub_wrap,
461 .sub_sat,
462 .mul,
463 .mul_wrap,
464 .mul_sat,
465 .div,
466 .mod,
467 .bit_and,
468 .bit_or,
469 .shl,
470 .shl_sat,
471 .shr,
472 .bit_xor,
473 .bang_equal,
474 .equal_equal,
475 .greater_than,
476 .greater_or_equal,
477 .less_than,
478 .less_or_equal,
479 .array_cat,
480 .array_mult,
481 .bool_and,
482 .bool_or,
483 .@"asm",
484 .asm_simple,
485 .string_literal,
486 .number_literal,
487 .call,
488 .call_comma,
489 .async_call,
490 .async_call_comma,
491 .call_one,
492 .call_one_comma,
493 .async_call_one,
494 .async_call_one_comma,
495 .unreachable_literal,
496 .@"return",
497 .@"if",
498 .if_simple,
499 .@"while",
500 .while_simple,
501 .while_cont,
502 .bool_not,
503 .address_of,
504 .optional_type,
505 .block,
506 .block_semicolon,
507 .block_two,
508 .block_two_semicolon,
509 .@"break",
510 .ptr_type_aligned,
511 .ptr_type_sentinel,
512 .ptr_type,
513 .ptr_type_bit_range,
514 .array_type,
515 .array_type_sentinel,
516 .enum_literal,
517 .multiline_string_literal,
518 .char_literal,
519 .@"defer",
520 .@"errdefer",
521 .@"catch",
522 .error_union,
523 .merge_error_sets,
524 .switch_range,
525 .for_range,
526 .@"await",
527 .bit_not,
528 .negation,
529 .negation_wrap,
530 .@"resume",
531 .@"try",
532 .slice,
533 .slice_open,
534 .slice_sentinel,
535 .array_init_one,
536 .array_init_one_comma,
537 .array_init_dot_two,
538 .array_init_dot_two_comma,
539 .array_init_dot,
540 .array_init_dot_comma,
541 .array_init,
542 .array_init_comma,
543 .struct_init_one,
544 .struct_init_one_comma,
545 .struct_init_dot_two,
546 .struct_init_dot_two_comma,
547 .struct_init_dot,
548 .struct_init_dot_comma,
549 .struct_init,
550 .struct_init_comma,
551 .@"switch",
552 .switch_comma,
553 .@"for",
554 .for_simple,
555 .@"suspend",
556 .@"continue",
557 .fn_proto_simple,
558 .fn_proto_multi,
559 .fn_proto_one,
560 .fn_proto,
561 .fn_decl,
562 .anyframe_type,
563 .anyframe_literal,
564 .error_set_decl,
565 .container_decl,
566 .container_decl_trailing,
567 .container_decl_two,
568 .container_decl_two_trailing,
569 .container_decl_arg,
570 .container_decl_arg_trailing,
571 .tagged_union,
572 .tagged_union_trailing,
573 .tagged_union_two,
574 .tagged_union_two_trailing,
575 .tagged_union_enum_tag,
576 .tagged_union_enum_tag_trailing,
577 .@"comptime",
578 .@"nosuspend",
579 .error_value,
580 => return astgen.failNode(node, "invalid left-hand side to assignment", .{}),
581
582 .builtin_call,
583 .builtin_call_comma,
584 .builtin_call_two,
585 .builtin_call_two_comma,
586 => {
587 const builtin_token = main_tokens[node];
588 const builtin_name = tree.tokenSlice(builtin_token);
589 // If the builtin is an invalid name, we don't cause an error here; instead
590 // let it pass, and the error will be "invalid builtin function" later.
591 if (BuiltinFn.list.get(builtin_name)) |info| {
592 if (!info.allows_lvalue) {
593 return astgen.failNode(node, "invalid left-hand side to assignment", .{});
594 }
595 }
596 },
597
598 // These can be assigned to.
599 .unwrap_optional,
600 .deref,
601 .field_access,
602 .array_access,
603 .identifier,
604 .grouped_expression,
605 .@"orelse",
606 => {},
607 }
608 return expr(gz, scope, .{ .rl = .ref }, node);
609}
610
611/// Turn Zig AST into untyped ZIR instructions.
612/// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the
613/// result instruction can be used to inspect whether it is isNoReturn() but that is it,
614/// it must otherwise not be used.
615fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
616 const astgen = gz.astgen;
617 const tree = astgen.tree;
618 const main_tokens = tree.nodes.items(.main_token);
619 const token_tags = tree.tokens.items(.tag);
620 const node_datas = tree.nodes.items(.data);
621 const node_tags = tree.nodes.items(.tag);
622
623 const prev_anon_name_strategy = gz.anon_name_strategy;
624 defer gz.anon_name_strategy = prev_anon_name_strategy;
625 if (!nodeUsesAnonNameStrategy(tree, node)) {
626 gz.anon_name_strategy = .anon;
627 }
628
629 switch (node_tags[node]) {
630 .root => unreachable, // Top-level declaration.
631 .@"usingnamespace" => unreachable, // Top-level declaration.
632 .test_decl => unreachable, // Top-level declaration.
633 .container_field_init => unreachable, // Top-level declaration.
634 .container_field_align => unreachable, // Top-level declaration.
635 .container_field => unreachable, // Top-level declaration.
636 .fn_decl => unreachable, // Top-level declaration.
637
638 .global_var_decl => unreachable, // Handled in `blockExpr`.
639 .local_var_decl => unreachable, // Handled in `blockExpr`.
640 .simple_var_decl => unreachable, // Handled in `blockExpr`.
641 .aligned_var_decl => unreachable, // Handled in `blockExpr`.
642 .@"defer" => unreachable, // Handled in `blockExpr`.
643 .@"errdefer" => unreachable, // Handled in `blockExpr`.
644
645 .switch_case => unreachable, // Handled in `switchExpr`.
646 .switch_case_inline => unreachable, // Handled in `switchExpr`.
647 .switch_case_one => unreachable, // Handled in `switchExpr`.
648 .switch_case_inline_one => unreachable, // Handled in `switchExpr`.
649 .switch_range => unreachable, // Handled in `switchExpr`.
650
651 .asm_output => unreachable, // Handled in `asmExpr`.
652 .asm_input => unreachable, // Handled in `asmExpr`.
653
654 .for_range => unreachable, // Handled in `forExpr`.
655
656 .assign => {
657 try assign(gz, scope, node);
658 return rvalue(gz, ri, .void_value, node);
659 },
660
661 .assign_destructure => {
662 // Note that this variant does not declare any new var/const: that
663 // variant is handled by `blockExprStmts`.
664 try assignDestructure(gz, scope, node);
665 return rvalue(gz, ri, .void_value, node);
666 },
667
668 .assign_shl => {
669 try assignShift(gz, scope, node, .shl);
670 return rvalue(gz, ri, .void_value, node);
671 },
672 .assign_shl_sat => {
673 try assignShiftSat(gz, scope, node);
674 return rvalue(gz, ri, .void_value, node);
675 },
676 .assign_shr => {
677 try assignShift(gz, scope, node, .shr);
678 return rvalue(gz, ri, .void_value, node);
679 },
680
681 .assign_bit_and => {
682 try assignOp(gz, scope, node, .bit_and);
683 return rvalue(gz, ri, .void_value, node);
684 },
685 .assign_bit_or => {
686 try assignOp(gz, scope, node, .bit_or);
687 return rvalue(gz, ri, .void_value, node);
688 },
689 .assign_bit_xor => {
690 try assignOp(gz, scope, node, .xor);
691 return rvalue(gz, ri, .void_value, node);
692 },
693 .assign_div => {
694 try assignOp(gz, scope, node, .div);
695 return rvalue(gz, ri, .void_value, node);
696 },
697 .assign_sub => {
698 try assignOp(gz, scope, node, .sub);
699 return rvalue(gz, ri, .void_value, node);
700 },
701 .assign_sub_wrap => {
702 try assignOp(gz, scope, node, .subwrap);
703 return rvalue(gz, ri, .void_value, node);
704 },
705 .assign_sub_sat => {
706 try assignOp(gz, scope, node, .sub_sat);
707 return rvalue(gz, ri, .void_value, node);
708 },
709 .assign_mod => {
710 try assignOp(gz, scope, node, .mod_rem);
711 return rvalue(gz, ri, .void_value, node);
712 },
713 .assign_add => {
714 try assignOp(gz, scope, node, .add);
715 return rvalue(gz, ri, .void_value, node);
716 },
717 .assign_add_wrap => {
718 try assignOp(gz, scope, node, .addwrap);
719 return rvalue(gz, ri, .void_value, node);
720 },
721 .assign_add_sat => {
722 try assignOp(gz, scope, node, .add_sat);
723 return rvalue(gz, ri, .void_value, node);
724 },
725 .assign_mul => {
726 try assignOp(gz, scope, node, .mul);
727 return rvalue(gz, ri, .void_value, node);
728 },
729 .assign_mul_wrap => {
730 try assignOp(gz, scope, node, .mulwrap);
731 return rvalue(gz, ri, .void_value, node);
732 },
733 .assign_mul_sat => {
734 try assignOp(gz, scope, node, .mul_sat);
735 return rvalue(gz, ri, .void_value, node);
736 },
737
738 // zig fmt: off
739 .shl => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
740 .shr => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
741
742 .add => return simpleBinOp(gz, scope, ri, node, .add),
743 .add_wrap => return simpleBinOp(gz, scope, ri, node, .addwrap),
744 .add_sat => return simpleBinOp(gz, scope, ri, node, .add_sat),
745 .sub => return simpleBinOp(gz, scope, ri, node, .sub),
746 .sub_wrap => return simpleBinOp(gz, scope, ri, node, .subwrap),
747 .sub_sat => return simpleBinOp(gz, scope, ri, node, .sub_sat),
748 .mul => return simpleBinOp(gz, scope, ri, node, .mul),
749 .mul_wrap => return simpleBinOp(gz, scope, ri, node, .mulwrap),
750 .mul_sat => return simpleBinOp(gz, scope, ri, node, .mul_sat),
751 .div => return simpleBinOp(gz, scope, ri, node, .div),
752 .mod => return simpleBinOp(gz, scope, ri, node, .mod_rem),
753 .shl_sat => return simpleBinOp(gz, scope, ri, node, .shl_sat),
754
755 .bit_and => return simpleBinOp(gz, scope, ri, node, .bit_and),
756 .bit_or => return simpleBinOp(gz, scope, ri, node, .bit_or),
757 .bit_xor => return simpleBinOp(gz, scope, ri, node, .xor),
758 .bang_equal => return simpleBinOp(gz, scope, ri, node, .cmp_neq),
759 .equal_equal => return simpleBinOp(gz, scope, ri, node, .cmp_eq),
760 .greater_than => return simpleBinOp(gz, scope, ri, node, .cmp_gt),
761 .greater_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_gte),
762 .less_than => return simpleBinOp(gz, scope, ri, node, .cmp_lt),
763 .less_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_lte),
764 .array_cat => return simpleBinOp(gz, scope, ri, node, .array_cat),
765
766 .array_mult => {
767 // This syntax form does not currently use the result type in the language specification.
768 // However, the result type can be used to emit more optimal code for large multiplications by
769 // having Sema perform a coercion before the multiplication operation.
770 const result = try gz.addPlNode(.array_mul, node, Zir.Inst.ArrayMul{
771 .res_ty = if (try ri.rl.resultType(gz, node)) |t| t else .none,
772 .lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs),
773 .rhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs),
774 });
775 return rvalue(gz, ri, result, node);
776 },
777
778 .error_union => return simpleBinOp(gz, scope, ri, node, .error_union_type),
779 .merge_error_sets => return simpleBinOp(gz, scope, ri, node, .merge_error_sets),
780
781 .bool_and => return boolBinOp(gz, scope, ri, node, .bool_br_and),
782 .bool_or => return boolBinOp(gz, scope, ri, node, .bool_br_or),
783
784 .bool_not => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, node_datas[node].lhs, .bool_not),
785 .bit_not => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .bit_not),
786
787 .negation => return negation(gz, scope, ri, node),
788 .negation_wrap => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .negate_wrap),
789
790 .identifier => return identifier(gz, scope, ri, node),
791
792 .asm_simple,
793 .@"asm",
794 => return asmExpr(gz, scope, ri, node, tree.fullAsm(node).?),
795
796 .string_literal => return stringLiteral(gz, ri, node),
797 .multiline_string_literal => return multilineStringLiteral(gz, ri, node),
798
799 .number_literal => return numberLiteral(gz, ri, node, node, .positive),
800 // zig fmt: on
801
802 .builtin_call_two, .builtin_call_two_comma => {
803 if (node_datas[node].lhs == 0) {
804 const params = [_]Ast.Node.Index{};
805 return builtinCall(gz, scope, ri, node, &params);
806 } else if (node_datas[node].rhs == 0) {
807 const params = [_]Ast.Node.Index{node_datas[node].lhs};
808 return builtinCall(gz, scope, ri, node, &params);
809 } else {
810 const params = [_]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
811 return builtinCall(gz, scope, ri, node, &params);
812 }
813 },
814 .builtin_call, .builtin_call_comma => {
815 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
816 return builtinCall(gz, scope, ri, node, params);
817 },
818
819 .call_one,
820 .call_one_comma,
821 .async_call_one,
822 .async_call_one_comma,
823 .call,
824 .call_comma,
825 .async_call,
826 .async_call_comma,
827 => {
828 var buf: [1]Ast.Node.Index = undefined;
829 return callExpr(gz, scope, ri, node, tree.fullCall(&buf, node).?);
830 },
831
832 .unreachable_literal => {
833 try emitDbgNode(gz, node);
834 _ = try gz.addAsIndex(.{
835 .tag = .@"unreachable",
836 .data = .{ .@"unreachable" = .{
837 .src_node = gz.nodeIndexToRelative(node),
838 } },
839 });
840 return Zir.Inst.Ref.unreachable_value;
841 },
842 .@"return" => return ret(gz, scope, node),
843 .field_access => return fieldAccess(gz, scope, ri, node),
844
845 .if_simple,
846 .@"if",
847 => {
848 const if_full = tree.fullIf(node).?;
849 no_switch_on_err: {
850 const error_token = if_full.error_token orelse break :no_switch_on_err;
851 switch (node_tags[if_full.ast.else_expr]) {
852 .@"switch", .switch_comma => {},
853 else => break :no_switch_on_err,
854 }
855 const switch_operand = node_datas[if_full.ast.else_expr].lhs;
856 if (node_tags[switch_operand] != .identifier) break :no_switch_on_err;
857 if (!mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(main_tokens[switch_operand]))) break :no_switch_on_err;
858 return switchExprErrUnion(gz, scope, ri.br(), node, .@"if");
859 }
860 return ifExpr(gz, scope, ri.br(), node, if_full);
861 },
862
863 .while_simple,
864 .while_cont,
865 .@"while",
866 => return whileExpr(gz, scope, ri.br(), node, tree.fullWhile(node).?, false),
867
868 .for_simple, .@"for" => return forExpr(gz, scope, ri.br(), node, tree.fullFor(node).?, false),
869
870 .slice_open => {
871 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
872
873 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
874 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
875 try emitDbgStmt(gz, cursor);
876 const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{
877 .lhs = lhs,
878 .start = start,
879 });
880 return rvalue(gz, ri, result, node);
881 },
882 .slice => {
883 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
884 const lhs_node = node_datas[node].lhs;
885 const lhs_tag = node_tags[lhs_node];
886 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
887 const lhs_is_open_slice = lhs_tag == .slice_open or
888 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
889 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
890 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
891
892 const start = if (lhs_is_slice_sentinel) start: {
893 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
894 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
895 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
896
897 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
898 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
899 try emitDbgStmt(gz, cursor);
900 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
901 .lhs = lhs,
902 .start = start,
903 .len = len,
904 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
905 .sentinel = .none,
906 });
907 return rvalue(gz, ri, result, node);
908 }
909 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
910
911 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
912 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
913 const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end);
914 try emitDbgStmt(gz, cursor);
915 const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{
916 .lhs = lhs,
917 .start = start,
918 .end = end,
919 });
920 return rvalue(gz, ri, result, node);
921 },
922 .slice_sentinel => {
923 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
924 const lhs_node = node_datas[node].lhs;
925 const lhs_tag = node_tags[lhs_node];
926 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
927 const lhs_is_open_slice = lhs_tag == .slice_open or
928 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
929 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
930 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
931
932 const start = if (lhs_is_slice_sentinel) start: {
933 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
934 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
935 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
936
937 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
938 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
939 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
940 try emitDbgStmt(gz, cursor);
941 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
942 .lhs = lhs,
943 .start = start,
944 .len = len,
945 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
946 .sentinel = sentinel,
947 });
948 return rvalue(gz, ri, result, node);
949 }
950 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
951
952 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
953 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
954 const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
955 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
956 try emitDbgStmt(gz, cursor);
957 const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
958 .lhs = lhs,
959 .start = start,
960 .end = end,
961 .sentinel = sentinel,
962 });
963 return rvalue(gz, ri, result, node);
964 },
965
966 .deref => {
967 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
968 _ = try gz.addUnNode(.validate_deref, lhs, node);
969 switch (ri.rl) {
970 .ref, .ref_coerced_ty => return lhs,
971 else => {
972 const result = try gz.addUnNode(.load, lhs, node);
973 return rvalue(gz, ri, result, node);
974 },
975 }
976 },
977 .address_of => {
978 const operand_rl: ResultInfo.Loc = if (try ri.rl.resultType(gz, node)) |res_ty_inst| rl: {
979 _ = try gz.addUnTok(.validate_ref_ty, res_ty_inst, tree.firstToken(node));
980 break :rl .{ .ref_coerced_ty = res_ty_inst };
981 } else .ref;
982 const result = try expr(gz, scope, .{ .rl = operand_rl }, node_datas[node].lhs);
983 return rvalue(gz, ri, result, node);
984 },
985 .optional_type => {
986 const operand = try typeExpr(gz, scope, node_datas[node].lhs);
987 const result = try gz.addUnNode(.optional_type, operand, node);
988 return rvalue(gz, ri, result, node);
989 },
990 .unwrap_optional => switch (ri.rl) {
991 .ref, .ref_coerced_ty => {
992 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
993
994 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
995 try emitDbgStmt(gz, cursor);
996
997 return gz.addUnNode(.optional_payload_safe_ptr, lhs, node);
998 },
999 else => {
1000 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
1001
1002 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
1003 try emitDbgStmt(gz, cursor);
1004
1005 return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node);
1006 },
1007 },
1008 .block_two, .block_two_semicolon => {
1009 const statements = [2]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
1010 if (node_datas[node].lhs == 0) {
1011 return blockExpr(gz, scope, ri, node, statements[0..0]);
1012 } else if (node_datas[node].rhs == 0) {
1013 return blockExpr(gz, scope, ri, node, statements[0..1]);
1014 } else {
1015 return blockExpr(gz, scope, ri, node, statements[0..2]);
1016 }
1017 },
1018 .block, .block_semicolon => {
1019 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
1020 return blockExpr(gz, scope, ri, node, statements);
1021 },
1022 .enum_literal => return simpleStrTok(gz, ri, main_tokens[node], node, .enum_literal),
1023 .error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value),
1024 // TODO restore this when implementing https://github.com/ziglang/zig/issues/6025
1025 // .anyframe_literal => return rvalue(gz, ri, .anyframe_type, node),
1026 .anyframe_literal => {
1027 const result = try gz.addUnNode(.anyframe_type, .void_type, node);
1028 return rvalue(gz, ri, result, node);
1029 },
1030 .anyframe_type => {
1031 const return_type = try typeExpr(gz, scope, node_datas[node].rhs);
1032 const result = try gz.addUnNode(.anyframe_type, return_type, node);
1033 return rvalue(gz, ri, result, node);
1034 },
1035 .@"catch" => {
1036 const catch_token = main_tokens[node];
1037 const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)
1038 catch_token + 2
1039 else
1040 null;
1041 no_switch_on_err: {
1042 const capture_token = payload_token orelse break :no_switch_on_err;
1043 switch (node_tags[node_datas[node].rhs]) {
1044 .@"switch", .switch_comma => {},
1045 else => break :no_switch_on_err,
1046 }
1047 const switch_operand = node_datas[node_datas[node].rhs].lhs;
1048 if (node_tags[switch_operand] != .identifier) break :no_switch_on_err;
1049 if (!mem.eql(u8, tree.tokenSlice(capture_token), tree.tokenSlice(main_tokens[switch_operand]))) break :no_switch_on_err;
1050 return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch");
1051 }
1052 switch (ri.rl) {
1053 .ref, .ref_coerced_ty => return orelseCatchExpr(
1054 gz,
1055 scope,
1056 ri,
1057 node,
1058 node_datas[node].lhs,
1059 .is_non_err_ptr,
1060 .err_union_payload_unsafe_ptr,
1061 .err_union_code_ptr,
1062 node_datas[node].rhs,
1063 payload_token,
1064 ),
1065 else => return orelseCatchExpr(
1066 gz,
1067 scope,
1068 ri,
1069 node,
1070 node_datas[node].lhs,
1071 .is_non_err,
1072 .err_union_payload_unsafe,
1073 .err_union_code,
1074 node_datas[node].rhs,
1075 payload_token,
1076 ),
1077 }
1078 },
1079 .@"orelse" => switch (ri.rl) {
1080 .ref, .ref_coerced_ty => return orelseCatchExpr(
1081 gz,
1082 scope,
1083 ri,
1084 node,
1085 node_datas[node].lhs,
1086 .is_non_null_ptr,
1087 .optional_payload_unsafe_ptr,
1088 undefined,
1089 node_datas[node].rhs,
1090 null,
1091 ),
1092 else => return orelseCatchExpr(
1093 gz,
1094 scope,
1095 ri,
1096 node,
1097 node_datas[node].lhs,
1098 .is_non_null,
1099 .optional_payload_unsafe,
1100 undefined,
1101 node_datas[node].rhs,
1102 null,
1103 ),
1104 },
1105
1106 .ptr_type_aligned,
1107 .ptr_type_sentinel,
1108 .ptr_type,
1109 .ptr_type_bit_range,
1110 => return ptrType(gz, scope, ri, node, tree.fullPtrType(node).?),
1111
1112 .container_decl,
1113 .container_decl_trailing,
1114 .container_decl_arg,
1115 .container_decl_arg_trailing,
1116 .container_decl_two,
1117 .container_decl_two_trailing,
1118 .tagged_union,
1119 .tagged_union_trailing,
1120 .tagged_union_enum_tag,
1121 .tagged_union_enum_tag_trailing,
1122 .tagged_union_two,
1123 .tagged_union_two_trailing,
1124 => {
1125 var buf: [2]Ast.Node.Index = undefined;
1126 return containerDecl(gz, scope, ri, node, tree.fullContainerDecl(&buf, node).?);
1127 },
1128
1129 .@"break" => return breakExpr(gz, scope, node),
1130 .@"continue" => return continueExpr(gz, scope, node),
1131 .grouped_expression => return expr(gz, scope, ri, node_datas[node].lhs),
1132 .array_type => return arrayType(gz, scope, ri, node),
1133 .array_type_sentinel => return arrayTypeSentinel(gz, scope, ri, node),
1134 .char_literal => return charLiteral(gz, ri, node),
1135 .error_set_decl => return errorSetDecl(gz, ri, node),
1136 .array_access => return arrayAccess(gz, scope, ri, node),
1137 .@"comptime" => return comptimeExprAst(gz, scope, ri, node),
1138 .@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node),
1139
1140 .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node),
1141 .@"suspend" => return suspendExpr(gz, scope, node),
1142 .@"await" => return awaitExpr(gz, scope, ri, node),
1143 .@"resume" => return resumeExpr(gz, scope, ri, node),
1144
1145 .@"try" => return tryExpr(gz, scope, ri, node, node_datas[node].lhs),
1146
1147 .array_init_one,
1148 .array_init_one_comma,
1149 .array_init_dot_two,
1150 .array_init_dot_two_comma,
1151 .array_init_dot,
1152 .array_init_dot_comma,
1153 .array_init,
1154 .array_init_comma,
1155 => {
1156 var buf: [2]Ast.Node.Index = undefined;
1157 return arrayInitExpr(gz, scope, ri, node, tree.fullArrayInit(&buf, node).?);
1158 },
1159
1160 .struct_init_one,
1161 .struct_init_one_comma,
1162 .struct_init_dot_two,
1163 .struct_init_dot_two_comma,
1164 .struct_init_dot,
1165 .struct_init_dot_comma,
1166 .struct_init,
1167 .struct_init_comma,
1168 => {
1169 var buf: [2]Ast.Node.Index = undefined;
1170 return structInitExpr(gz, scope, ri, node, tree.fullStructInit(&buf, node).?);
1171 },
1172
1173 .fn_proto_simple,
1174 .fn_proto_multi,
1175 .fn_proto_one,
1176 .fn_proto,
1177 => {
1178 var buf: [1]Ast.Node.Index = undefined;
1179 return fnProtoExpr(gz, scope, ri, node, tree.fullFnProto(&buf, node).?);
1180 },
1181 }
1182}
1183
1184fn nosuspendExpr(
1185 gz: *GenZir,
1186 scope: *Scope,
1187 ri: ResultInfo,
1188 node: Ast.Node.Index,
1189) InnerError!Zir.Inst.Ref {
1190 const astgen = gz.astgen;
1191 const tree = astgen.tree;
1192 const node_datas = tree.nodes.items(.data);
1193 const body_node = node_datas[node].lhs;
1194 assert(body_node != 0);
1195 if (gz.nosuspend_node != 0) {
1196 try astgen.appendErrorNodeNotes(node, "redundant nosuspend block", .{}, &[_]u32{
1197 try astgen.errNoteNode(gz.nosuspend_node, "other nosuspend block here", .{}),
1198 });
1199 }
1200 gz.nosuspend_node = node;
1201 defer gz.nosuspend_node = 0;
1202 return expr(gz, scope, ri, body_node);
1203}
1204
1205fn suspendExpr(
1206 gz: *GenZir,
1207 scope: *Scope,
1208 node: Ast.Node.Index,
1209) InnerError!Zir.Inst.Ref {
1210 const astgen = gz.astgen;
1211 const gpa = astgen.gpa;
1212 const tree = astgen.tree;
1213 const node_datas = tree.nodes.items(.data);
1214 const body_node = node_datas[node].lhs;
1215
1216 if (gz.nosuspend_node != 0) {
1217 return astgen.failNodeNotes(node, "suspend inside nosuspend block", .{}, &[_]u32{
1218 try astgen.errNoteNode(gz.nosuspend_node, "nosuspend block here", .{}),
1219 });
1220 }
1221 if (gz.suspend_node != 0) {
1222 return astgen.failNodeNotes(node, "cannot suspend inside suspend block", .{}, &[_]u32{
1223 try astgen.errNoteNode(gz.suspend_node, "other suspend block here", .{}),
1224 });
1225 }
1226 assert(body_node != 0);
1227
1228 const suspend_inst = try gz.makeBlockInst(.suspend_block, node);
1229 try gz.instructions.append(gpa, suspend_inst);
1230
1231 var suspend_scope = gz.makeSubBlock(scope);
1232 suspend_scope.suspend_node = node;
1233 defer suspend_scope.unstack();
1234
1235 const body_result = try expr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node);
1236 if (!gz.refIsNoReturn(body_result)) {
1237 _ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value);
1238 }
1239 try suspend_scope.setBlockBody(suspend_inst);
1240
1241 return suspend_inst.toRef();
1242}
1243
1244fn awaitExpr(
1245 gz: *GenZir,
1246 scope: *Scope,
1247 ri: ResultInfo,
1248 node: Ast.Node.Index,
1249) InnerError!Zir.Inst.Ref {
1250 const astgen = gz.astgen;
1251 const tree = astgen.tree;
1252 const node_datas = tree.nodes.items(.data);
1253 const rhs_node = node_datas[node].lhs;
1254
1255 if (gz.suspend_node != 0) {
1256 return astgen.failNodeNotes(node, "cannot await inside suspend block", .{}, &[_]u32{
1257 try astgen.errNoteNode(gz.suspend_node, "suspend block here", .{}),
1258 });
1259 }
1260 const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
1261 const result = if (gz.nosuspend_node != 0)
1262 try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{
1263 .node = gz.nodeIndexToRelative(node),
1264 .operand = operand,
1265 })
1266 else
1267 try gz.addUnNode(.@"await", operand, node);
1268
1269 return rvalue(gz, ri, result, node);
1270}
1271
1272fn resumeExpr(
1273 gz: *GenZir,
1274 scope: *Scope,
1275 ri: ResultInfo,
1276 node: Ast.Node.Index,
1277) InnerError!Zir.Inst.Ref {
1278 const astgen = gz.astgen;
1279 const tree = astgen.tree;
1280 const node_datas = tree.nodes.items(.data);
1281 const rhs_node = node_datas[node].lhs;
1282 const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
1283 const result = try gz.addUnNode(.@"resume", operand, node);
1284 return rvalue(gz, ri, result, node);
1285}
1286
1287fn fnProtoExpr(
1288 gz: *GenZir,
1289 scope: *Scope,
1290 ri: ResultInfo,
1291 node: Ast.Node.Index,
1292 fn_proto: Ast.full.FnProto,
1293) InnerError!Zir.Inst.Ref {
1294 const astgen = gz.astgen;
1295 const tree = astgen.tree;
1296 const token_tags = tree.tokens.items(.tag);
1297
1298 if (fn_proto.name_token) |some| {
1299 return astgen.failTok(some, "function type cannot have a name", .{});
1300 }
1301
1302 const is_extern = blk: {
1303 const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
1304 break :blk token_tags[maybe_extern_token] == .keyword_extern;
1305 };
1306 assert(!is_extern);
1307
1308 var block_scope = gz.makeSubBlock(scope);
1309 defer block_scope.unstack();
1310
1311 const block_inst = try gz.makeBlockInst(.block_inline, node);
1312
1313 var noalias_bits: u32 = 0;
1314 const is_var_args = is_var_args: {
1315 var param_type_i: usize = 0;
1316 var it = fn_proto.iterate(tree);
1317 while (it.next()) |param| : (param_type_i += 1) {
1318 const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
1319 .keyword_noalias => is_comptime: {
1320 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
1321 return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
1322 break :is_comptime false;
1323 },
1324 .keyword_comptime => true,
1325 else => false,
1326 } else false;
1327
1328 const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
1329 switch (token_tags[token]) {
1330 .keyword_anytype => break :blk true,
1331 .ellipsis3 => break :is_var_args true,
1332 else => unreachable,
1333 }
1334 } else false;
1335
1336 const param_name = if (param.name_token) |name_token| blk: {
1337 if (mem.eql(u8, "_", tree.tokenSlice(name_token)))
1338 break :blk .empty;
1339
1340 break :blk try astgen.identAsString(name_token);
1341 } else .empty;
1342
1343 if (is_anytype) {
1344 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
1345
1346 const tag: Zir.Inst.Tag = if (is_comptime)
1347 .param_anytype_comptime
1348 else
1349 .param_anytype;
1350 _ = try block_scope.addStrTok(tag, param_name, name_token);
1351 } else {
1352 const param_type_node = param.type_expr;
1353 assert(param_type_node != 0);
1354 var param_gz = block_scope.makeSubBlock(scope);
1355 defer param_gz.unstack();
1356 const param_type = try expr(&param_gz, scope, coerced_type_ri, param_type_node);
1357 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
1358 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
1359 const main_tokens = tree.nodes.items(.main_token);
1360 const name_token = param.name_token orelse main_tokens[param_type_node];
1361 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
1362 const param_inst = try block_scope.addParam(&param_gz, tag, name_token, param_name, param.first_doc_comment);
1363 assert(param_inst_expected == param_inst);
1364 }
1365 }
1366 break :is_var_args false;
1367 };
1368
1369 const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
1370 break :inst try expr(&block_scope, scope, coerced_align_ri, fn_proto.ast.align_expr);
1371 };
1372
1373 if (fn_proto.ast.addrspace_expr != 0) {
1374 return astgen.failNode(fn_proto.ast.addrspace_expr, "addrspace not allowed on function prototypes", .{});
1375 }
1376
1377 if (fn_proto.ast.section_expr != 0) {
1378 return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{});
1379 }
1380
1381 const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0)
1382 try expr(
1383 &block_scope,
1384 scope,
1385 .{ .rl = .{ .coerced_ty = .calling_convention_type } },
1386 fn_proto.ast.callconv_expr,
1387 )
1388 else
1389 Zir.Inst.Ref.none;
1390
1391 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
1392 const is_inferred_error = token_tags[maybe_bang] == .bang;
1393 if (is_inferred_error) {
1394 return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
1395 }
1396 const ret_ty = try expr(&block_scope, scope, coerced_type_ri, fn_proto.ast.return_type);
1397
1398 const result = try block_scope.addFunc(.{
1399 .src_node = fn_proto.ast.proto_node,
1400
1401 .cc_ref = cc,
1402 .cc_gz = null,
1403 .align_ref = align_ref,
1404 .align_gz = null,
1405 .ret_ref = ret_ty,
1406 .ret_gz = null,
1407 .section_ref = .none,
1408 .section_gz = null,
1409 .addrspace_ref = .none,
1410 .addrspace_gz = null,
1411
1412 .param_block = block_inst,
1413 .body_gz = null,
1414 .lib_name = .empty,
1415 .is_var_args = is_var_args,
1416 .is_inferred_error = false,
1417 .is_test = false,
1418 .is_extern = false,
1419 .is_noinline = false,
1420 .noalias_bits = noalias_bits,
1421 });
1422
1423 _ = try block_scope.addBreak(.break_inline, block_inst, result);
1424 try block_scope.setBlockBody(block_inst);
1425 try gz.instructions.append(astgen.gpa, block_inst);
1426
1427 return rvalue(gz, ri, block_inst.toRef(), fn_proto.ast.proto_node);
1428}
1429
1430fn arrayInitExpr(
1431 gz: *GenZir,
1432 scope: *Scope,
1433 ri: ResultInfo,
1434 node: Ast.Node.Index,
1435 array_init: Ast.full.ArrayInit,
1436) InnerError!Zir.Inst.Ref {
1437 const astgen = gz.astgen;
1438 const tree = astgen.tree;
1439 const node_tags = tree.nodes.items(.tag);
1440 const main_tokens = tree.nodes.items(.main_token);
1441
1442 assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init.
1443
1444 const array_ty: Zir.Inst.Ref, const elem_ty: Zir.Inst.Ref = inst: {
1445 if (array_init.ast.type_expr == 0) break :inst .{ .none, .none };
1446
1447 infer: {
1448 const array_type: Ast.full.ArrayType = tree.fullArrayType(array_init.ast.type_expr) orelse break :infer;
1449 // This intentionally does not support `@"_"` syntax.
1450 if (node_tags[array_type.ast.elem_count] == .identifier and
1451 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_"))
1452 {
1453 const len_inst = try gz.addInt(array_init.ast.elements.len);
1454 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
1455 if (array_type.ast.sentinel == 0) {
1456 const array_type_inst = try gz.addPlNode(.array_type, array_init.ast.type_expr, Zir.Inst.Bin{
1457 .lhs = len_inst,
1458 .rhs = elem_type,
1459 });
1460 break :inst .{ array_type_inst, elem_type };
1461 } else {
1462 const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel);
1463 const array_type_inst = try gz.addPlNode(
1464 .array_type_sentinel,
1465 array_init.ast.type_expr,
1466 Zir.Inst.ArrayTypeSentinel{
1467 .len = len_inst,
1468 .elem_type = elem_type,
1469 .sentinel = sentinel,
1470 },
1471 );
1472 break :inst .{ array_type_inst, elem_type };
1473 }
1474 }
1475 }
1476 const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr);
1477 _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{
1478 .ty = array_type_inst,
1479 .init_count = @intCast(array_init.ast.elements.len),
1480 });
1481 break :inst .{ array_type_inst, .none };
1482 };
1483
1484 if (array_ty != .none) {
1485 // Typed inits do not use RLS for language simplicity.
1486 switch (ri.rl) {
1487 .discard => {
1488 if (elem_ty != .none) {
1489 const elem_ri: ResultInfo = .{ .rl = .{ .ty = elem_ty } };
1490 for (array_init.ast.elements) |elem_init| {
1491 _ = try expr(gz, scope, elem_ri, elem_init);
1492 }
1493 } else {
1494 for (array_init.ast.elements, 0..) |elem_init, i| {
1495 const this_elem_ty = try gz.add(.{
1496 .tag = .array_init_elem_type,
1497 .data = .{ .bin = .{
1498 .lhs = array_ty,
1499 .rhs = @enumFromInt(i),
1500 } },
1501 });
1502 _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init);
1503 }
1504 }
1505 return .void_value;
1506 },
1507 .ref => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true),
1508 else => {
1509 const array_inst = try arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, false);
1510 return rvalue(gz, ri, array_inst, node);
1511 },
1512 }
1513 }
1514
1515 switch (ri.rl) {
1516 .none => return arrayInitExprAnon(gz, scope, node, array_init.ast.elements),
1517 .discard => {
1518 for (array_init.ast.elements) |elem_init| {
1519 _ = try expr(gz, scope, .{ .rl = .discard }, elem_init);
1520 }
1521 return Zir.Inst.Ref.void_value;
1522 },
1523 .ref => {
1524 const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);
1525 return gz.addUnTok(.ref, result, tree.firstToken(node));
1526 },
1527 .ref_coerced_ty => |ptr_ty_inst| {
1528 const dest_arr_ty_inst = try gz.addPlNode(.validate_array_init_ref_ty, node, Zir.Inst.ArrayInitRefTy{
1529 .ptr_ty = ptr_ty_inst,
1530 .elem_count = @intCast(array_init.ast.elements.len),
1531 });
1532 return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, dest_arr_ty_inst, .none, true);
1533 },
1534 .ty, .coerced_ty => |result_ty_inst| {
1535 _ = try gz.addPlNode(.validate_array_init_result_ty, node, Zir.Inst.ArrayInit{
1536 .ty = result_ty_inst,
1537 .init_count = @intCast(array_init.ast.elements.len),
1538 });
1539 return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, result_ty_inst, .none, false);
1540 },
1541 .ptr => |ptr| {
1542 try arrayInitExprPtr(gz, scope, node, array_init.ast.elements, ptr.inst);
1543 return .void_value;
1544 },
1545 .inferred_ptr => {
1546 // We can't get elem pointers of an untyped inferred alloc, so must perform a
1547 // standard anonymous initialization followed by an rvalue store.
1548 // See corresponding logic in structInitExpr.
1549 const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);
1550 return rvalue(gz, ri, result, node);
1551 },
1552 .destructure => |destructure| {
1553 // Untyped init - destructure directly into result pointers
1554 if (array_init.ast.elements.len != destructure.components.len) {
1555 return astgen.failNodeNotes(node, "expected {} elements for destructure, found {}", .{
1556 destructure.components.len,
1557 array_init.ast.elements.len,
1558 }, &.{
1559 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
1560 });
1561 }
1562 for (array_init.ast.elements, destructure.components) |elem_init, ds_comp| {
1563 const elem_ri: ResultInfo = .{ .rl = switch (ds_comp) {
1564 .typed_ptr => |ptr_rl| .{ .ptr = ptr_rl },
1565 .inferred_ptr => |ptr_inst| .{ .inferred_ptr = ptr_inst },
1566 .discard => .discard,
1567 } };
1568 _ = try expr(gz, scope, elem_ri, elem_init);
1569 }
1570 return .void_value;
1571 },
1572 }
1573}
1574
1575/// An array initialization expression using an `array_init_anon` instruction.
1576fn arrayInitExprAnon(
1577 gz: *GenZir,
1578 scope: *Scope,
1579 node: Ast.Node.Index,
1580 elements: []const Ast.Node.Index,
1581) InnerError!Zir.Inst.Ref {
1582 const astgen = gz.astgen;
1583
1584 const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
1585 .operands_len = @intCast(elements.len),
1586 });
1587 var extra_index = try reserveExtra(astgen, elements.len);
1588
1589 for (elements) |elem_init| {
1590 const elem_ref = try expr(gz, scope, .{ .rl = .none }, elem_init);
1591 astgen.extra.items[extra_index] = @intFromEnum(elem_ref);
1592 extra_index += 1;
1593 }
1594 return try gz.addPlNodePayloadIndex(.array_init_anon, node, payload_index);
1595}
1596
1597/// An array initialization expression using an `array_init` or `array_init_ref` instruction.
1598fn arrayInitExprTyped(
1599 gz: *GenZir,
1600 scope: *Scope,
1601 node: Ast.Node.Index,
1602 elements: []const Ast.Node.Index,
1603 ty_inst: Zir.Inst.Ref,
1604 maybe_elem_ty_inst: Zir.Inst.Ref,
1605 is_ref: bool,
1606) InnerError!Zir.Inst.Ref {
1607 const astgen = gz.astgen;
1608
1609 const len = elements.len + 1; // +1 for type
1610 const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
1611 .operands_len = @intCast(len),
1612 });
1613 var extra_index = try reserveExtra(astgen, len);
1614 astgen.extra.items[extra_index] = @intFromEnum(ty_inst);
1615 extra_index += 1;
1616
1617 if (maybe_elem_ty_inst != .none) {
1618 const elem_ri: ResultInfo = .{ .rl = .{ .coerced_ty = maybe_elem_ty_inst } };
1619 for (elements) |elem_init| {
1620 const elem_inst = try expr(gz, scope, elem_ri, elem_init);
1621 astgen.extra.items[extra_index] = @intFromEnum(elem_inst);
1622 extra_index += 1;
1623 }
1624 } else {
1625 for (elements, 0..) |elem_init, i| {
1626 const ri: ResultInfo = .{ .rl = .{ .coerced_ty = try gz.add(.{
1627 .tag = .array_init_elem_type,
1628 .data = .{ .bin = .{
1629 .lhs = ty_inst,
1630 .rhs = @enumFromInt(i),
1631 } },
1632 }) } };
1633
1634 const elem_inst = try expr(gz, scope, ri, elem_init);
1635 astgen.extra.items[extra_index] = @intFromEnum(elem_inst);
1636 extra_index += 1;
1637 }
1638 }
1639
1640 const tag: Zir.Inst.Tag = if (is_ref) .array_init_ref else .array_init;
1641 return try gz.addPlNodePayloadIndex(tag, node, payload_index);
1642}
1643
1644/// An array initialization expression using element pointers.
1645fn arrayInitExprPtr(
1646 gz: *GenZir,
1647 scope: *Scope,
1648 node: Ast.Node.Index,
1649 elements: []const Ast.Node.Index,
1650 ptr_inst: Zir.Inst.Ref,
1651) InnerError!void {
1652 const astgen = gz.astgen;
1653
1654 const array_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node);
1655
1656 const payload_index = try addExtra(astgen, Zir.Inst.Block{
1657 .body_len = @intCast(elements.len),
1658 });
1659 var extra_index = try reserveExtra(astgen, elements.len);
1660
1661 for (elements, 0..) |elem_init, i| {
1662 const elem_ptr_inst = try gz.addPlNode(.array_init_elem_ptr, elem_init, Zir.Inst.ElemPtrImm{
1663 .ptr = array_ptr_inst,
1664 .index = @intCast(i),
1665 });
1666 astgen.extra.items[extra_index] = @intFromEnum(elem_ptr_inst.toIndex().?);
1667 extra_index += 1;
1668 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = elem_ptr_inst } } }, elem_init);
1669 }
1670
1671 _ = try gz.addPlNodePayloadIndex(.validate_ptr_array_init, node, payload_index);
1672}
1673
1674fn structInitExpr(
1675 gz: *GenZir,
1676 scope: *Scope,
1677 ri: ResultInfo,
1678 node: Ast.Node.Index,
1679 struct_init: Ast.full.StructInit,
1680) InnerError!Zir.Inst.Ref {
1681 const astgen = gz.astgen;
1682 const tree = astgen.tree;
1683
1684 if (struct_init.ast.type_expr == 0) {
1685 if (struct_init.ast.fields.len == 0) {
1686 // Anonymous init with no fields.
1687 switch (ri.rl) {
1688 .discard => return .void_value,
1689 .ref_coerced_ty => |ptr_ty_inst| return gz.addUnNode(.struct_init_empty_ref_result, ptr_ty_inst, node),
1690 .ty, .coerced_ty => |ty_inst| return gz.addUnNode(.struct_init_empty_result, ty_inst, node),
1691 .ptr => {
1692 // TODO: should we modify this to use RLS for the field stores here?
1693 const ty_inst = (try ri.rl.resultType(gz, node)).?;
1694 const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node);
1695 return rvalue(gz, ri, val, node);
1696 },
1697 .none, .ref, .inferred_ptr => {
1698 return rvalue(gz, ri, .empty_struct, node);
1699 },
1700 .destructure => |destructure| {
1701 return astgen.failNodeNotes(node, "empty initializer cannot be destructured", .{}, &.{
1702 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
1703 });
1704 },
1705 }
1706 }
1707 } else array: {
1708 const node_tags = tree.nodes.items(.tag);
1709 const main_tokens = tree.nodes.items(.main_token);
1710 const array_type: Ast.full.ArrayType = tree.fullArrayType(struct_init.ast.type_expr) orelse {
1711 if (struct_init.ast.fields.len == 0) {
1712 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
1713 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
1714 return rvalue(gz, ri, result, node);
1715 }
1716 break :array;
1717 };
1718 const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and
1719 // This intentionally does not support `@"_"` syntax.
1720 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_");
1721 if (struct_init.ast.fields.len == 0) {
1722 if (is_inferred_array_len) {
1723 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
1724 const array_type_inst = if (array_type.ast.sentinel == 0) blk: {
1725 break :blk try gz.addPlNode(.array_type, struct_init.ast.type_expr, Zir.Inst.Bin{
1726 .lhs = .zero_usize,
1727 .rhs = elem_type,
1728 });
1729 } else blk: {
1730 const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel);
1731 break :blk try gz.addPlNode(
1732 .array_type_sentinel,
1733 struct_init.ast.type_expr,
1734 Zir.Inst.ArrayTypeSentinel{
1735 .len = .zero_usize,
1736 .elem_type = elem_type,
1737 .sentinel = sentinel,
1738 },
1739 );
1740 };
1741 const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node);
1742 return rvalue(gz, ri, result, node);
1743 }
1744 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
1745 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
1746 return rvalue(gz, ri, result, node);
1747 } else {
1748 return astgen.failNode(
1749 struct_init.ast.type_expr,
1750 "initializing array with struct syntax",
1751 .{},
1752 );
1753 }
1754 }
1755
1756 {
1757 var sfba = std.heap.stackFallback(256, astgen.arena);
1758 const sfba_allocator = sfba.get();
1759
1760 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
1761 try duplicate_names.ensureTotalCapacity(@intCast(struct_init.ast.fields.len));
1762
1763 // When there aren't errors, use this to avoid a second iteration.
1764 var any_duplicate = false;
1765
1766 for (struct_init.ast.fields) |field| {
1767 const name_token = tree.firstToken(field) - 2;
1768 const name_index = try astgen.identAsString(name_token);
1769
1770 const gop = try duplicate_names.getOrPut(name_index);
1771
1772 if (gop.found_existing) {
1773 try gop.value_ptr.append(sfba_allocator, name_token);
1774 any_duplicate = true;
1775 } else {
1776 gop.value_ptr.* = .{};
1777 try gop.value_ptr.append(sfba_allocator, name_token);
1778 }
1779 }
1780
1781 if (any_duplicate) {
1782 var it = duplicate_names.iterator();
1783
1784 while (it.next()) |entry| {
1785 const record = entry.value_ptr.*;
1786 if (record.items.len > 1) {
1787 var error_notes = std.ArrayList(u32).init(astgen.arena);
1788
1789 for (record.items[1..]) |duplicate| {
1790 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate name here", .{}));
1791 }
1792
1793 try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));
1794
1795 try astgen.appendErrorTokNotes(
1796 record.items[0],
1797 "duplicate struct field name",
1798 .{},
1799 error_notes.items,
1800 );
1801 }
1802 }
1803
1804 return error.AnalysisFail;
1805 }
1806 }
1807
1808 if (struct_init.ast.type_expr != 0) {
1809 // Typed inits do not use RLS for language simplicity.
1810 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
1811 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
1812 switch (ri.rl) {
1813 .ref => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true),
1814 else => {
1815 const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false);
1816 return rvalue(gz, ri, struct_inst, node);
1817 },
1818 }
1819 }
1820
1821 switch (ri.rl) {
1822 .none => return structInitExprAnon(gz, scope, node, struct_init),
1823 .discard => {
1824 // Even if discarding we must perform side-effects.
1825 for (struct_init.ast.fields) |field_init| {
1826 _ = try expr(gz, scope, .{ .rl = .discard }, field_init);
1827 }
1828 return .void_value;
1829 },
1830 .ref => {
1831 const result = try structInitExprAnon(gz, scope, node, struct_init);
1832 return gz.addUnTok(.ref, result, tree.firstToken(node));
1833 },
1834 .ref_coerced_ty => |ptr_ty_inst| {
1835 const result_ty_inst = try gz.addUnNode(.elem_type, ptr_ty_inst, node);
1836 _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node);
1837 return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, true);
1838 },
1839 .ty, .coerced_ty => |result_ty_inst| {
1840 _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node);
1841 return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, false);
1842 },
1843 .ptr => |ptr| {
1844 try structInitExprPtr(gz, scope, node, struct_init, ptr.inst);
1845 return .void_value;
1846 },
1847 .inferred_ptr => {
1848 // We can't get field pointers of an untyped inferred alloc, so must perform a
1849 // standard anonymous initialization followed by an rvalue store.
1850 // See corresponding logic in arrayInitExpr.
1851 const struct_inst = try structInitExprAnon(gz, scope, node, struct_init);
1852 return rvalue(gz, ri, struct_inst, node);
1853 },
1854 .destructure => |destructure| {
1855 // This is an untyped init, so is an actual struct, which does
1856 // not support destructuring.
1857 return astgen.failNodeNotes(node, "struct value cannot be destructured", .{}, &.{
1858 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
1859 });
1860 },
1861 }
1862}
1863
1864/// A struct initialization expression using a `struct_init_anon` instruction.
1865fn structInitExprAnon(
1866 gz: *GenZir,
1867 scope: *Scope,
1868 node: Ast.Node.Index,
1869 struct_init: Ast.full.StructInit,
1870) InnerError!Zir.Inst.Ref {
1871 const astgen = gz.astgen;
1872 const tree = astgen.tree;
1873
1874 const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{
1875 .fields_len = @intCast(struct_init.ast.fields.len),
1876 });
1877 const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).Struct.fields.len;
1878 var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
1879
1880 for (struct_init.ast.fields) |field_init| {
1881 const name_token = tree.firstToken(field_init) - 2;
1882 const str_index = try astgen.identAsString(name_token);
1883 setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{
1884 .field_name = str_index,
1885 .init = try expr(gz, scope, .{ .rl = .none }, field_init),
1886 });
1887 extra_index += field_size;
1888 }
1889
1890 return gz.addPlNodePayloadIndex(.struct_init_anon, node, payload_index);
1891}
1892
1893/// A struct initialization expression using a `struct_init` or `struct_init_ref` instruction.
1894fn structInitExprTyped(
1895 gz: *GenZir,
1896 scope: *Scope,
1897 node: Ast.Node.Index,
1898 struct_init: Ast.full.StructInit,
1899 ty_inst: Zir.Inst.Ref,
1900 is_ref: bool,
1901) InnerError!Zir.Inst.Ref {
1902 const astgen = gz.astgen;
1903 const tree = astgen.tree;
1904
1905 const payload_index = try addExtra(astgen, Zir.Inst.StructInit{
1906 .fields_len = @intCast(struct_init.ast.fields.len),
1907 });
1908 const field_size = @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len;
1909 var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
1910
1911 for (struct_init.ast.fields) |field_init| {
1912 const name_token = tree.firstToken(field_init) - 2;
1913 const str_index = try astgen.identAsString(name_token);
1914 const field_ty_inst = try gz.addPlNode(.struct_init_field_type, field_init, Zir.Inst.FieldType{
1915 .container_type = ty_inst,
1916 .name_start = str_index,
1917 });
1918 setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{
1919 .field_type = field_ty_inst.toIndex().?,
1920 .init = try expr(gz, scope, .{ .rl = .{ .coerced_ty = field_ty_inst } }, field_init),
1921 });
1922 extra_index += field_size;
1923 }
1924
1925 const tag: Zir.Inst.Tag = if (is_ref) .struct_init_ref else .struct_init;
1926 return gz.addPlNodePayloadIndex(tag, node, payload_index);
1927}
1928
1929/// A struct initialization expression using field pointers.
1930fn structInitExprPtr(
1931 gz: *GenZir,
1932 scope: *Scope,
1933 node: Ast.Node.Index,
1934 struct_init: Ast.full.StructInit,
1935 ptr_inst: Zir.Inst.Ref,
1936) InnerError!void {
1937 const astgen = gz.astgen;
1938 const tree = astgen.tree;
1939
1940 const struct_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node);
1941
1942 const payload_index = try addExtra(astgen, Zir.Inst.Block{
1943 .body_len = @intCast(struct_init.ast.fields.len),
1944 });
1945 var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len);
1946
1947 for (struct_init.ast.fields) |field_init| {
1948 const name_token = tree.firstToken(field_init) - 2;
1949 const str_index = try astgen.identAsString(name_token);
1950 const field_ptr = try gz.addPlNode(.struct_init_field_ptr, field_init, Zir.Inst.Field{
1951 .lhs = struct_ptr_inst,
1952 .field_name_start = str_index,
1953 });
1954 astgen.extra.items[extra_index] = @intFromEnum(field_ptr.toIndex().?);
1955 extra_index += 1;
1956 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init);
1957 }
1958
1959 _ = try gz.addPlNodePayloadIndex(.validate_ptr_struct_init, node, payload_index);
1960}
1961
1962/// This explicitly calls expr in a comptime scope by wrapping it in a `block_comptime` if
1963/// necessary. It should be used whenever we need to force compile-time evaluation of something,
1964/// such as a type.
1965/// The function corresponding to `comptime` expression syntax is `comptimeExprAst`.
1966fn comptimeExpr(
1967 gz: *GenZir,
1968 scope: *Scope,
1969 ri: ResultInfo,
1970 node: Ast.Node.Index,
1971) InnerError!Zir.Inst.Ref {
1972 if (gz.is_comptime) {
1973 // No need to change anything!
1974 return expr(gz, scope, ri, node);
1975 }
1976
1977 // There's an optimization here: if the body will be evaluated at comptime regardless, there's
1978 // no need to wrap it in a block. This is hard to determine in general, but we can identify a
1979 // common subset of trivially comptime expressions to take down the size of the ZIR a bit.
1980 const tree = gz.astgen.tree;
1981 const main_tokens = tree.nodes.items(.main_token);
1982 const node_tags = tree.nodes.items(.tag);
1983 switch (node_tags[node]) {
1984 // Any identifier in `primitive_instrs` is trivially comptime. In particular, this includes
1985 // some common types, so we can elide `block_comptime` for a few common type annotations.
1986 .identifier => {
1987 const ident_token = main_tokens[node];
1988 const ident_name_raw = tree.tokenSlice(ident_token);
1989 if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
1990 // No need to worry about result location here, we're not creating a comptime block!
1991 return rvalue(gz, ri, zir_const_ref, node);
1992 }
1993 },
1994
1995 // We can also avoid the block for a few trivial AST tags which are always comptime-known.
1996 .number_literal, .string_literal, .multiline_string_literal, .enum_literal, .error_value => {
1997 // No need to worry about result location here, we're not creating a comptime block!
1998 return expr(gz, scope, ri, node);
1999 },
2000
2001 // Lastly, for labelled blocks, avoid emitting a labelled block directly inside this
2002 // comptime block, because that would be silly! Note that we don't bother doing this for
2003 // unlabelled blocks, since they don't generate blocks at comptime anyway (see `blockExpr`).
2004 .block_two, .block_two_semicolon, .block, .block_semicolon => {
2005 const token_tags = tree.tokens.items(.tag);
2006 const lbrace = main_tokens[node];
2007 // Careful! We can't pass in the real result location here, since it may
2008 // refer to runtime memory. A runtime-to-comptime boundary has to remove
2009 // result location information, compute the result, and copy it to the true
2010 // result location at runtime. We do this below as well.
2011 const ty_only_ri: ResultInfo = .{
2012 .ctx = ri.ctx,
2013 .rl = if (try ri.rl.resultType(gz, node)) |res_ty|
2014 .{ .coerced_ty = res_ty }
2015 else
2016 .none,
2017 };
2018 if (token_tags[lbrace - 1] == .colon and
2019 token_tags[lbrace - 2] == .identifier)
2020 {
2021 const node_datas = tree.nodes.items(.data);
2022 switch (node_tags[node]) {
2023 .block_two, .block_two_semicolon => {
2024 const stmts: [2]Ast.Node.Index = .{ node_datas[node].lhs, node_datas[node].rhs };
2025 const stmt_slice = if (stmts[0] == 0)
2026 stmts[0..0]
2027 else if (stmts[1] == 0)
2028 stmts[0..1]
2029 else
2030 stmts[0..2];
2031
2032 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmt_slice, true);
2033 return rvalue(gz, ri, block_ref, node);
2034 },
2035 .block, .block_semicolon => {
2036 const stmts = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
2037 // Replace result location and copy back later - see above.
2038 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true);
2039 return rvalue(gz, ri, block_ref, node);
2040 },
2041 else => unreachable,
2042 }
2043 }
2044 },
2045
2046 // In other cases, we don't optimize anything - we need a wrapper comptime block.
2047 else => {},
2048 }
2049
2050 var block_scope = gz.makeSubBlock(scope);
2051 block_scope.is_comptime = true;
2052 defer block_scope.unstack();
2053
2054 const block_inst = try gz.makeBlockInst(.block_comptime, node);
2055 // Replace result location and copy back later - see above.
2056 const ty_only_ri: ResultInfo = .{
2057 .ctx = ri.ctx,
2058 .rl = if (try ri.rl.resultType(gz, node)) |res_ty|
2059 .{ .coerced_ty = res_ty }
2060 else
2061 .none,
2062 };
2063 const block_result = try expr(&block_scope, scope, ty_only_ri, node);
2064 if (!gz.refIsNoReturn(block_result)) {
2065 _ = try block_scope.addBreak(.@"break", block_inst, block_result);
2066 }
2067 try block_scope.setBlockBody(block_inst);
2068 try gz.instructions.append(gz.astgen.gpa, block_inst);
2069
2070 return rvalue(gz, ri, block_inst.toRef(), node);
2071}
2072
2073/// This one is for an actual `comptime` syntax, and will emit a compile error if
2074/// the scope is already known to be comptime-evaluated.
2075/// See `comptimeExpr` for the helper function for calling expr in a comptime scope.
2076fn comptimeExprAst(
2077 gz: *GenZir,
2078 scope: *Scope,
2079 ri: ResultInfo,
2080 node: Ast.Node.Index,
2081) InnerError!Zir.Inst.Ref {
2082 const astgen = gz.astgen;
2083 if (gz.is_comptime) {
2084 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
2085 }
2086 const tree = astgen.tree;
2087 const node_datas = tree.nodes.items(.data);
2088 const body_node = node_datas[node].lhs;
2089 return comptimeExpr(gz, scope, ri, body_node);
2090}
2091
2092/// Restore the error return trace index. Performs the restore only if the result is a non-error or
2093/// if the result location is a non-error-handling expression.
2094fn restoreErrRetIndex(
2095 gz: *GenZir,
2096 bt: GenZir.BranchTarget,
2097 ri: ResultInfo,
2098 node: Ast.Node.Index,
2099 result: Zir.Inst.Ref,
2100) !void {
2101 const op = switch (nodeMayEvalToError(gz.astgen.tree, node)) {
2102 .always => return, // never restore/pop
2103 .never => .none, // always restore/pop
2104 .maybe => switch (ri.ctx) {
2105 .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) {
2106 .ptr => |ptr_res| try gz.addUnNode(.load, ptr_res.inst, node),
2107 .inferred_ptr => blk: {
2108 // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr
2109 // before its type has been resolved. There is no valid operand to use here, so error
2110 // traces will be popped prematurely.
2111 // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it.
2112 break :blk .none;
2113 },
2114 .destructure => return, // value must be a tuple or array, so never restore/pop
2115 else => result,
2116 },
2117 else => .none, // always restore/pop
2118 },
2119 };
2120 _ = try gz.addRestoreErrRetIndex(bt, .{ .if_non_error = op }, node);
2121}
2122
2123fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
2124 const astgen = parent_gz.astgen;
2125 const tree = astgen.tree;
2126 const node_datas = tree.nodes.items(.data);
2127 const break_label = node_datas[node].lhs;
2128 const rhs = node_datas[node].rhs;
2129
2130 // Look for the label in the scope.
2131 var scope = parent_scope;
2132 while (true) {
2133 switch (scope.tag) {
2134 .gen_zir => {
2135 const block_gz = scope.cast(GenZir).?;
2136
2137 if (block_gz.cur_defer_node != 0) {
2138 // We are breaking out of a `defer` block.
2139 return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
2140 try astgen.errNoteNode(
2141 block_gz.cur_defer_node,
2142 "defer expression here",
2143 .{},
2144 ),
2145 });
2146 }
2147
2148 const block_inst = blk: {
2149 if (break_label != 0) {
2150 if (block_gz.label) |*label| {
2151 if (try astgen.tokenIdentEql(label.token, break_label)) {
2152 label.used = true;
2153 break :blk label.block_inst;
2154 }
2155 }
2156 } else if (block_gz.break_block.unwrap()) |i| {
2157 break :blk i;
2158 }
2159 // If not the target, start over with the parent
2160 scope = block_gz.parent;
2161 continue;
2162 };
2163 // If we made it here, this block is the target of the break expr
2164
2165 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline)
2166 .break_inline
2167 else
2168 .@"break";
2169
2170 if (rhs == 0) {
2171 _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node);
2172
2173 try genDefers(parent_gz, scope, parent_scope, .normal_only);
2174
2175 // As our last action before the break, "pop" the error trace if needed
2176 if (!block_gz.is_comptime)
2177 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, node);
2178
2179 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
2180 return Zir.Inst.Ref.unreachable_value;
2181 }
2182
2183 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node);
2184
2185 try genDefers(parent_gz, scope, parent_scope, .normal_only);
2186
2187 // As our last action before the break, "pop" the error trace if needed
2188 if (!block_gz.is_comptime)
2189 try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand);
2190
2191 switch (block_gz.break_result_info.rl) {
2192 .ptr => {
2193 // In this case we don't have any mechanism to intercept it;
2194 // we assume the result location is written, and we break with void.
2195 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
2196 },
2197 .discard => {
2198 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
2199 },
2200 else => {
2201 _ = try parent_gz.addBreakWithSrcNode(break_tag, block_inst, operand, rhs);
2202 },
2203 }
2204 return Zir.Inst.Ref.unreachable_value;
2205 },
2206 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2207 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2208 .namespace, .enum_namespace => break,
2209 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2210 .top => unreachable,
2211 }
2212 }
2213 if (break_label != 0) {
2214 const label_name = try astgen.identifierTokenString(break_label);
2215 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
2216 } else {
2217 return astgen.failNode(node, "break expression outside loop", .{});
2218 }
2219}
2220
2221fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
2222 const astgen = parent_gz.astgen;
2223 const tree = astgen.tree;
2224 const node_datas = tree.nodes.items(.data);
2225 const break_label = node_datas[node].lhs;
2226
2227 // Look for the label in the scope.
2228 var scope = parent_scope;
2229 while (true) {
2230 switch (scope.tag) {
2231 .gen_zir => {
2232 const gen_zir = scope.cast(GenZir).?;
2233
2234 if (gen_zir.cur_defer_node != 0) {
2235 return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
2236 try astgen.errNoteNode(
2237 gen_zir.cur_defer_node,
2238 "defer expression here",
2239 .{},
2240 ),
2241 });
2242 }
2243 const continue_block = gen_zir.continue_block.unwrap() orelse {
2244 scope = gen_zir.parent;
2245 continue;
2246 };
2247 if (break_label != 0) blk: {
2248 if (gen_zir.label) |*label| {
2249 if (try astgen.tokenIdentEql(label.token, break_label)) {
2250 label.used = true;
2251 break :blk;
2252 }
2253 }
2254 // found continue but either it has a different label, or no label
2255 scope = gen_zir.parent;
2256 continue;
2257 }
2258
2259 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
2260 .break_inline
2261 else
2262 .@"break";
2263 if (break_tag == .break_inline) {
2264 _ = try parent_gz.addUnNode(.check_comptime_control_flow, continue_block.toRef(), node);
2265 }
2266
2267 // As our last action before the continue, "pop" the error trace if needed
2268 if (!gen_zir.is_comptime)
2269 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node);
2270
2271 _ = try parent_gz.addBreak(break_tag, continue_block, .void_value);
2272 return Zir.Inst.Ref.unreachable_value;
2273 },
2274 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2275 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2276 .defer_normal => {
2277 const defer_scope = scope.cast(Scope.Defer).?;
2278 scope = defer_scope.parent;
2279 try parent_gz.addDefer(defer_scope.index, defer_scope.len);
2280 },
2281 .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2282 .namespace, .enum_namespace => break,
2283 .top => unreachable,
2284 }
2285 }
2286 if (break_label != 0) {
2287 const label_name = try astgen.identifierTokenString(break_label);
2288 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
2289 } else {
2290 return astgen.failNode(node, "continue expression outside loop", .{});
2291 }
2292}
2293
2294fn blockExpr(
2295 gz: *GenZir,
2296 scope: *Scope,
2297 ri: ResultInfo,
2298 block_node: Ast.Node.Index,
2299 statements: []const Ast.Node.Index,
2300) InnerError!Zir.Inst.Ref {
2301 const astgen = gz.astgen;
2302 const tree = astgen.tree;
2303 const main_tokens = tree.nodes.items(.main_token);
2304 const token_tags = tree.tokens.items(.tag);
2305
2306 const lbrace = main_tokens[block_node];
2307 if (token_tags[lbrace - 1] == .colon and
2308 token_tags[lbrace - 2] == .identifier)
2309 {
2310 return labeledBlockExpr(gz, scope, ri, block_node, statements, false);
2311 }
2312
2313 if (!gz.is_comptime) {
2314 // Since this block is unlabeled, its control flow is effectively linear and we
2315 // can *almost* get away with inlining the block here. However, we actually need
2316 // to preserve the .block for Sema, to properly pop the error return trace.
2317
2318 const block_tag: Zir.Inst.Tag = .block;
2319 const block_inst = try gz.makeBlockInst(block_tag, block_node);
2320 try gz.instructions.append(astgen.gpa, block_inst);
2321
2322 var block_scope = gz.makeSubBlock(scope);
2323 defer block_scope.unstack();
2324
2325 try blockExprStmts(&block_scope, &block_scope.base, statements);
2326
2327 if (!block_scope.endsWithNoReturn()) {
2328 // As our last action before the break, "pop" the error trace if needed
2329 _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node);
2330 _ = try block_scope.addBreak(.@"break", block_inst, .void_value);
2331 }
2332
2333 try block_scope.setBlockBody(block_inst);
2334 } else {
2335 var sub_gz = gz.makeSubBlock(scope);
2336 try blockExprStmts(&sub_gz, &sub_gz.base, statements);
2337 }
2338
2339 return rvalue(gz, ri, .void_value, block_node);
2340}
2341
2342fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void {
2343 // Look for the label in the scope.
2344 var scope = parent_scope;
2345 while (true) {
2346 switch (scope.tag) {
2347 .gen_zir => {
2348 const gen_zir = scope.cast(GenZir).?;
2349 if (gen_zir.label) |prev_label| {
2350 if (try astgen.tokenIdentEql(label, prev_label.token)) {
2351 const label_name = try astgen.identifierTokenString(label);
2352 return astgen.failTokNotes(label, "redefinition of label '{s}'", .{
2353 label_name,
2354 }, &[_]u32{
2355 try astgen.errNoteTok(
2356 prev_label.token,
2357 "previous definition here",
2358 .{},
2359 ),
2360 });
2361 }
2362 }
2363 scope = gen_zir.parent;
2364 },
2365 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2366 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2367 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2368 .namespace, .enum_namespace => break,
2369 .top => unreachable,
2370 }
2371 }
2372}
2373
2374fn labeledBlockExpr(
2375 gz: *GenZir,
2376 parent_scope: *Scope,
2377 ri: ResultInfo,
2378 block_node: Ast.Node.Index,
2379 statements: []const Ast.Node.Index,
2380 force_comptime: bool,
2381) InnerError!Zir.Inst.Ref {
2382 const astgen = gz.astgen;
2383 const tree = astgen.tree;
2384 const main_tokens = tree.nodes.items(.main_token);
2385 const token_tags = tree.tokens.items(.tag);
2386
2387 const lbrace = main_tokens[block_node];
2388 const label_token = lbrace - 2;
2389 assert(token_tags[label_token] == .identifier);
2390
2391 try astgen.checkLabelRedefinition(parent_scope, label_token);
2392
2393 const need_rl = astgen.nodes_need_rl.contains(block_node);
2394 const block_ri: ResultInfo = if (need_rl) ri else .{
2395 .rl = switch (ri.rl) {
2396 .ptr => .{ .ty = (try ri.rl.resultType(gz, block_node)).? },
2397 .inferred_ptr => .none,
2398 else => ri.rl,
2399 },
2400 .ctx = ri.ctx,
2401 };
2402 // We need to call `rvalue` to write through to the pointer only if we had a
2403 // result pointer and aren't forwarding it.
2404 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
2405 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
2406
2407 // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
2408 // so that break statements can reference it.
2409 const block_tag: Zir.Inst.Tag = if (force_comptime) .block_comptime else .block;
2410 const block_inst = try gz.makeBlockInst(block_tag, block_node);
2411 try gz.instructions.append(astgen.gpa, block_inst);
2412 var block_scope = gz.makeSubBlock(parent_scope);
2413 block_scope.label = GenZir.Label{
2414 .token = label_token,
2415 .block_inst = block_inst,
2416 };
2417 block_scope.setBreakResultInfo(block_ri);
2418 if (force_comptime) block_scope.is_comptime = true;
2419 defer block_scope.unstack();
2420
2421 try blockExprStmts(&block_scope, &block_scope.base, statements);
2422 if (!block_scope.endsWithNoReturn()) {
2423 // As our last action before the return, "pop" the error trace if needed
2424 _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node);
2425 _ = try block_scope.addBreak(.@"break", block_inst, .void_value);
2426 }
2427
2428 if (!block_scope.label.?.used) {
2429 try astgen.appendErrorTok(label_token, "unused block label", .{});
2430 }
2431
2432 try block_scope.setBlockBody(block_inst);
2433 if (need_result_rvalue) {
2434 return rvalue(gz, ri, block_inst.toRef(), block_node);
2435 } else {
2436 return block_inst.toRef();
2437 }
2438}
2439
2440fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index) !void {
2441 const astgen = gz.astgen;
2442 const tree = astgen.tree;
2443 const node_tags = tree.nodes.items(.tag);
2444 const node_data = tree.nodes.items(.data);
2445
2446 if (statements.len == 0) return;
2447
2448 var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa);
2449 defer block_arena.deinit();
2450 const block_arena_allocator = block_arena.allocator();
2451
2452 var noreturn_src_node: Ast.Node.Index = 0;
2453 var scope = parent_scope;
2454 for (statements) |statement| {
2455 if (noreturn_src_node != 0) {
2456 try astgen.appendErrorNodeNotes(
2457 statement,
2458 "unreachable code",
2459 .{},
2460 &[_]u32{
2461 try astgen.errNoteNode(
2462 noreturn_src_node,
2463 "control flow is diverted here",
2464 .{},
2465 ),
2466 },
2467 );
2468 }
2469 var inner_node = statement;
2470 while (true) {
2471 switch (node_tags[inner_node]) {
2472 // zig fmt: off
2473 .global_var_decl,
2474 .local_var_decl,
2475 .simple_var_decl,
2476 .aligned_var_decl, => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.fullVarDecl(statement).?),
2477
2478 .assign_destructure => scope = try assignDestructureMaybeDecls(gz, scope, statement, block_arena_allocator),
2479
2480 .@"defer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal),
2481 .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error),
2482
2483 .assign => try assign(gz, scope, statement),
2484
2485 .assign_shl => try assignShift(gz, scope, statement, .shl),
2486 .assign_shr => try assignShift(gz, scope, statement, .shr),
2487
2488 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and),
2489 .assign_bit_or => try assignOp(gz, scope, statement, .bit_or),
2490 .assign_bit_xor => try assignOp(gz, scope, statement, .xor),
2491 .assign_div => try assignOp(gz, scope, statement, .div),
2492 .assign_sub => try assignOp(gz, scope, statement, .sub),
2493 .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap),
2494 .assign_mod => try assignOp(gz, scope, statement, .mod_rem),
2495 .assign_add => try assignOp(gz, scope, statement, .add),
2496 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
2497 .assign_mul => try assignOp(gz, scope, statement, .mul),
2498 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
2499
2500 .grouped_expression => {
2501 inner_node = node_data[statement].lhs;
2502 continue;
2503 },
2504
2505 .while_simple,
2506 .while_cont,
2507 .@"while", => _ = try whileExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullWhile(inner_node).?, true),
2508
2509 .for_simple,
2510 .@"for", => _ = try forExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullFor(inner_node).?, true),
2511
2512 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
2513 // zig fmt: on
2514 }
2515 break;
2516 }
2517 }
2518
2519 try genDefers(gz, parent_scope, scope, .normal_only);
2520 try checkUsed(gz, parent_scope, scope);
2521}
2522
2523/// Returns AST source node of the thing that is noreturn if the statement is
2524/// definitely `noreturn`. Otherwise returns 0.
2525fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
2526 try emitDbgNode(gz, statement);
2527 // We need to emit an error if the result is not `noreturn` or `void`, but
2528 // we want to avoid adding the ZIR instruction if possible for performance.
2529 const maybe_unused_result = try expr(gz, scope, .{ .rl = .none }, statement);
2530 return addEnsureResult(gz, maybe_unused_result, statement);
2531}
2532
2533fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
2534 var noreturn_src_node: Ast.Node.Index = 0;
2535 const elide_check = if (maybe_unused_result.toIndex()) |inst| b: {
2536 // Note that this array becomes invalid after appending more items to it
2537 // in the above while loop.
2538 const zir_tags = gz.astgen.instructions.items(.tag);
2539 switch (zir_tags[@intFromEnum(inst)]) {
2540 // For some instructions, modify the zir data
2541 // so we can avoid a separate ensure_result_used instruction.
2542 .call, .field_call => {
2543 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index;
2544 comptime assert(std.meta.fieldIndex(Zir.Inst.Call, "flags") ==
2545 std.meta.fieldIndex(Zir.Inst.FieldCall, "flags"));
2546 const flags: *Zir.Inst.Call.Flags = @ptrCast(&gz.astgen.extra.items[
2547 break_extra + std.meta.fieldIndex(Zir.Inst.Call, "flags").?
2548 ]);
2549 flags.ensure_result_used = true;
2550 break :b true;
2551 },
2552 .builtin_call => {
2553 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index;
2554 const flags: *Zir.Inst.BuiltinCall.Flags = @ptrCast(&gz.astgen.extra.items[
2555 break_extra + std.meta.fieldIndex(Zir.Inst.BuiltinCall, "flags").?
2556 ]);
2557 flags.ensure_result_used = true;
2558 break :b true;
2559 },
2560
2561 // ZIR instructions that might be a type other than `noreturn` or `void`.
2562 .add,
2563 .addwrap,
2564 .add_sat,
2565 .add_unsafe,
2566 .param,
2567 .param_comptime,
2568 .param_anytype,
2569 .param_anytype_comptime,
2570 .alloc,
2571 .alloc_mut,
2572 .alloc_comptime_mut,
2573 .alloc_inferred,
2574 .alloc_inferred_mut,
2575 .alloc_inferred_comptime,
2576 .alloc_inferred_comptime_mut,
2577 .make_ptr_const,
2578 .array_cat,
2579 .array_mul,
2580 .array_type,
2581 .array_type_sentinel,
2582 .elem_type,
2583 .indexable_ptr_elem_type,
2584 .vector_elem_type,
2585 .vector_type,
2586 .indexable_ptr_len,
2587 .anyframe_type,
2588 .as_node,
2589 .as_shift_operand,
2590 .bit_and,
2591 .bitcast,
2592 .bit_or,
2593 .block,
2594 .block_comptime,
2595 .block_inline,
2596 .declaration,
2597 .suspend_block,
2598 .loop,
2599 .bool_br_and,
2600 .bool_br_or,
2601 .bool_not,
2602 .cmp_lt,
2603 .cmp_lte,
2604 .cmp_eq,
2605 .cmp_gte,
2606 .cmp_gt,
2607 .cmp_neq,
2608 .decl_ref,
2609 .decl_val,
2610 .load,
2611 .div,
2612 .elem_ptr,
2613 .elem_val,
2614 .elem_ptr_node,
2615 .elem_val_node,
2616 .elem_val_imm,
2617 .field_ptr,
2618 .field_val,
2619 .field_ptr_named,
2620 .field_val_named,
2621 .func,
2622 .func_inferred,
2623 .func_fancy,
2624 .int,
2625 .int_big,
2626 .float,
2627 .float128,
2628 .int_type,
2629 .is_non_null,
2630 .is_non_null_ptr,
2631 .is_non_err,
2632 .is_non_err_ptr,
2633 .ret_is_non_err,
2634 .mod_rem,
2635 .mul,
2636 .mulwrap,
2637 .mul_sat,
2638 .ref,
2639 .shl,
2640 .shl_sat,
2641 .shr,
2642 .str,
2643 .sub,
2644 .subwrap,
2645 .sub_sat,
2646 .negate,
2647 .negate_wrap,
2648 .typeof,
2649 .typeof_builtin,
2650 .xor,
2651 .optional_type,
2652 .optional_payload_safe,
2653 .optional_payload_unsafe,
2654 .optional_payload_safe_ptr,
2655 .optional_payload_unsafe_ptr,
2656 .err_union_payload_unsafe,
2657 .err_union_payload_unsafe_ptr,
2658 .err_union_code,
2659 .err_union_code_ptr,
2660 .ptr_type,
2661 .enum_literal,
2662 .merge_error_sets,
2663 .error_union_type,
2664 .bit_not,
2665 .error_value,
2666 .slice_start,
2667 .slice_end,
2668 .slice_sentinel,
2669 .slice_length,
2670 .import,
2671 .switch_block,
2672 .switch_block_ref,
2673 .switch_block_err_union,
2674 .union_init,
2675 .field_type_ref,
2676 .error_set_decl,
2677 .error_set_decl_anon,
2678 .error_set_decl_func,
2679 .enum_from_int,
2680 .int_from_enum,
2681 .type_info,
2682 .size_of,
2683 .bit_size_of,
2684 .typeof_log2_int_type,
2685 .int_from_ptr,
2686 .align_of,
2687 .int_from_bool,
2688 .embed_file,
2689 .error_name,
2690 .sqrt,
2691 .sin,
2692 .cos,
2693 .tan,
2694 .exp,
2695 .exp2,
2696 .log,
2697 .log2,
2698 .log10,
2699 .abs,
2700 .floor,
2701 .ceil,
2702 .trunc,
2703 .round,
2704 .tag_name,
2705 .type_name,
2706 .frame_type,
2707 .frame_size,
2708 .int_from_float,
2709 .float_from_int,
2710 .ptr_from_int,
2711 .float_cast,
2712 .int_cast,
2713 .ptr_cast,
2714 .truncate,
2715 .has_decl,
2716 .has_field,
2717 .clz,
2718 .ctz,
2719 .pop_count,
2720 .byte_swap,
2721 .bit_reverse,
2722 .div_exact,
2723 .div_floor,
2724 .div_trunc,
2725 .mod,
2726 .rem,
2727 .shl_exact,
2728 .shr_exact,
2729 .bit_offset_of,
2730 .offset_of,
2731 .splat,
2732 .reduce,
2733 .shuffle,
2734 .atomic_load,
2735 .atomic_rmw,
2736 .mul_add,
2737 .field_parent_ptr,
2738 .max,
2739 .min,
2740 .c_import,
2741 .@"resume",
2742 .@"await",
2743 .ret_err_value_code,
2744 .closure_get,
2745 .ret_ptr,
2746 .ret_type,
2747 .for_len,
2748 .@"try",
2749 .try_ptr,
2750 .opt_eu_base_ptr_init,
2751 .coerce_ptr_elem_ty,
2752 .struct_init_empty,
2753 .struct_init_empty_result,
2754 .struct_init_empty_ref_result,
2755 .struct_init_anon,
2756 .struct_init,
2757 .struct_init_ref,
2758 .struct_init_field_type,
2759 .struct_init_field_ptr,
2760 .array_init_anon,
2761 .array_init,
2762 .array_init_ref,
2763 .validate_array_init_ref_ty,
2764 .array_init_elem_type,
2765 .array_init_elem_ptr,
2766 => break :b false,
2767
2768 .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {
2769 .breakpoint,
2770 .fence,
2771 .set_float_mode,
2772 .set_align_stack,
2773 .set_cold,
2774 => break :b true,
2775 else => break :b false,
2776 },
2777
2778 // ZIR instructions that are always `noreturn`.
2779 .@"break",
2780 .break_inline,
2781 .condbr,
2782 .condbr_inline,
2783 .compile_error,
2784 .ret_node,
2785 .ret_load,
2786 .ret_implicit,
2787 .ret_err_value,
2788 .@"unreachable",
2789 .repeat,
2790 .repeat_inline,
2791 .panic,
2792 .trap,
2793 .check_comptime_control_flow,
2794 => {
2795 noreturn_src_node = statement;
2796 break :b true;
2797 },
2798
2799 // ZIR instructions that are always `void`.
2800 .dbg_stmt,
2801 .dbg_var_ptr,
2802 .dbg_var_val,
2803 .ensure_result_used,
2804 .ensure_result_non_error,
2805 .ensure_err_union_payload_void,
2806 .@"export",
2807 .export_value,
2808 .set_eval_branch_quota,
2809 .atomic_store,
2810 .store_node,
2811 .store_to_inferred_ptr,
2812 .resolve_inferred_alloc,
2813 .set_runtime_safety,
2814 .closure_capture,
2815 .memcpy,
2816 .memset,
2817 .validate_deref,
2818 .validate_destructure,
2819 .save_err_ret_index,
2820 .restore_err_ret_index_unconditional,
2821 .restore_err_ret_index_fn_entry,
2822 .validate_struct_init_ty,
2823 .validate_struct_init_result_ty,
2824 .validate_ptr_struct_init,
2825 .validate_array_init_ty,
2826 .validate_array_init_result_ty,
2827 .validate_ptr_array_init,
2828 .validate_ref_ty,
2829 => break :b true,
2830
2831 .@"defer" => unreachable,
2832 .defer_err_code => unreachable,
2833 }
2834 } else switch (maybe_unused_result) {
2835 .none => unreachable,
2836
2837 .unreachable_value => b: {
2838 noreturn_src_node = statement;
2839 break :b true;
2840 },
2841
2842 .void_value => true,
2843
2844 else => false,
2845 };
2846 if (!elide_check) {
2847 _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
2848 }
2849 return noreturn_src_node;
2850}
2851
2852fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
2853 have_any: bool,
2854 have_normal: bool,
2855 have_err: bool,
2856 need_err_code: bool,
2857} {
2858 var have_normal = false;
2859 var have_err = false;
2860 var need_err_code = false;
2861 var scope = inner_scope;
2862 while (scope != outer_scope) {
2863 switch (scope.tag) {
2864 .gen_zir => scope = scope.cast(GenZir).?.parent,
2865 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2866 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2867 .defer_normal => {
2868 const defer_scope = scope.cast(Scope.Defer).?;
2869 scope = defer_scope.parent;
2870
2871 have_normal = true;
2872 },
2873 .defer_error => {
2874 const defer_scope = scope.cast(Scope.Defer).?;
2875 scope = defer_scope.parent;
2876
2877 have_err = true;
2878
2879 const have_err_payload = defer_scope.remapped_err_code != .none;
2880 need_err_code = need_err_code or have_err_payload;
2881 },
2882 .namespace, .enum_namespace => unreachable,
2883 .top => unreachable,
2884 }
2885 }
2886 return .{
2887 .have_any = have_normal or have_err,
2888 .have_normal = have_normal,
2889 .have_err = have_err,
2890 .need_err_code = need_err_code,
2891 };
2892}
2893
2894const DefersToEmit = union(enum) {
2895 both: Zir.Inst.Ref, // err code
2896 both_sans_err,
2897 normal_only,
2898};
2899
2900fn genDefers(
2901 gz: *GenZir,
2902 outer_scope: *Scope,
2903 inner_scope: *Scope,
2904 which_ones: DefersToEmit,
2905) InnerError!void {
2906 const gpa = gz.astgen.gpa;
2907
2908 var scope = inner_scope;
2909 while (scope != outer_scope) {
2910 switch (scope.tag) {
2911 .gen_zir => scope = scope.cast(GenZir).?.parent,
2912 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2913 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2914 .defer_normal => {
2915 const defer_scope = scope.cast(Scope.Defer).?;
2916 scope = defer_scope.parent;
2917 try gz.addDefer(defer_scope.index, defer_scope.len);
2918 },
2919 .defer_error => {
2920 const defer_scope = scope.cast(Scope.Defer).?;
2921 scope = defer_scope.parent;
2922 switch (which_ones) {
2923 .both_sans_err => {
2924 try gz.addDefer(defer_scope.index, defer_scope.len);
2925 },
2926 .both => |err_code| {
2927 if (defer_scope.remapped_err_code.unwrap()) |remapped_err_code| {
2928 try gz.instructions.ensureUnusedCapacity(gpa, 1);
2929 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
2930
2931 const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{
2932 .remapped_err_code = remapped_err_code,
2933 .index = defer_scope.index,
2934 .len = defer_scope.len,
2935 });
2936 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
2937 gz.astgen.instructions.appendAssumeCapacity(.{
2938 .tag = .defer_err_code,
2939 .data = .{ .defer_err_code = .{
2940 .err_code = err_code,
2941 .payload_index = payload_index,
2942 } },
2943 });
2944 gz.instructions.appendAssumeCapacity(new_index);
2945 } else {
2946 try gz.addDefer(defer_scope.index, defer_scope.len);
2947 }
2948 },
2949 .normal_only => continue,
2950 }
2951 },
2952 .namespace, .enum_namespace => unreachable,
2953 .top => unreachable,
2954 }
2955 }
2956}
2957
2958fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!void {
2959 const astgen = gz.astgen;
2960
2961 var scope = inner_scope;
2962 while (scope != outer_scope) {
2963 switch (scope.tag) {
2964 .gen_zir => scope = scope.cast(GenZir).?.parent,
2965 .local_val => {
2966 const s = scope.cast(Scope.LocalVal).?;
2967 if (s.used == 0 and s.discarded == 0) {
2968 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
2969 } else if (s.used != 0 and s.discarded != 0) {
2970 try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
2971 try gz.astgen.errNoteTok(s.used, "used here", .{}),
2972 });
2973 }
2974 scope = s.parent;
2975 },
2976 .local_ptr => {
2977 const s = scope.cast(Scope.LocalPtr).?;
2978 if (s.used == 0 and s.discarded == 0) {
2979 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
2980 } else {
2981 if (s.used != 0 and s.discarded != 0) {
2982 try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
2983 try astgen.errNoteTok(s.used, "used here", .{}),
2984 });
2985 }
2986 if (s.id_cat == .@"local variable" and !s.used_as_lvalue) {
2987 try astgen.appendErrorTokNotes(s.token_src, "local variable is never mutated", .{}, &.{
2988 try astgen.errNoteTok(s.token_src, "consider using 'const'", .{}),
2989 });
2990 }
2991 }
2992
2993 scope = s.parent;
2994 },
2995 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2996 .namespace, .enum_namespace => unreachable,
2997 .top => unreachable,
2998 }
2999 }
3000}
3001
3002fn deferStmt(
3003 gz: *GenZir,
3004 scope: *Scope,
3005 node: Ast.Node.Index,
3006 block_arena: Allocator,
3007 scope_tag: Scope.Tag,
3008) InnerError!*Scope {
3009 var defer_gen = gz.makeSubBlock(scope);
3010 defer_gen.cur_defer_node = node;
3011 defer_gen.any_defer_node = node;
3012 defer defer_gen.unstack();
3013
3014 const tree = gz.astgen.tree;
3015 const node_datas = tree.nodes.items(.data);
3016 const expr_node = node_datas[node].rhs;
3017
3018 const payload_token = node_datas[node].lhs;
3019 var local_val_scope: Scope.LocalVal = undefined;
3020 var opt_remapped_err_code: Zir.Inst.OptionalIndex = .none;
3021 const have_err_code = scope_tag == .defer_error and payload_token != 0;
3022 const sub_scope = if (!have_err_code) &defer_gen.base else blk: {
3023 const ident_name = try gz.astgen.identAsString(payload_token);
3024 const remapped_err_code: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
3025 opt_remapped_err_code = remapped_err_code.toOptional();
3026 try gz.astgen.instructions.append(gz.astgen.gpa, .{
3027 .tag = .extended,
3028 .data = .{ .extended = .{
3029 .opcode = .value_placeholder,
3030 .small = undefined,
3031 .operand = undefined,
3032 } },
3033 });
3034 const remapped_err_code_ref = remapped_err_code.toRef();
3035 local_val_scope = .{
3036 .parent = &defer_gen.base,
3037 .gen_zir = gz,
3038 .name = ident_name,
3039 .inst = remapped_err_code_ref,
3040 .token_src = payload_token,
3041 .id_cat = .capture,
3042 };
3043 try gz.addDbgVar(.dbg_var_val, ident_name, remapped_err_code_ref);
3044 break :blk &local_val_scope.base;
3045 };
3046 _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node);
3047 try checkUsed(gz, scope, sub_scope);
3048 _ = try defer_gen.addBreak(.break_inline, @enumFromInt(0), .void_value);
3049
3050 // We must handle ref_table for remapped_err_code manually.
3051 const body = defer_gen.instructionsSlice();
3052 const body_len = blk: {
3053 var refs: u32 = 0;
3054 if (opt_remapped_err_code.unwrap()) |remapped_err_code| {
3055 var cur_inst = remapped_err_code;
3056 while (gz.astgen.ref_table.get(cur_inst)) |ref_inst| {
3057 refs += 1;
3058 cur_inst = ref_inst;
3059 }
3060 }
3061 break :blk gz.astgen.countBodyLenAfterFixups(body) + refs;
3062 };
3063
3064 const index: u32 = @intCast(gz.astgen.extra.items.len);
3065 try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len);
3066 if (opt_remapped_err_code.unwrap()) |remapped_err_code| {
3067 if (gz.astgen.ref_table.fetchRemove(remapped_err_code)) |kv| {
3068 gz.astgen.appendPossiblyRefdBodyInst(&gz.astgen.extra, kv.value);
3069 }
3070 }
3071 gz.astgen.appendBodyWithFixups(body);
3072
3073 const defer_scope = try block_arena.create(Scope.Defer);
3074
3075 defer_scope.* = .{
3076 .base = .{ .tag = scope_tag },
3077 .parent = scope,
3078 .index = index,
3079 .len = body_len,
3080 .remapped_err_code = opt_remapped_err_code,
3081 };
3082 return &defer_scope.base;
3083}
3084
3085fn varDecl(
3086 gz: *GenZir,
3087 scope: *Scope,
3088 node: Ast.Node.Index,
3089 block_arena: Allocator,
3090 var_decl: Ast.full.VarDecl,
3091) InnerError!*Scope {
3092 try emitDbgNode(gz, node);
3093 const astgen = gz.astgen;
3094 const tree = astgen.tree;
3095 const token_tags = tree.tokens.items(.tag);
3096 const main_tokens = tree.nodes.items(.main_token);
3097
3098 const name_token = var_decl.ast.mut_token + 1;
3099 const ident_name_raw = tree.tokenSlice(name_token);
3100 if (mem.eql(u8, ident_name_raw, "_")) {
3101 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
3102 }
3103 const ident_name = try astgen.identAsString(name_token);
3104
3105 try astgen.detectLocalShadowing(
3106 scope,
3107 ident_name,
3108 name_token,
3109 ident_name_raw,
3110 if (token_tags[var_decl.ast.mut_token] == .keyword_const) .@"local constant" else .@"local variable",
3111 );
3112
3113 if (var_decl.ast.init_node == 0) {
3114 return astgen.failNode(node, "variables must be initialized", .{});
3115 }
3116
3117 if (var_decl.ast.addrspace_node != 0) {
3118 return astgen.failTok(main_tokens[var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
3119 }
3120
3121 if (var_decl.ast.section_node != 0) {
3122 return astgen.failTok(main_tokens[var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
3123 }
3124
3125 const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0)
3126 try expr(gz, scope, coerced_align_ri, var_decl.ast.align_node)
3127 else
3128 .none;
3129
3130 switch (token_tags[var_decl.ast.mut_token]) {
3131 .keyword_const => {
3132 if (var_decl.comptime_token) |comptime_token| {
3133 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
3134 }
3135
3136 // Depending on the type of AST the initialization expression is, we may need an lvalue
3137 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
3138 // the variable, no memory location needed.
3139 const type_node = var_decl.ast.type_node;
3140 if (align_inst == .none and
3141 !astgen.nodes_need_rl.contains(node))
3142 {
3143 const result_info: ResultInfo = if (type_node != 0) .{
3144 .rl = .{ .ty = try typeExpr(gz, scope, type_node) },
3145 .ctx = .const_init,
3146 } else .{ .rl = .none, .ctx = .const_init };
3147 const prev_anon_name_strategy = gz.anon_name_strategy;
3148 gz.anon_name_strategy = .dbg_var;
3149 const init_inst = try reachableExpr(gz, scope, result_info, var_decl.ast.init_node, node);
3150 gz.anon_name_strategy = prev_anon_name_strategy;
3151
3152 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
3153
3154 // The const init expression may have modified the error return trace, so signal
3155 // to Sema that it should save the new index for restoring later.
3156 if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node))
3157 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
3158
3159 const sub_scope = try block_arena.create(Scope.LocalVal);
3160 sub_scope.* = .{
3161 .parent = scope,
3162 .gen_zir = gz,
3163 .name = ident_name,
3164 .inst = init_inst,
3165 .token_src = name_token,
3166 .id_cat = .@"local constant",
3167 };
3168 return &sub_scope.base;
3169 }
3170
3171 const is_comptime = gz.is_comptime or
3172 tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime";
3173
3174 var resolve_inferred_alloc: Zir.Inst.Ref = .none;
3175 var opt_type_inst: Zir.Inst.Ref = .none;
3176 const init_rl: ResultInfo.Loc = if (type_node != 0) init_rl: {
3177 const type_inst = try typeExpr(gz, scope, type_node);
3178 opt_type_inst = type_inst;
3179 if (align_inst == .none) {
3180 break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } };
3181 } else {
3182 break :init_rl .{ .ptr = .{ .inst = try gz.addAllocExtended(.{
3183 .node = node,
3184 .type_inst = type_inst,
3185 .align_inst = align_inst,
3186 .is_const = true,
3187 .is_comptime = is_comptime,
3188 }) } };
3189 }
3190 } else init_rl: {
3191 const alloc_inst = if (align_inst == .none) ptr: {
3192 const tag: Zir.Inst.Tag = if (is_comptime)
3193 .alloc_inferred_comptime
3194 else
3195 .alloc_inferred;
3196 break :ptr try gz.addNode(tag, node);
3197 } else ptr: {
3198 break :ptr try gz.addAllocExtended(.{
3199 .node = node,
3200 .type_inst = .none,
3201 .align_inst = align_inst,
3202 .is_const = true,
3203 .is_comptime = is_comptime,
3204 });
3205 };
3206 resolve_inferred_alloc = alloc_inst;
3207 break :init_rl .{ .inferred_ptr = alloc_inst };
3208 };
3209 const var_ptr = switch (init_rl) {
3210 .ptr => |ptr| ptr.inst,
3211 .inferred_ptr => |inst| inst,
3212 else => unreachable,
3213 };
3214 const init_result_info: ResultInfo = .{ .rl = init_rl, .ctx = .const_init };
3215
3216 const prev_anon_name_strategy = gz.anon_name_strategy;
3217 gz.anon_name_strategy = .dbg_var;
3218 defer gz.anon_name_strategy = prev_anon_name_strategy;
3219 const init_inst = try reachableExpr(gz, scope, init_result_info, var_decl.ast.init_node, node);
3220
3221 // The const init expression may have modified the error return trace, so signal
3222 // to Sema that it should save the new index for restoring later.
3223 if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node))
3224 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
3225
3226 const const_ptr = if (resolve_inferred_alloc != .none) p: {
3227 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
3228 break :p var_ptr;
3229 } else try gz.addUnNode(.make_ptr_const, var_ptr, node);
3230
3231 try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr);
3232
3233 const sub_scope = try block_arena.create(Scope.LocalPtr);
3234 sub_scope.* = .{
3235 .parent = scope,
3236 .gen_zir = gz,
3237 .name = ident_name,
3238 .ptr = const_ptr,
3239 .token_src = name_token,
3240 .maybe_comptime = true,
3241 .id_cat = .@"local constant",
3242 };
3243 return &sub_scope.base;
3244 },
3245 .keyword_var => {
3246 if (var_decl.comptime_token != null and gz.is_comptime)
3247 return astgen.failTok(var_decl.comptime_token.?, "'comptime var' is redundant in comptime scope", .{});
3248 const is_comptime = var_decl.comptime_token != null or gz.is_comptime;
3249 var resolve_inferred_alloc: Zir.Inst.Ref = .none;
3250 const alloc: Zir.Inst.Ref, const result_info: ResultInfo = if (var_decl.ast.type_node != 0) a: {
3251 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
3252 const alloc = alloc: {
3253 if (align_inst == .none) {
3254 const tag: Zir.Inst.Tag = if (is_comptime)
3255 .alloc_comptime_mut
3256 else
3257 .alloc_mut;
3258 break :alloc try gz.addUnNode(tag, type_inst, node);
3259 } else {
3260 break :alloc try gz.addAllocExtended(.{
3261 .node = node,
3262 .type_inst = type_inst,
3263 .align_inst = align_inst,
3264 .is_const = false,
3265 .is_comptime = is_comptime,
3266 });
3267 }
3268 };
3269 break :a .{ alloc, .{ .rl = .{ .ptr = .{ .inst = alloc } } } };
3270 } else a: {
3271 const alloc = alloc: {
3272 if (align_inst == .none) {
3273 const tag: Zir.Inst.Tag = if (is_comptime)
3274 .alloc_inferred_comptime_mut
3275 else
3276 .alloc_inferred_mut;
3277 break :alloc try gz.addNode(tag, node);
3278 } else {
3279 break :alloc try gz.addAllocExtended(.{
3280 .node = node,
3281 .type_inst = .none,
3282 .align_inst = align_inst,
3283 .is_const = false,
3284 .is_comptime = is_comptime,
3285 });
3286 }
3287 };
3288 resolve_inferred_alloc = alloc;
3289 break :a .{ alloc, .{ .rl = .{ .inferred_ptr = alloc } } };
3290 };
3291 const prev_anon_name_strategy = gz.anon_name_strategy;
3292 gz.anon_name_strategy = .dbg_var;
3293 _ = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, is_comptime);
3294 gz.anon_name_strategy = prev_anon_name_strategy;
3295 if (resolve_inferred_alloc != .none) {
3296 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
3297 }
3298
3299 try gz.addDbgVar(.dbg_var_ptr, ident_name, alloc);
3300
3301 const sub_scope = try block_arena.create(Scope.LocalPtr);
3302 sub_scope.* = .{
3303 .parent = scope,
3304 .gen_zir = gz,
3305 .name = ident_name,
3306 .ptr = alloc,
3307 .token_src = name_token,
3308 .maybe_comptime = is_comptime,
3309 .id_cat = .@"local variable",
3310 };
3311 return &sub_scope.base;
3312 },
3313 else => unreachable,
3314 }
3315}
3316
3317fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void {
3318 // The instruction emitted here is for debugging runtime code.
3319 // If the current block will be evaluated only during semantic analysis
3320 // then no dbg_stmt ZIR instruction is needed.
3321 if (gz.is_comptime) return;
3322 const astgen = gz.astgen;
3323 astgen.advanceSourceCursorToNode(node);
3324 const line = astgen.source_line - gz.decl_line;
3325 const column = astgen.source_column;
3326 try emitDbgStmt(gz, .{ line, column });
3327}
3328
3329fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
3330 try emitDbgNode(gz, infix_node);
3331 const astgen = gz.astgen;
3332 const tree = astgen.tree;
3333 const node_datas = tree.nodes.items(.data);
3334 const main_tokens = tree.nodes.items(.main_token);
3335 const node_tags = tree.nodes.items(.tag);
3336
3337 const lhs = node_datas[infix_node].lhs;
3338 const rhs = node_datas[infix_node].rhs;
3339 if (node_tags[lhs] == .identifier) {
3340 // This intentionally does not support `@"_"` syntax.
3341 const ident_name = tree.tokenSlice(main_tokens[lhs]);
3342 if (mem.eql(u8, ident_name, "_")) {
3343 _ = try expr(gz, scope, .{ .rl = .discard, .ctx = .assignment }, rhs);
3344 return;
3345 }
3346 }
3347 const lvalue = try lvalExpr(gz, scope, lhs);
3348 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{
3349 .inst = lvalue,
3350 .src_node = infix_node,
3351 } } }, rhs);
3352}
3353
3354/// Handles destructure assignments where no LHS is a `const` or `var` decl.
3355fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!void {
3356 try emitDbgNode(gz, node);
3357 const astgen = gz.astgen;
3358 const tree = astgen.tree;
3359 const token_tags = tree.tokens.items(.tag);
3360 const node_datas = tree.nodes.items(.data);
3361 const main_tokens = tree.nodes.items(.main_token);
3362 const node_tags = tree.nodes.items(.tag);
3363
3364 const extra_index = node_datas[node].lhs;
3365 const lhs_count = tree.extra_data[extra_index];
3366 const lhs_nodes: []const Ast.Node.Index = @ptrCast(tree.extra_data[extra_index + 1 ..][0..lhs_count]);
3367 const rhs = node_datas[node].rhs;
3368
3369 const maybe_comptime_token = tree.firstToken(node) - 1;
3370 const declared_comptime = token_tags[maybe_comptime_token] == .keyword_comptime;
3371
3372 if (declared_comptime and gz.is_comptime) {
3373 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
3374 }
3375
3376 // If this expression is marked comptime, we must wrap the whole thing in a comptime block.
3377 var gz_buf: GenZir = undefined;
3378 const inner_gz = if (declared_comptime) bs: {
3379 gz_buf = gz.makeSubBlock(scope);
3380 gz_buf.is_comptime = true;
3381 break :bs &gz_buf;
3382 } else gz;
3383 defer if (declared_comptime) inner_gz.unstack();
3384
3385 const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, lhs_nodes.len);
3386 for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| {
3387 if (node_tags[lhs_node] == .identifier) {
3388 // This intentionally does not support `@"_"` syntax.
3389 const ident_name = tree.tokenSlice(main_tokens[lhs_node]);
3390 if (mem.eql(u8, ident_name, "_")) {
3391 lhs_rl.* = .discard;
3392 continue;
3393 }
3394 }
3395 lhs_rl.* = .{ .typed_ptr = .{
3396 .inst = try lvalExpr(inner_gz, scope, lhs_node),
3397 .src_node = lhs_node,
3398 } };
3399 }
3400
3401 const ri: ResultInfo = .{ .rl = .{ .destructure = .{
3402 .src_node = node,
3403 .components = rl_components,
3404 } } };
3405
3406 _ = try expr(inner_gz, scope, ri, rhs);
3407
3408 if (declared_comptime) {
3409 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
3410 _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value);
3411 try inner_gz.setBlockBody(comptime_block_inst);
3412 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);
3413 }
3414}
3415
3416/// Handles destructure assignments where the LHS may contain `const` or `var` decls.
3417fn assignDestructureMaybeDecls(
3418 gz: *GenZir,
3419 scope: *Scope,
3420 node: Ast.Node.Index,
3421 block_arena: Allocator,
3422) InnerError!*Scope {
3423 try emitDbgNode(gz, node);
3424 const astgen = gz.astgen;
3425 const tree = astgen.tree;
3426 const token_tags = tree.tokens.items(.tag);
3427 const node_datas = tree.nodes.items(.data);
3428 const main_tokens = tree.nodes.items(.main_token);
3429 const node_tags = tree.nodes.items(.tag);
3430
3431 const extra_index = node_datas[node].lhs;
3432 const lhs_count = tree.extra_data[extra_index];
3433 const lhs_nodes: []const Ast.Node.Index = @ptrCast(tree.extra_data[extra_index + 1 ..][0..lhs_count]);
3434 const rhs = node_datas[node].rhs;
3435
3436 const maybe_comptime_token = tree.firstToken(node) - 1;
3437 const declared_comptime = token_tags[maybe_comptime_token] == .keyword_comptime;
3438 if (declared_comptime and gz.is_comptime) {
3439 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
3440 }
3441
3442 const is_comptime = declared_comptime or gz.is_comptime;
3443 const rhs_is_comptime = tree.nodes.items(.tag)[rhs] == .@"comptime";
3444
3445 // When declaring consts via a destructure, we always use a result pointer.
3446 // This avoids the need to create tuple types, and is also likely easier to
3447 // optimize, since it's a bit tricky for the optimizer to "split up" the
3448 // value into individual pointer writes down the line.
3449
3450 // We know this rl information won't live past the evaluation of this
3451 // expression, so it may as well go in the block arena.
3452 const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, lhs_nodes.len);
3453 var any_non_const_lhs = false;
3454 var any_lvalue_expr = false;
3455 for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| {
3456 switch (node_tags[lhs_node]) {
3457 .identifier => {
3458 // This intentionally does not support `@"_"` syntax.
3459 const ident_name = tree.tokenSlice(main_tokens[lhs_node]);
3460 if (mem.eql(u8, ident_name, "_")) {
3461 any_non_const_lhs = true;
3462 lhs_rl.* = .discard;
3463 continue;
3464 }
3465 },
3466 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => {
3467 const full = tree.fullVarDecl(lhs_node).?;
3468
3469 const name_token = full.ast.mut_token + 1;
3470 const ident_name_raw = tree.tokenSlice(name_token);
3471 if (mem.eql(u8, ident_name_raw, "_")) {
3472 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
3473 }
3474
3475 // We detect shadowing in the second pass over these, while we're creating scopes.
3476
3477 if (full.ast.addrspace_node != 0) {
3478 return astgen.failTok(main_tokens[full.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
3479 }
3480 if (full.ast.section_node != 0) {
3481 return astgen.failTok(main_tokens[full.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
3482 }
3483
3484 const is_const = switch (token_tags[full.ast.mut_token]) {
3485 .keyword_var => false,
3486 .keyword_const => true,
3487 else => unreachable,
3488 };
3489 if (!is_const) any_non_const_lhs = true;
3490
3491 // We also mark `const`s as comptime if the RHS is definitely comptime-known.
3492 const this_lhs_comptime = is_comptime or (is_const and rhs_is_comptime);
3493
3494 const align_inst: Zir.Inst.Ref = if (full.ast.align_node != 0)
3495 try expr(gz, scope, coerced_align_ri, full.ast.align_node)
3496 else
3497 .none;
3498
3499 if (full.ast.type_node != 0) {
3500 // Typed alloc
3501 const type_inst = try typeExpr(gz, scope, full.ast.type_node);
3502 const ptr = if (align_inst == .none) ptr: {
3503 const tag: Zir.Inst.Tag = if (is_const)
3504 .alloc
3505 else if (this_lhs_comptime)
3506 .alloc_comptime_mut
3507 else
3508 .alloc_mut;
3509 break :ptr try gz.addUnNode(tag, type_inst, node);
3510 } else try gz.addAllocExtended(.{
3511 .node = node,
3512 .type_inst = type_inst,
3513 .align_inst = align_inst,
3514 .is_const = is_const,
3515 .is_comptime = this_lhs_comptime,
3516 });
3517 lhs_rl.* = .{ .typed_ptr = .{ .inst = ptr } };
3518 } else {
3519 // Inferred alloc
3520 const ptr = if (align_inst == .none) ptr: {
3521 const tag: Zir.Inst.Tag = if (is_const) tag: {
3522 break :tag if (this_lhs_comptime) .alloc_inferred_comptime else .alloc_inferred;
3523 } else tag: {
3524 break :tag if (this_lhs_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut;
3525 };
3526 break :ptr try gz.addNode(tag, node);
3527 } else try gz.addAllocExtended(.{
3528 .node = node,
3529 .type_inst = .none,
3530 .align_inst = align_inst,
3531 .is_const = is_const,
3532 .is_comptime = this_lhs_comptime,
3533 });
3534 lhs_rl.* = .{ .inferred_ptr = ptr };
3535 }
3536
3537 continue;
3538 },
3539 else => {},
3540 }
3541 // This LHS is just an lvalue expression.
3542 // We will fill in its result pointer later, inside a comptime block.
3543 any_non_const_lhs = true;
3544 any_lvalue_expr = true;
3545 lhs_rl.* = .{ .typed_ptr = .{
3546 .inst = undefined,
3547 .src_node = lhs_node,
3548 } };
3549 }
3550
3551 if (declared_comptime and !any_non_const_lhs) {
3552 try astgen.appendErrorTok(maybe_comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
3553 }
3554
3555 // If this expression is marked comptime, we must wrap it in a comptime block.
3556 var gz_buf: GenZir = undefined;
3557 const inner_gz = if (declared_comptime) bs: {
3558 gz_buf = gz.makeSubBlock(scope);
3559 gz_buf.is_comptime = true;
3560 break :bs &gz_buf;
3561 } else gz;
3562 defer if (declared_comptime) inner_gz.unstack();
3563
3564 if (any_lvalue_expr) {
3565 // At least one LHS was an lvalue expr. Iterate again in order to
3566 // evaluate the lvalues from within the possible block_comptime.
3567 for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| {
3568 if (lhs_rl.* != .typed_ptr) continue;
3569 switch (node_tags[lhs_node]) {
3570 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue,
3571 else => {},
3572 }
3573 lhs_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, lhs_node);
3574 }
3575 }
3576
3577 // We can't give a reasonable anon name strategy for destructured inits, so
3578 // leave it at its default of `.anon`.
3579 _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{
3580 .src_node = node,
3581 .components = rl_components,
3582 } } }, rhs, node);
3583
3584 if (declared_comptime) {
3585 // Finish the block_comptime. Inferred alloc resolution etc will occur
3586 // in the parent block.
3587 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
3588 _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value);
3589 try inner_gz.setBlockBody(comptime_block_inst);
3590 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);
3591 }
3592
3593 // Now, iterate over the LHS exprs to construct any new scopes.
3594 // If there were any inferred allocations, resolve them.
3595 // If there were any `const` decls, make the pointer constant.
3596 var cur_scope = scope;
3597 for (rl_components, lhs_nodes) |lhs_rl, lhs_node| {
3598 switch (node_tags[lhs_node]) {
3599 .local_var_decl, .simple_var_decl, .aligned_var_decl => {},
3600 else => continue, // We were mutating an existing lvalue - nothing to do
3601 }
3602 const full = tree.fullVarDecl(lhs_node).?;
3603 const raw_ptr = switch (lhs_rl) {
3604 .discard => unreachable,
3605 .typed_ptr => |typed_ptr| typed_ptr.inst,
3606 .inferred_ptr => |ptr_inst| ptr_inst,
3607 };
3608 // If the alloc was inferred, resolve it.
3609 if (full.ast.type_node == 0) {
3610 _ = try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, lhs_node);
3611 }
3612 const is_const = switch (token_tags[full.ast.mut_token]) {
3613 .keyword_var => false,
3614 .keyword_const => true,
3615 else => unreachable,
3616 };
3617 // If the alloc was const, make it const.
3618 const var_ptr = if (is_const and full.ast.type_node != 0) make_const: {
3619 // Note that we don't do this if type_node == 0 since `resolve_inferred_alloc`
3620 // handles it for us.
3621 break :make_const try gz.addUnNode(.make_ptr_const, raw_ptr, node);
3622 } else raw_ptr;
3623 const name_token = full.ast.mut_token + 1;
3624 const ident_name_raw = tree.tokenSlice(name_token);
3625 const ident_name = try astgen.identAsString(name_token);
3626 try astgen.detectLocalShadowing(
3627 cur_scope,
3628 ident_name,
3629 name_token,
3630 ident_name_raw,
3631 if (is_const) .@"local constant" else .@"local variable",
3632 );
3633 try gz.addDbgVar(.dbg_var_ptr, ident_name, var_ptr);
3634 // Finally, create the scope.
3635 const sub_scope = try block_arena.create(Scope.LocalPtr);
3636 sub_scope.* = .{
3637 .parent = cur_scope,
3638 .gen_zir = gz,
3639 .name = ident_name,
3640 .ptr = var_ptr,
3641 .token_src = name_token,
3642 .maybe_comptime = is_const or is_comptime,
3643 .id_cat = if (is_const) .@"local constant" else .@"local variable",
3644 };
3645 cur_scope = &sub_scope.base;
3646 }
3647
3648 return cur_scope;
3649}
3650
3651fn assignOp(
3652 gz: *GenZir,
3653 scope: *Scope,
3654 infix_node: Ast.Node.Index,
3655 op_inst_tag: Zir.Inst.Tag,
3656) InnerError!void {
3657 try emitDbgNode(gz, infix_node);
3658 const astgen = gz.astgen;
3659 const tree = astgen.tree;
3660 const node_datas = tree.nodes.items(.data);
3661
3662 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
3663
3664 const cursor = switch (op_inst_tag) {
3665 .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, infix_node),
3666 else => undefined,
3667 };
3668 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
3669 const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
3670 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = lhs_type } }, node_datas[infix_node].rhs);
3671
3672 switch (op_inst_tag) {
3673 .add, .sub, .mul, .div, .mod_rem => {
3674 try emitDbgStmt(gz, cursor);
3675 },
3676 else => {},
3677 }
3678 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
3679 .lhs = lhs,
3680 .rhs = rhs,
3681 });
3682 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
3683 .lhs = lhs_ptr,
3684 .rhs = result,
3685 });
3686}
3687
3688fn assignShift(
3689 gz: *GenZir,
3690 scope: *Scope,
3691 infix_node: Ast.Node.Index,
3692 op_inst_tag: Zir.Inst.Tag,
3693) InnerError!void {
3694 try emitDbgNode(gz, infix_node);
3695 const astgen = gz.astgen;
3696 const tree = astgen.tree;
3697 const node_datas = tree.nodes.items(.data);
3698
3699 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
3700 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
3701 const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node);
3702 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_type } }, node_datas[infix_node].rhs);
3703
3704 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
3705 .lhs = lhs,
3706 .rhs = rhs,
3707 });
3708 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
3709 .lhs = lhs_ptr,
3710 .rhs = result,
3711 });
3712}
3713
3714fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
3715 try emitDbgNode(gz, infix_node);
3716 const astgen = gz.astgen;
3717 const tree = astgen.tree;
3718 const node_datas = tree.nodes.items(.data);
3719
3720 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
3721 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
3722 // Saturating shift-left allows any integer type for both the LHS and RHS.
3723 const rhs = try expr(gz, scope, .{ .rl = .none }, node_datas[infix_node].rhs);
3724
3725 const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{
3726 .lhs = lhs,
3727 .rhs = rhs,
3728 });
3729 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
3730 .lhs = lhs_ptr,
3731 .rhs = result,
3732 });
3733}
3734
3735fn ptrType(
3736 gz: *GenZir,
3737 scope: *Scope,
3738 ri: ResultInfo,
3739 node: Ast.Node.Index,
3740 ptr_info: Ast.full.PtrType,
3741) InnerError!Zir.Inst.Ref {
3742 if (ptr_info.size == .C and ptr_info.allowzero_token != null) {
3743 return gz.astgen.failTok(ptr_info.allowzero_token.?, "C pointers always allow address zero", .{});
3744 }
3745
3746 const source_offset = gz.astgen.source_offset;
3747 const source_line = gz.astgen.source_line;
3748 const source_column = gz.astgen.source_column;
3749 const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
3750
3751 var sentinel_ref: Zir.Inst.Ref = .none;
3752 var align_ref: Zir.Inst.Ref = .none;
3753 var addrspace_ref: Zir.Inst.Ref = .none;
3754 var bit_start_ref: Zir.Inst.Ref = .none;
3755 var bit_end_ref: Zir.Inst.Ref = .none;
3756 var trailing_count: u32 = 0;
3757
3758 if (ptr_info.ast.sentinel != 0) {
3759 // These attributes can appear in any order and they all come before the
3760 // element type so we need to reset the source cursor before generating them.
3761 gz.astgen.source_offset = source_offset;
3762 gz.astgen.source_line = source_line;
3763 gz.astgen.source_column = source_column;
3764
3765 sentinel_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, ptr_info.ast.sentinel);
3766 trailing_count += 1;
3767 }
3768 if (ptr_info.ast.addrspace_node != 0) {
3769 gz.astgen.source_offset = source_offset;
3770 gz.astgen.source_line = source_line;
3771 gz.astgen.source_column = source_column;
3772
3773 addrspace_ref = try expr(gz, scope, coerced_addrspace_ri, ptr_info.ast.addrspace_node);
3774 trailing_count += 1;
3775 }
3776 if (ptr_info.ast.align_node != 0) {
3777 gz.astgen.source_offset = source_offset;
3778 gz.astgen.source_line = source_line;
3779 gz.astgen.source_column = source_column;
3780
3781 align_ref = try expr(gz, scope, coerced_align_ri, ptr_info.ast.align_node);
3782 trailing_count += 1;
3783 }
3784 if (ptr_info.ast.bit_range_start != 0) {
3785 assert(ptr_info.ast.bit_range_end != 0);
3786 bit_start_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_start);
3787 bit_end_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_end);
3788 trailing_count += 2;
3789 }
3790
3791 const gpa = gz.astgen.gpa;
3792 try gz.instructions.ensureUnusedCapacity(gpa, 1);
3793 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
3794 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).Struct.fields.len +
3795 trailing_count);
3796
3797 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{
3798 .elem_type = elem_type,
3799 .src_node = gz.nodeIndexToRelative(node),
3800 });
3801 if (sentinel_ref != .none) {
3802 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(sentinel_ref));
3803 }
3804 if (align_ref != .none) {
3805 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(align_ref));
3806 }
3807 if (addrspace_ref != .none) {
3808 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(addrspace_ref));
3809 }
3810 if (bit_start_ref != .none) {
3811 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_start_ref));
3812 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_end_ref));
3813 }
3814
3815 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
3816 const result = new_index.toRef();
3817 gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
3818 .ptr_type = .{
3819 .flags = .{
3820 .is_allowzero = ptr_info.allowzero_token != null,
3821 .is_mutable = ptr_info.const_token == null,
3822 .is_volatile = ptr_info.volatile_token != null,
3823 .has_sentinel = sentinel_ref != .none,
3824 .has_align = align_ref != .none,
3825 .has_addrspace = addrspace_ref != .none,
3826 .has_bit_range = bit_start_ref != .none,
3827 },
3828 .size = ptr_info.size,
3829 .payload_index = payload_index,
3830 },
3831 } });
3832 gz.instructions.appendAssumeCapacity(new_index);
3833
3834 return rvalue(gz, ri, result, node);
3835}
3836
3837fn arrayType(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref {
3838 const astgen = gz.astgen;
3839 const tree = astgen.tree;
3840 const node_datas = tree.nodes.items(.data);
3841 const node_tags = tree.nodes.items(.tag);
3842 const main_tokens = tree.nodes.items(.main_token);
3843
3844 const len_node = node_datas[node].lhs;
3845 if (node_tags[len_node] == .identifier and
3846 mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
3847 {
3848 return astgen.failNode(len_node, "unable to infer array size", .{});
3849 }
3850 const len = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node);
3851 const elem_type = try typeExpr(gz, scope, node_datas[node].rhs);
3852
3853 const result = try gz.addPlNode(.array_type, node, Zir.Inst.Bin{
3854 .lhs = len,
3855 .rhs = elem_type,
3856 });
3857 return rvalue(gz, ri, result, node);
3858}
3859
3860fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref {
3861 const astgen = gz.astgen;
3862 const tree = astgen.tree;
3863 const node_datas = tree.nodes.items(.data);
3864 const node_tags = tree.nodes.items(.tag);
3865 const main_tokens = tree.nodes.items(.main_token);
3866 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel);
3867
3868 const len_node = node_datas[node].lhs;
3869 if (node_tags[len_node] == .identifier and
3870 mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
3871 {
3872 return astgen.failNode(len_node, "unable to infer array size", .{});
3873 }
3874 const len = try reachableExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node);
3875 const elem_type = try typeExpr(gz, scope, extra.elem_type);
3876 const sentinel = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = elem_type } }, extra.sentinel, node, true);
3877
3878 const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{
3879 .len = len,
3880 .elem_type = elem_type,
3881 .sentinel = sentinel,
3882 });
3883 return rvalue(gz, ri, result, node);
3884}
3885
3886const WipMembers = struct {
3887 payload: *ArrayListUnmanaged(u32),
3888 payload_top: usize,
3889 field_bits_start: u32,
3890 fields_start: u32,
3891 fields_end: u32,
3892 decl_index: u32 = 0,
3893 field_index: u32 = 0,
3894
3895 const Self = @This();
3896
3897 fn init(gpa: Allocator, payload: *ArrayListUnmanaged(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self {
3898 const payload_top: u32 = @intCast(payload.items.len);
3899 const field_bits_start = payload_top + decl_count;
3900 const fields_start = field_bits_start + if (bits_per_field > 0) blk: {
3901 const fields_per_u32 = 32 / bits_per_field;
3902 break :blk (field_count + fields_per_u32 - 1) / fields_per_u32;
3903 } else 0;
3904 const payload_end = fields_start + field_count * max_field_size;
3905 try payload.resize(gpa, payload_end);
3906 return .{
3907 .payload = payload,
3908 .payload_top = payload_top,
3909 .field_bits_start = field_bits_start,
3910 .fields_start = fields_start,
3911 .fields_end = fields_start,
3912 };
3913 }
3914
3915 fn nextDecl(self: *Self, decl_inst: Zir.Inst.Index) void {
3916 self.payload.items[self.payload_top + self.decl_index] = @intFromEnum(decl_inst);
3917 self.decl_index += 1;
3918 }
3919
3920 fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void {
3921 const fields_per_u32 = 32 / bits_per_field;
3922 const index = self.field_bits_start + self.field_index / fields_per_u32;
3923 assert(index < self.fields_start);
3924 var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload.items[index];
3925 bit_bag >>= bits_per_field;
3926 comptime var i = 0;
3927 inline while (i < bits_per_field) : (i += 1) {
3928 bit_bag |= @as(u32, @intFromBool(bits[i])) << (32 - bits_per_field + i);
3929 }
3930 self.payload.items[index] = bit_bag;
3931 self.field_index += 1;
3932 }
3933
3934 fn appendToField(self: *Self, data: u32) void {
3935 assert(self.fields_end < self.payload.items.len);
3936 self.payload.items[self.fields_end] = data;
3937 self.fields_end += 1;
3938 }
3939
3940 fn finishBits(self: *Self, comptime bits_per_field: u32) void {
3941 if (bits_per_field > 0) {
3942 const fields_per_u32 = 32 / bits_per_field;
3943 const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32);
3944 if (self.field_index > 0 and empty_field_slots < fields_per_u32) {
3945 const index = self.field_bits_start + self.field_index / fields_per_u32;
3946 self.payload.items[index] >>= @intCast(empty_field_slots * bits_per_field);
3947 }
3948 }
3949 }
3950
3951 fn declsSlice(self: *Self) []u32 {
3952 return self.payload.items[self.payload_top..][0..self.decl_index];
3953 }
3954
3955 fn fieldsSlice(self: *Self) []u32 {
3956 return self.payload.items[self.field_bits_start..self.fields_end];
3957 }
3958
3959 fn deinit(self: *Self) void {
3960 self.payload.items.len = self.payload_top;
3961 }
3962};
3963
3964fn fnDecl(
3965 astgen: *AstGen,
3966 gz: *GenZir,
3967 scope: *Scope,
3968 wip_members: *WipMembers,
3969 decl_node: Ast.Node.Index,
3970 body_node: Ast.Node.Index,
3971 fn_proto: Ast.full.FnProto,
3972) InnerError!void {
3973 const tree = astgen.tree;
3974 const token_tags = tree.tokens.items(.tag);
3975
3976 // missing function name already happened in scanDecls()
3977 const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;
3978
3979 // We insert this at the beginning so that its instruction index marks the
3980 // start of the top level declaration.
3981 const decl_inst = try gz.makeBlockInst(.declaration, fn_proto.ast.proto_node);
3982 astgen.advanceSourceCursorToNode(decl_node);
3983
3984 var decl_gz: GenZir = .{
3985 .is_comptime = true,
3986 .decl_node_index = fn_proto.ast.proto_node,
3987 .decl_line = astgen.source_line,
3988 .parent = scope,
3989 .astgen = astgen,
3990 .instructions = gz.instructions,
3991 .instructions_top = gz.instructions.items.len,
3992 };
3993 defer decl_gz.unstack();
3994
3995 var fn_gz: GenZir = .{
3996 .is_comptime = false,
3997 .decl_node_index = fn_proto.ast.proto_node,
3998 .decl_line = decl_gz.decl_line,
3999 .parent = &decl_gz.base,
4000 .astgen = astgen,
4001 .instructions = gz.instructions,
4002 .instructions_top = GenZir.unstacked_top,
4003 };
4004 defer fn_gz.unstack();
4005
4006 const is_pub = fn_proto.visib_token != null;
4007 const is_export = blk: {
4008 const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;
4009 break :blk token_tags[maybe_export_token] == .keyword_export;
4010 };
4011 const is_extern = blk: {
4012 const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
4013 break :blk token_tags[maybe_extern_token] == .keyword_extern;
4014 };
4015 const has_inline_keyword = blk: {
4016 const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
4017 break :blk token_tags[maybe_inline_token] == .keyword_inline;
4018 };
4019 const is_noinline = blk: {
4020 const maybe_noinline_token = fn_proto.extern_export_inline_token orelse break :blk false;
4021 break :blk token_tags[maybe_noinline_token] == .keyword_noinline;
4022 };
4023
4024 const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken());
4025
4026 wip_members.nextDecl(decl_inst);
4027
4028 var noalias_bits: u32 = 0;
4029 var params_scope = &fn_gz.base;
4030 const is_var_args = is_var_args: {
4031 var param_type_i: usize = 0;
4032 var it = fn_proto.iterate(tree);
4033 while (it.next()) |param| : (param_type_i += 1) {
4034 const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
4035 .keyword_noalias => is_comptime: {
4036 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
4037 return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
4038 break :is_comptime false;
4039 },
4040 .keyword_comptime => true,
4041 else => false,
4042 } else false;
4043
4044 const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
4045 switch (token_tags[token]) {
4046 .keyword_anytype => break :blk true,
4047 .ellipsis3 => break :is_var_args true,
4048 else => unreachable,
4049 }
4050 } else false;
4051
4052 const param_name: Zir.NullTerminatedString = if (param.name_token) |name_token| blk: {
4053 const name_bytes = tree.tokenSlice(name_token);
4054 if (mem.eql(u8, "_", name_bytes))
4055 break :blk .empty;
4056
4057 const param_name = try astgen.identAsString(name_token);
4058 if (!is_extern) {
4059 try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter");
4060 }
4061 break :blk param_name;
4062 } else if (!is_extern) {
4063 if (param.anytype_ellipsis3) |tok| {
4064 return astgen.failTok(tok, "missing parameter name", .{});
4065 } else {
4066 ambiguous: {
4067 if (tree.nodes.items(.tag)[param.type_expr] != .identifier) break :ambiguous;
4068 const main_token = tree.nodes.items(.main_token)[param.type_expr];
4069 const identifier_str = tree.tokenSlice(main_token);
4070 if (isPrimitive(identifier_str)) break :ambiguous;
4071 return astgen.failNodeNotes(
4072 param.type_expr,
4073 "missing parameter name or type",
4074 .{},
4075 &[_]u32{
4076 try astgen.errNoteNode(
4077 param.type_expr,
4078 "if this is a name, annotate its type '{s}: T'",
4079 .{identifier_str},
4080 ),
4081 try astgen.errNoteNode(
4082 param.type_expr,
4083 "if this is a type, give it a name '<name>: {s}'",
4084 .{identifier_str},
4085 ),
4086 },
4087 );
4088 }
4089 return astgen.failNode(param.type_expr, "missing parameter name", .{});
4090 }
4091 } else .empty;
4092
4093 const param_inst = if (is_anytype) param: {
4094 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
4095 const tag: Zir.Inst.Tag = if (is_comptime)
4096 .param_anytype_comptime
4097 else
4098 .param_anytype;
4099 break :param try decl_gz.addStrTok(tag, param_name, name_token);
4100 } else param: {
4101 const param_type_node = param.type_expr;
4102 assert(param_type_node != 0);
4103 var param_gz = decl_gz.makeSubBlock(scope);
4104 defer param_gz.unstack();
4105 const param_type = try expr(&param_gz, params_scope, coerced_type_ri, param_type_node);
4106 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
4107 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
4108
4109 const main_tokens = tree.nodes.items(.main_token);
4110 const name_token = param.name_token orelse main_tokens[param_type_node];
4111 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
4112 const param_inst = try decl_gz.addParam(&param_gz, tag, name_token, param_name, param.first_doc_comment);
4113 assert(param_inst_expected == param_inst);
4114 break :param param_inst.toRef();
4115 };
4116
4117 if (param_name == .empty or is_extern) continue;
4118
4119 const sub_scope = try astgen.arena.create(Scope.LocalVal);
4120 sub_scope.* = .{
4121 .parent = params_scope,
4122 .gen_zir = &decl_gz,
4123 .name = param_name,
4124 .inst = param_inst,
4125 .token_src = param.name_token.?,
4126 .id_cat = .@"function parameter",
4127 };
4128 params_scope = &sub_scope.base;
4129 }
4130 break :is_var_args false;
4131 };
4132
4133 const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: {
4134 const lib_name_str = try astgen.strLitAsString(lib_name_token);
4135 const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
4136 if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
4137 return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
4138 } else if (lib_name_str.len == 0) {
4139 return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
4140 }
4141 break :blk lib_name_str.index;
4142 } else .empty;
4143
4144 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
4145 const is_inferred_error = token_tags[maybe_bang] == .bang;
4146
4147 // After creating the function ZIR instruction, it will need to update the break
4148 // instructions inside the expression blocks for align, addrspace, cc, and ret_ty
4149 // to use the function instruction as the "block" to break from.
4150
4151 var align_gz = decl_gz.makeSubBlock(params_scope);
4152 defer align_gz.unstack();
4153 const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
4154 const inst = try expr(&decl_gz, params_scope, coerced_align_ri, fn_proto.ast.align_expr);
4155 if (align_gz.instructionsSlice().len == 0) {
4156 // In this case we will send a len=0 body which can be encoded more efficiently.
4157 break :inst inst;
4158 }
4159 _ = try align_gz.addBreak(.break_inline, @enumFromInt(0), inst);
4160 break :inst inst;
4161 };
4162
4163 var addrspace_gz = decl_gz.makeSubBlock(params_scope);
4164 defer addrspace_gz.unstack();
4165 const addrspace_ref: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: {
4166 const inst = try expr(&decl_gz, params_scope, coerced_addrspace_ri, fn_proto.ast.addrspace_expr);
4167 if (addrspace_gz.instructionsSlice().len == 0) {
4168 // In this case we will send a len=0 body which can be encoded more efficiently.
4169 break :inst inst;
4170 }
4171 _ = try addrspace_gz.addBreak(.break_inline, @enumFromInt(0), inst);
4172 break :inst inst;
4173 };
4174
4175 var section_gz = decl_gz.makeSubBlock(params_scope);
4176 defer section_gz.unstack();
4177 const section_ref: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
4178 const inst = try expr(&decl_gz, params_scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, fn_proto.ast.section_expr);
4179 if (section_gz.instructionsSlice().len == 0) {
4180 // In this case we will send a len=0 body which can be encoded more efficiently.
4181 break :inst inst;
4182 }
4183 _ = try section_gz.addBreak(.break_inline, @enumFromInt(0), inst);
4184 break :inst inst;
4185 };
4186
4187 var cc_gz = decl_gz.makeSubBlock(params_scope);
4188 defer cc_gz.unstack();
4189 const cc_ref: Zir.Inst.Ref = blk: {
4190 if (fn_proto.ast.callconv_expr != 0) {
4191 if (has_inline_keyword) {
4192 return astgen.failNode(
4193 fn_proto.ast.callconv_expr,
4194 "explicit callconv incompatible with inline keyword",
4195 .{},
4196 );
4197 }
4198 const inst = try expr(
4199 &decl_gz,
4200 params_scope,
4201 .{ .rl = .{ .coerced_ty = .calling_convention_type } },
4202 fn_proto.ast.callconv_expr,
4203 );
4204 if (cc_gz.instructionsSlice().len == 0) {
4205 // In this case we will send a len=0 body which can be encoded more efficiently.
4206 break :blk inst;
4207 }
4208 _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
4209 break :blk inst;
4210 } else if (is_extern) {
4211 // note: https://github.com/ziglang/zig/issues/5269
4212 break :blk .calling_convention_c;
4213 } else if (has_inline_keyword) {
4214 break :blk .calling_convention_inline;
4215 } else {
4216 break :blk .none;
4217 }
4218 };
4219
4220 var ret_gz = decl_gz.makeSubBlock(params_scope);
4221 defer ret_gz.unstack();
4222 const ret_ref: Zir.Inst.Ref = inst: {
4223 const inst = try expr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type);
4224 if (ret_gz.instructionsSlice().len == 0) {
4225 // In this case we will send a len=0 body which can be encoded more efficiently.
4226 break :inst inst;
4227 }
4228 _ = try ret_gz.addBreak(.break_inline, @enumFromInt(0), inst);
4229 break :inst inst;
4230 };
4231
4232 const func_inst: Zir.Inst.Ref = if (body_node == 0) func: {
4233 if (!is_extern) {
4234 return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{});
4235 }
4236 if (is_inferred_error) {
4237 return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
4238 }
4239 break :func try decl_gz.addFunc(.{
4240 .src_node = decl_node,
4241 .cc_ref = cc_ref,
4242 .cc_gz = &cc_gz,
4243 .align_ref = align_ref,
4244 .align_gz = &align_gz,
4245 .ret_ref = ret_ref,
4246 .ret_gz = &ret_gz,
4247 .section_ref = section_ref,
4248 .section_gz = &section_gz,
4249 .addrspace_ref = addrspace_ref,
4250 .addrspace_gz = &addrspace_gz,
4251 .param_block = decl_inst,
4252 .body_gz = null,
4253 .lib_name = lib_name,
4254 .is_var_args = is_var_args,
4255 .is_inferred_error = false,
4256 .is_test = false,
4257 .is_extern = true,
4258 .is_noinline = is_noinline,
4259 .noalias_bits = noalias_bits,
4260 });
4261 } else func: {
4262 // as a scope, fn_gz encloses ret_gz, but for instruction list, fn_gz stacks on ret_gz
4263 fn_gz.instructions_top = ret_gz.instructions.items.len;
4264
4265 const prev_fn_block = astgen.fn_block;
4266 const prev_fn_ret_ty = astgen.fn_ret_ty;
4267 astgen.fn_block = &fn_gz;
4268 astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: {
4269 // We're essentially guaranteed to need the return type at some point,
4270 // since the return type is likely not `void` or `noreturn` so there
4271 // will probably be an explicit return requiring RLS. Fetch this
4272 // return type now so the rest of the function can use it.
4273 break :r try fn_gz.addNode(.ret_type, decl_node);
4274 } else ret_ref;
4275 defer {
4276 astgen.fn_block = prev_fn_block;
4277 astgen.fn_ret_ty = prev_fn_ret_ty;
4278 }
4279
4280 const prev_var_args = astgen.fn_var_args;
4281 astgen.fn_var_args = is_var_args;
4282 defer astgen.fn_var_args = prev_var_args;
4283
4284 astgen.advanceSourceCursorToNode(body_node);
4285 const lbrace_line = astgen.source_line - decl_gz.decl_line;
4286 const lbrace_column = astgen.source_column;
4287
4288 _ = try expr(&fn_gz, params_scope, .{ .rl = .none }, body_node);
4289 try checkUsed(gz, &fn_gz.base, params_scope);
4290
4291 if (!fn_gz.endsWithNoReturn()) {
4292 // As our last action before the return, "pop" the error trace if needed
4293 _ = try fn_gz.addRestoreErrRetIndex(.ret, .always, decl_node);
4294
4295 // Add implicit return at end of function.
4296 _ = try fn_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
4297 }
4298
4299 break :func try decl_gz.addFunc(.{
4300 .src_node = decl_node,
4301 .cc_ref = cc_ref,
4302 .cc_gz = &cc_gz,
4303 .align_ref = align_ref,
4304 .align_gz = &align_gz,
4305 .ret_ref = ret_ref,
4306 .ret_gz = &ret_gz,
4307 .section_ref = section_ref,
4308 .section_gz = &section_gz,
4309 .addrspace_ref = addrspace_ref,
4310 .addrspace_gz = &addrspace_gz,
4311 .lbrace_line = lbrace_line,
4312 .lbrace_column = lbrace_column,
4313 .param_block = decl_inst,
4314 .body_gz = &fn_gz,
4315 .lib_name = lib_name,
4316 .is_var_args = is_var_args,
4317 .is_inferred_error = is_inferred_error,
4318 .is_test = false,
4319 .is_extern = false,
4320 .is_noinline = is_noinline,
4321 .noalias_bits = noalias_bits,
4322 });
4323 };
4324
4325 // We add this at the end so that its instruction index marks the end range
4326 // of the top level declaration. addFunc already unstacked fn_gz and ret_gz.
4327 _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst);
4328
4329 try setDeclaration(
4330 decl_inst,
4331 std.zig.hashSrc(tree.getNodeSource(decl_node)),
4332 .{ .named = fn_name_token },
4333 decl_gz.decl_line - gz.decl_line,
4334 is_pub,
4335 is_export,
4336 doc_comment_index,
4337 &decl_gz,
4338 // align, linksection, and addrspace are passed in the func instruction in this case.
4339 // TODO: move them from the function instruction to the declaration instruction?
4340 null,
4341 );
4342}
4343
4344fn globalVarDecl(
4345 astgen: *AstGen,
4346 gz: *GenZir,
4347 scope: *Scope,
4348 wip_members: *WipMembers,
4349 node: Ast.Node.Index,
4350 var_decl: Ast.full.VarDecl,
4351) InnerError!void {
4352 const tree = astgen.tree;
4353 const token_tags = tree.tokens.items(.tag);
4354
4355 const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
4356 // We do this at the beginning so that the instruction index marks the range start
4357 // of the top level declaration.
4358 const decl_inst = try gz.makeBlockInst(.declaration, node);
4359
4360 const name_token = var_decl.ast.mut_token + 1;
4361 astgen.advanceSourceCursorToNode(node);
4362
4363 var block_scope: GenZir = .{
4364 .parent = scope,
4365 .decl_node_index = node,
4366 .decl_line = astgen.source_line,
4367 .astgen = astgen,
4368 .is_comptime = true,
4369 .anon_name_strategy = .parent,
4370 .instructions = gz.instructions,
4371 .instructions_top = gz.instructions.items.len,
4372 };
4373 defer block_scope.unstack();
4374
4375 const is_pub = var_decl.visib_token != null;
4376 const is_export = blk: {
4377 const maybe_export_token = var_decl.extern_export_token orelse break :blk false;
4378 break :blk token_tags[maybe_export_token] == .keyword_export;
4379 };
4380 const is_extern = blk: {
4381 const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
4382 break :blk token_tags[maybe_extern_token] == .keyword_extern;
4383 };
4384 wip_members.nextDecl(decl_inst);
4385
4386 const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
4387 if (!is_mutable) {
4388 return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});
4389 }
4390 break :blk true;
4391 } else false;
4392
4393 const lib_name = if (var_decl.lib_name) |lib_name_token| blk: {
4394 const lib_name_str = try astgen.strLitAsString(lib_name_token);
4395 const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
4396 if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
4397 return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
4398 } else if (lib_name_str.len == 0) {
4399 return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
4400 }
4401 break :blk lib_name_str.index;
4402 } else .empty;
4403
4404 const doc_comment_index = try astgen.docCommentAsString(var_decl.firstToken());
4405
4406 assert(var_decl.comptime_token == null); // handled by parser
4407
4408 const var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: {
4409 if (is_extern) {
4410 return astgen.failNode(
4411 var_decl.ast.init_node,
4412 "extern variables have no initializers",
4413 .{},
4414 );
4415 }
4416
4417 const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node != 0)
4418 try expr(
4419 &block_scope,
4420 &block_scope.base,
4421 coerced_type_ri,
4422 var_decl.ast.type_node,
4423 )
4424 else
4425 .none;
4426
4427 const init_inst = try expr(
4428 &block_scope,
4429 &block_scope.base,
4430 if (type_inst != .none) .{ .rl = .{ .ty = type_inst } } else .{ .rl = .none },
4431 var_decl.ast.init_node,
4432 );
4433
4434 if (is_mutable) {
4435 const var_inst = try block_scope.addVar(.{
4436 .var_type = type_inst,
4437 .lib_name = .empty,
4438 .align_inst = .none, // passed via the decls data
4439 .init = init_inst,
4440 .is_extern = false,
4441 .is_const = !is_mutable,
4442 .is_threadlocal = is_threadlocal,
4443 });
4444 break :vi var_inst;
4445 } else {
4446 break :vi init_inst;
4447 }
4448 } else if (!is_extern) {
4449 return astgen.failNode(node, "variables must be initialized", .{});
4450 } else if (var_decl.ast.type_node != 0) vi: {
4451 // Extern variable which has an explicit type.
4452 const type_inst = try typeExpr(&block_scope, &block_scope.base, var_decl.ast.type_node);
4453
4454 const var_inst = try block_scope.addVar(.{
4455 .var_type = type_inst,
4456 .lib_name = lib_name,
4457 .align_inst = .none, // passed via the decls data
4458 .init = .none,
4459 .is_extern = true,
4460 .is_const = !is_mutable,
4461 .is_threadlocal = is_threadlocal,
4462 });
4463 break :vi var_inst;
4464 } else {
4465 return astgen.failNode(node, "unable to infer variable type", .{});
4466 };
4467
4468 // We do this at the end so that the instruction index marks the end
4469 // range of a top level declaration.
4470 _ = try block_scope.addBreakWithSrcNode(.break_inline, decl_inst, var_inst, node);
4471
4472 var align_gz = block_scope.makeSubBlock(scope);
4473 if (var_decl.ast.align_node != 0) {
4474 const align_inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node);
4475 _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node);
4476 }
4477
4478 var linksection_gz = align_gz.makeSubBlock(scope);
4479 if (var_decl.ast.section_node != 0) {
4480 const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node);
4481 _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node);
4482 }
4483
4484 var addrspace_gz = linksection_gz.makeSubBlock(scope);
4485 if (var_decl.ast.addrspace_node != 0) {
4486 const addrspace_inst = try expr(&addrspace_gz, &addrspace_gz.base, coerced_addrspace_ri, var_decl.ast.addrspace_node);
4487 _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node);
4488 }
4489
4490 try setDeclaration(
4491 decl_inst,
4492 std.zig.hashSrc(tree.getNodeSource(node)),
4493 .{ .named = name_token },
4494 block_scope.decl_line - gz.decl_line,
4495 is_pub,
4496 is_export,
4497 doc_comment_index,
4498 &block_scope,
4499 .{
4500 .align_gz = &align_gz,
4501 .linksection_gz = &linksection_gz,
4502 .addrspace_gz = &addrspace_gz,
4503 },
4504 );
4505}
4506
4507fn comptimeDecl(
4508 astgen: *AstGen,
4509 gz: *GenZir,
4510 scope: *Scope,
4511 wip_members: *WipMembers,
4512 node: Ast.Node.Index,
4513) InnerError!void {
4514 const tree = astgen.tree;
4515 const node_datas = tree.nodes.items(.data);
4516 const body_node = node_datas[node].lhs;
4517
4518 // Up top so the ZIR instruction index marks the start range of this
4519 // top-level declaration.
4520 const decl_inst = try gz.makeBlockInst(.declaration, node);
4521 wip_members.nextDecl(decl_inst);
4522 astgen.advanceSourceCursorToNode(node);
4523
4524 var decl_block: GenZir = .{
4525 .is_comptime = true,
4526 .decl_node_index = node,
4527 .decl_line = astgen.source_line,
4528 .parent = scope,
4529 .astgen = astgen,
4530 .instructions = gz.instructions,
4531 .instructions_top = gz.instructions.items.len,
4532 };
4533 defer decl_block.unstack();
4534
4535 const block_result = try expr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node);
4536 if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) {
4537 _ = try decl_block.addBreak(.break_inline, decl_inst, .void_value);
4538 }
4539
4540 try setDeclaration(
4541 decl_inst,
4542 std.zig.hashSrc(tree.getNodeSource(node)),
4543 .@"comptime",
4544 decl_block.decl_line - gz.decl_line,
4545 false,
4546 false,
4547 .empty,
4548 &decl_block,
4549 null,
4550 );
4551}
4552
4553fn usingnamespaceDecl(
4554 astgen: *AstGen,
4555 gz: *GenZir,
4556 scope: *Scope,
4557 wip_members: *WipMembers,
4558 node: Ast.Node.Index,
4559) InnerError!void {
4560 const tree = astgen.tree;
4561 const node_datas = tree.nodes.items(.data);
4562
4563 const type_expr = node_datas[node].lhs;
4564 const is_pub = blk: {
4565 const main_tokens = tree.nodes.items(.main_token);
4566 const token_tags = tree.tokens.items(.tag);
4567 const main_token = main_tokens[node];
4568 break :blk (main_token > 0 and token_tags[main_token - 1] == .keyword_pub);
4569 };
4570 // Up top so the ZIR instruction index marks the start range of this
4571 // top-level declaration.
4572 const decl_inst = try gz.makeBlockInst(.declaration, node);
4573 wip_members.nextDecl(decl_inst);
4574 astgen.advanceSourceCursorToNode(node);
4575
4576 var decl_block: GenZir = .{
4577 .is_comptime = true,
4578 .decl_node_index = node,
4579 .decl_line = astgen.source_line,
4580 .parent = scope,
4581 .astgen = astgen,
4582 .instructions = gz.instructions,
4583 .instructions_top = gz.instructions.items.len,
4584 };
4585 defer decl_block.unstack();
4586
4587 const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr);
4588 _ = try decl_block.addBreak(.break_inline, decl_inst, namespace_inst);
4589
4590 try setDeclaration(
4591 decl_inst,
4592 std.zig.hashSrc(tree.getNodeSource(node)),
4593 .@"usingnamespace",
4594 decl_block.decl_line - gz.decl_line,
4595 is_pub,
4596 false,
4597 .empty,
4598 &decl_block,
4599 null,
4600 );
4601}
4602
4603fn testDecl(
4604 astgen: *AstGen,
4605 gz: *GenZir,
4606 scope: *Scope,
4607 wip_members: *WipMembers,
4608 node: Ast.Node.Index,
4609) InnerError!void {
4610 const tree = astgen.tree;
4611 const node_datas = tree.nodes.items(.data);
4612 const body_node = node_datas[node].rhs;
4613
4614 // Up top so the ZIR instruction index marks the start range of this
4615 // top-level declaration.
4616 const decl_inst = try gz.makeBlockInst(.declaration, node);
4617
4618 wip_members.nextDecl(decl_inst);
4619 astgen.advanceSourceCursorToNode(node);
4620
4621 var decl_block: GenZir = .{
4622 .is_comptime = true,
4623 .decl_node_index = node,
4624 .decl_line = astgen.source_line,
4625 .parent = scope,
4626 .astgen = astgen,
4627 .instructions = gz.instructions,
4628 .instructions_top = gz.instructions.items.len,
4629 };
4630 defer decl_block.unstack();
4631
4632 const main_tokens = tree.nodes.items(.main_token);
4633 const token_tags = tree.tokens.items(.tag);
4634 const test_token = main_tokens[node];
4635 const test_name_token = test_token + 1;
4636 const test_name: DeclarationName = switch (token_tags[test_name_token]) {
4637 else => .unnamed_test,
4638 .string_literal => .{ .named_test = test_name_token },
4639 .identifier => blk: {
4640 const ident_name_raw = tree.tokenSlice(test_name_token);
4641
4642 if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{});
4643
4644 // if not @"" syntax, just use raw token slice
4645 if (ident_name_raw[0] != '@') {
4646 if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{});
4647 }
4648
4649 // Local variables, including function parameters.
4650 const name_str_index = try astgen.identAsString(test_name_token);
4651 var s = scope;
4652 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
4653 var num_namespaces_out: u32 = 0;
4654 var capturing_namespace: ?*Scope.Namespace = null;
4655 while (true) switch (s.tag) {
4656 .local_val => {
4657 const local_val = s.cast(Scope.LocalVal).?;
4658 if (local_val.name == name_str_index) {
4659 local_val.used = test_name_token;
4660 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
4661 @tagName(local_val.id_cat),
4662 }, &[_]u32{
4663 try astgen.errNoteTok(local_val.token_src, "{s} declared here", .{
4664 @tagName(local_val.id_cat),
4665 }),
4666 });
4667 }
4668 s = local_val.parent;
4669 },
4670 .local_ptr => {
4671 const local_ptr = s.cast(Scope.LocalPtr).?;
4672 if (local_ptr.name == name_str_index) {
4673 local_ptr.used = test_name_token;
4674 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
4675 @tagName(local_ptr.id_cat),
4676 }, &[_]u32{
4677 try astgen.errNoteTok(local_ptr.token_src, "{s} declared here", .{
4678 @tagName(local_ptr.id_cat),
4679 }),
4680 });
4681 }
4682 s = local_ptr.parent;
4683 },
4684 .gen_zir => s = s.cast(GenZir).?.parent,
4685 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
4686 .namespace, .enum_namespace => {
4687 const ns = s.cast(Scope.Namespace).?;
4688 if (ns.decls.get(name_str_index)) |i| {
4689 if (found_already) |f| {
4690 return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{
4691 try astgen.errNoteNode(f, "declared here", .{}),
4692 try astgen.errNoteNode(i, "also declared here", .{}),
4693 });
4694 }
4695 // We found a match but must continue looking for ambiguous references to decls.
4696 found_already = i;
4697 }
4698 num_namespaces_out += 1;
4699 capturing_namespace = ns;
4700 s = ns.parent;
4701 },
4702 .top => break,
4703 };
4704 if (found_already == null) {
4705 const ident_name = try astgen.identifierTokenString(test_name_token);
4706 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
4707 }
4708
4709 break :blk .{ .decltest = name_str_index };
4710 },
4711 };
4712
4713 var fn_block: GenZir = .{
4714 .is_comptime = false,
4715 .decl_node_index = node,
4716 .decl_line = decl_block.decl_line,
4717 .parent = &decl_block.base,
4718 .astgen = astgen,
4719 .instructions = decl_block.instructions,
4720 .instructions_top = decl_block.instructions.items.len,
4721 };
4722 defer fn_block.unstack();
4723
4724 const prev_fn_block = astgen.fn_block;
4725 const prev_fn_ret_ty = astgen.fn_ret_ty;
4726 astgen.fn_block = &fn_block;
4727 astgen.fn_ret_ty = .anyerror_void_error_union_type;
4728 defer {
4729 astgen.fn_block = prev_fn_block;
4730 astgen.fn_ret_ty = prev_fn_ret_ty;
4731 }
4732
4733 astgen.advanceSourceCursorToNode(body_node);
4734 const lbrace_line = astgen.source_line - decl_block.decl_line;
4735 const lbrace_column = astgen.source_column;
4736
4737 const block_result = try expr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node);
4738 if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) {
4739
4740 // As our last action before the return, "pop" the error trace if needed
4741 _ = try fn_block.addRestoreErrRetIndex(.ret, .always, node);
4742
4743 // Add implicit return at end of function.
4744 _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
4745 }
4746
4747 const func_inst = try decl_block.addFunc(.{
4748 .src_node = node,
4749
4750 .cc_ref = .none,
4751 .cc_gz = null,
4752 .align_ref = .none,
4753 .align_gz = null,
4754 .ret_ref = .anyerror_void_error_union_type,
4755 .ret_gz = null,
4756 .section_ref = .none,
4757 .section_gz = null,
4758 .addrspace_ref = .none,
4759 .addrspace_gz = null,
4760
4761 .lbrace_line = lbrace_line,
4762 .lbrace_column = lbrace_column,
4763 .param_block = decl_inst,
4764 .body_gz = &fn_block,
4765 .lib_name = .empty,
4766 .is_var_args = false,
4767 .is_inferred_error = false,
4768 .is_test = true,
4769 .is_extern = false,
4770 .is_noinline = false,
4771 .noalias_bits = 0,
4772 });
4773
4774 _ = try decl_block.addBreak(.break_inline, decl_inst, func_inst);
4775
4776 try setDeclaration(
4777 decl_inst,
4778 std.zig.hashSrc(tree.getNodeSource(node)),
4779 test_name,
4780 decl_block.decl_line - gz.decl_line,
4781 false,
4782 false,
4783 .empty,
4784 &decl_block,
4785 null,
4786 );
4787}
4788
4789fn structDeclInner(
4790 gz: *GenZir,
4791 scope: *Scope,
4792 node: Ast.Node.Index,
4793 container_decl: Ast.full.ContainerDecl,
4794 layout: std.builtin.Type.ContainerLayout,
4795 backing_int_node: Ast.Node.Index,
4796) InnerError!Zir.Inst.Ref {
4797 const decl_inst = try gz.reserveInstructionIndex();
4798
4799 if (container_decl.ast.members.len == 0 and backing_int_node == 0) {
4800 try gz.setStruct(decl_inst, .{
4801 .src_node = node,
4802 .layout = layout,
4803 .fields_len = 0,
4804 .decls_len = 0,
4805 .backing_int_ref = .none,
4806 .backing_int_body_len = 0,
4807 .known_non_opv = false,
4808 .known_comptime_only = false,
4809 .is_tuple = false,
4810 .any_comptime_fields = false,
4811 .any_default_inits = false,
4812 .any_aligned_fields = false,
4813 .fields_hash = std.zig.hashSrc(@tagName(layout)),
4814 });
4815 return decl_inst.toRef();
4816 }
4817
4818 const astgen = gz.astgen;
4819 const gpa = astgen.gpa;
4820 const tree = astgen.tree;
4821
4822 var namespace: Scope.Namespace = .{
4823 .parent = scope,
4824 .node = node,
4825 .inst = decl_inst,
4826 .declaring_gz = gz,
4827 };
4828 defer namespace.deinit(gpa);
4829
4830 // The struct_decl instruction introduces a scope in which the decls of the struct
4831 // are in scope, so that field types, alignments, and default value expressions
4832 // can refer to decls within the struct itself.
4833 astgen.advanceSourceCursorToNode(node);
4834 var block_scope: GenZir = .{
4835 .parent = &namespace.base,
4836 .decl_node_index = node,
4837 .decl_line = gz.decl_line,
4838 .astgen = astgen,
4839 .is_comptime = true,
4840 .instructions = gz.instructions,
4841 .instructions_top = gz.instructions.items.len,
4842 };
4843 defer block_scope.unstack();
4844
4845 const scratch_top = astgen.scratch.items.len;
4846 defer astgen.scratch.items.len = scratch_top;
4847
4848 var backing_int_body_len: usize = 0;
4849 const backing_int_ref: Zir.Inst.Ref = blk: {
4850 if (backing_int_node != 0) {
4851 if (layout != .Packed) {
4852 return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{});
4853 } else {
4854 const backing_int_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node);
4855 if (!block_scope.isEmpty()) {
4856 if (!block_scope.endsWithNoReturn()) {
4857 _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref);
4858 }
4859
4860 const body = block_scope.instructionsSlice();
4861 const old_scratch_len = astgen.scratch.items.len;
4862 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
4863 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
4864 backing_int_body_len = astgen.scratch.items.len - old_scratch_len;
4865 block_scope.instructions.items.len = block_scope.instructions_top;
4866 }
4867 break :blk backing_int_ref;
4868 }
4869 } else {
4870 break :blk .none;
4871 }
4872 };
4873
4874 const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
4875 const field_count: u32 = @intCast(container_decl.ast.members.len - decl_count);
4876
4877 const bits_per_field = 4;
4878 const max_field_size = 5;
4879 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
4880 defer wip_members.deinit();
4881
4882 // We will use the scratch buffer, starting here, for the bodies:
4883 // bodies: { // for every fields_len
4884 // field_type_body_inst: Inst, // for each field_type_body_len
4885 // align_body_inst: Inst, // for each align_body_len
4886 // init_body_inst: Inst, // for each init_body_len
4887 // }
4888 // Note that the scratch buffer is simultaneously being used by WipMembers, however
4889 // it will not access any elements beyond this point in the ArrayList. It also
4890 // accesses via the ArrayList items field so it can handle the scratch buffer being
4891 // reallocated.
4892 // No defer needed here because it is handled by `wip_members.deinit()` above.
4893 const bodies_start = astgen.scratch.items.len;
4894
4895 const node_tags = tree.nodes.items(.tag);
4896 const is_tuple = for (container_decl.ast.members) |member_node| {
4897 const container_field = tree.fullContainerField(member_node) orelse continue;
4898 if (container_field.ast.tuple_like) break true;
4899 } else false;
4900
4901 if (is_tuple) switch (layout) {
4902 .Auto => {},
4903 .Extern => return astgen.failNode(node, "extern tuples are not supported", .{}),
4904 .Packed => return astgen.failNode(node, "packed tuples are not supported", .{}),
4905 };
4906
4907 if (is_tuple) for (container_decl.ast.members) |member_node| {
4908 switch (node_tags[member_node]) {
4909 .container_field_init,
4910 .container_field_align,
4911 .container_field,
4912 .@"comptime",
4913 .test_decl,
4914 => continue,
4915 else => {
4916 const tuple_member = for (container_decl.ast.members) |maybe_tuple| switch (node_tags[maybe_tuple]) {
4917 .container_field_init,
4918 .container_field_align,
4919 .container_field,
4920 => break maybe_tuple,
4921 else => {},
4922 } else unreachable;
4923 return astgen.failNodeNotes(
4924 member_node,
4925 "tuple declarations cannot contain declarations",
4926 .{},
4927 &[_]u32{
4928 try astgen.errNoteNode(tuple_member, "tuple field here", .{}),
4929 },
4930 );
4931 },
4932 }
4933 };
4934
4935 var fields_hasher = std.zig.SrcHasher.init(.{});
4936 fields_hasher.update(@tagName(layout));
4937 if (backing_int_node != 0) {
4938 fields_hasher.update(tree.getNodeSource(backing_int_node));
4939 }
4940
4941 var sfba = std.heap.stackFallback(256, astgen.arena);
4942 const sfba_allocator = sfba.get();
4943
4944 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
4945 try duplicate_names.ensureTotalCapacity(field_count);
4946
4947 // When there aren't errors, use this to avoid a second iteration.
4948 var any_duplicate = false;
4949
4950 var known_non_opv = false;
4951 var known_comptime_only = false;
4952 var any_comptime_fields = false;
4953 var any_aligned_fields = false;
4954 var any_default_inits = false;
4955 for (container_decl.ast.members) |member_node| {
4956 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
4957 .decl => continue,
4958 .field => |field| field,
4959 };
4960
4961 fields_hasher.update(tree.getNodeSource(member_node));
4962
4963 if (!is_tuple) {
4964 const field_name = try astgen.identAsString(member.ast.main_token);
4965
4966 member.convertToNonTupleLike(astgen.tree.nodes);
4967 assert(!member.ast.tuple_like);
4968
4969 wip_members.appendToField(@intFromEnum(field_name));
4970
4971 const gop = try duplicate_names.getOrPut(field_name);
4972
4973 if (gop.found_existing) {
4974 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
4975 any_duplicate = true;
4976 } else {
4977 gop.value_ptr.* = .{};
4978 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
4979 }
4980 } else if (!member.ast.tuple_like) {
4981 return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
4982 }
4983
4984 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
4985 wip_members.appendToField(@intFromEnum(doc_comment_index));
4986
4987 if (member.ast.type_expr == 0) {
4988 return astgen.failTok(member.ast.main_token, "struct field missing type", .{});
4989 }
4990
4991 const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
4992 const have_type_body = !block_scope.isEmpty();
4993 const have_align = member.ast.align_expr != 0;
4994 const have_value = member.ast.value_expr != 0;
4995 const is_comptime = member.comptime_token != null;
4996
4997 if (is_comptime) {
4998 switch (layout) {
4999 .Packed => return astgen.failTok(member.comptime_token.?, "packed struct fields cannot be marked comptime", .{}),
5000 .Extern => return astgen.failTok(member.comptime_token.?, "extern struct fields cannot be marked comptime", .{}),
5001 .Auto => any_comptime_fields = true,
5002 }
5003 } else {
5004 known_non_opv = known_non_opv or
5005 nodeImpliesMoreThanOnePossibleValue(tree, member.ast.type_expr);
5006 known_comptime_only = known_comptime_only or
5007 nodeImpliesComptimeOnly(tree, member.ast.type_expr);
5008 }
5009 wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, have_type_body });
5010
5011 if (have_type_body) {
5012 if (!block_scope.endsWithNoReturn()) {
5013 _ = try block_scope.addBreak(.break_inline, decl_inst, field_type);
5014 }
5015 const body = block_scope.instructionsSlice();
5016 const old_scratch_len = astgen.scratch.items.len;
5017 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
5018 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
5019 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
5020 block_scope.instructions.items.len = block_scope.instructions_top;
5021 } else {
5022 wip_members.appendToField(@intFromEnum(field_type));
5023 }
5024
5025 if (have_align) {
5026 if (layout == .Packed) {
5027 try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
5028 }
5029 any_aligned_fields = true;
5030 const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr);
5031 if (!block_scope.endsWithNoReturn()) {
5032 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref);
5033 }
5034 const body = block_scope.instructionsSlice();
5035 const old_scratch_len = astgen.scratch.items.len;
5036 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
5037 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
5038 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
5039 block_scope.instructions.items.len = block_scope.instructions_top;
5040 }
5041
5042 if (have_value) {
5043 any_default_inits = true;
5044
5045 // The decl_inst is used as here so that we can easily reconstruct a mapping
5046 // between it and the field type when the fields inits are analzyed.
5047 const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = decl_inst.toRef() } };
5048
5049 const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr);
5050 if (!block_scope.endsWithNoReturn()) {
5051 _ = try block_scope.addBreak(.break_inline, decl_inst, default_inst);
5052 }
5053 const body = block_scope.instructionsSlice();
5054 const old_scratch_len = astgen.scratch.items.len;
5055 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
5056 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
5057 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
5058 block_scope.instructions.items.len = block_scope.instructions_top;
5059 } else if (member.comptime_token) |comptime_token| {
5060 return astgen.failTok(comptime_token, "comptime field without default initialization value", .{});
5061 }
5062 }
5063
5064 if (any_duplicate) {
5065 var it = duplicate_names.iterator();
5066
5067 while (it.next()) |entry| {
5068 const record = entry.value_ptr.*;
5069 if (record.items.len > 1) {
5070 var error_notes = std.ArrayList(u32).init(astgen.arena);
5071
5072 for (record.items[1..]) |duplicate| {
5073 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate field here", .{}));
5074 }
5075
5076 try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));
5077
5078 try astgen.appendErrorTokNotes(
5079 record.items[0],
5080 "duplicate struct field name",
5081 .{},
5082 error_notes.items,
5083 );
5084 }
5085 }
5086
5087 return error.AnalysisFail;
5088 }
5089
5090 var fields_hash: std.zig.SrcHash = undefined;
5091 fields_hasher.final(&fields_hash);
5092
5093 try gz.setStruct(decl_inst, .{
5094 .src_node = node,
5095 .layout = layout,
5096 .fields_len = field_count,
5097 .decls_len = decl_count,
5098 .backing_int_ref = backing_int_ref,
5099 .backing_int_body_len = @intCast(backing_int_body_len),
5100 .known_non_opv = known_non_opv,
5101 .known_comptime_only = known_comptime_only,
5102 .is_tuple = is_tuple,
5103 .any_comptime_fields = any_comptime_fields,
5104 .any_default_inits = any_default_inits,
5105 .any_aligned_fields = any_aligned_fields,
5106 .fields_hash = fields_hash,
5107 });
5108
5109 wip_members.finishBits(bits_per_field);
5110 const decls_slice = wip_members.declsSlice();
5111 const fields_slice = wip_members.fieldsSlice();
5112 const bodies_slice = astgen.scratch.items[bodies_start..];
5113 try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len +
5114 decls_slice.len + fields_slice.len + bodies_slice.len);
5115 astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]);
5116 astgen.extra.appendSliceAssumeCapacity(decls_slice);
5117 astgen.extra.appendSliceAssumeCapacity(fields_slice);
5118 astgen.extra.appendSliceAssumeCapacity(bodies_slice);
5119
5120 block_scope.unstack();
5121 try gz.addNamespaceCaptures(&namespace);
5122 return decl_inst.toRef();
5123}
5124
5125fn unionDeclInner(
5126 gz: *GenZir,
5127 scope: *Scope,
5128 node: Ast.Node.Index,
5129 members: []const Ast.Node.Index,
5130 layout: std.builtin.Type.ContainerLayout,
5131 arg_node: Ast.Node.Index,
5132 auto_enum_tok: ?Ast.TokenIndex,
5133) InnerError!Zir.Inst.Ref {
5134 const decl_inst = try gz.reserveInstructionIndex();
5135
5136 const astgen = gz.astgen;
5137 const gpa = astgen.gpa;
5138
5139 var namespace: Scope.Namespace = .{
5140 .parent = scope,
5141 .node = node,
5142 .inst = decl_inst,
5143 .declaring_gz = gz,
5144 };
5145 defer namespace.deinit(gpa);
5146
5147 // The union_decl instruction introduces a scope in which the decls of the union
5148 // are in scope, so that field types, alignments, and default value expressions
5149 // can refer to decls within the union itself.
5150 astgen.advanceSourceCursorToNode(node);
5151 var block_scope: GenZir = .{
5152 .parent = &namespace.base,
5153 .decl_node_index = node,
5154 .decl_line = gz.decl_line,
5155 .astgen = astgen,
5156 .is_comptime = true,
5157 .instructions = gz.instructions,
5158 .instructions_top = gz.instructions.items.len,
5159 };
5160 defer block_scope.unstack();
5161
5162 const decl_count = try astgen.scanDecls(&namespace, members);
5163 const field_count: u32 = @intCast(members.len - decl_count);
5164
5165 if (layout != .Auto and (auto_enum_tok != null or arg_node != 0)) {
5166 const layout_str = if (layout == .Extern) "extern" else "packed";
5167 if (arg_node != 0) {
5168 return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{layout_str});
5169 } else {
5170 return astgen.failTok(auto_enum_tok.?, "{s} union does not support enum tag type", .{layout_str});
5171 }
5172 }
5173
5174 const arg_inst: Zir.Inst.Ref = if (arg_node != 0)
5175 try typeExpr(&block_scope, &namespace.base, arg_node)
5176 else
5177 .none;
5178
5179 const bits_per_field = 4;
5180 const max_field_size = 5;
5181 var any_aligned_fields = false;
5182 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
5183 defer wip_members.deinit();
5184
5185 var fields_hasher = std.zig.SrcHasher.init(.{});
5186 fields_hasher.update(@tagName(layout));
5187 fields_hasher.update(&.{@intFromBool(auto_enum_tok != null)});
5188 if (arg_node != 0) {
5189 fields_hasher.update(astgen.tree.getNodeSource(arg_node));
5190 }
5191
5192 var sfba = std.heap.stackFallback(256, astgen.arena);
5193 const sfba_allocator = sfba.get();
5194
5195 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
5196 try duplicate_names.ensureTotalCapacity(field_count);
5197
5198 // When there aren't errors, use this to avoid a second iteration.
5199 var any_duplicate = false;
5200
5201 for (members) |member_node| {
5202 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
5203 .decl => continue,
5204 .field => |field| field,
5205 };
5206 fields_hasher.update(astgen.tree.getNodeSource(member_node));
5207 member.convertToNonTupleLike(astgen.tree.nodes);
5208 if (member.ast.tuple_like) {
5209 return astgen.failTok(member.ast.main_token, "union field missing name", .{});
5210 }
5211 if (member.comptime_token) |comptime_token| {
5212 return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});
5213 }
5214
5215 const field_name = try astgen.identAsString(member.ast.main_token);
5216 wip_members.appendToField(@intFromEnum(field_name));
5217
5218 const gop = try duplicate_names.getOrPut(field_name);
5219
5220 if (gop.found_existing) {
5221 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5222 any_duplicate = true;
5223 } else {
5224 gop.value_ptr.* = .{};
5225 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5226 }
5227
5228 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
5229 wip_members.appendToField(@intFromEnum(doc_comment_index));
5230
5231 const have_type = member.ast.type_expr != 0;
5232 const have_align = member.ast.align_expr != 0;
5233 const have_value = member.ast.value_expr != 0;
5234 const unused = false;
5235 wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused });
5236
5237 if (have_type) {
5238 const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
5239 wip_members.appendToField(@intFromEnum(field_type));
5240 } else if (arg_inst == .none and auto_enum_tok == null) {
5241 return astgen.failNode(member_node, "union field missing type", .{});
5242 }
5243 if (have_align) {
5244 const align_inst = try expr(&block_scope, &block_scope.base, coerced_align_ri, member.ast.align_expr);
5245 wip_members.appendToField(@intFromEnum(align_inst));
5246 any_aligned_fields = true;
5247 }
5248 if (have_value) {
5249 if (arg_inst == .none) {
5250 return astgen.failNodeNotes(
5251 node,
5252 "explicitly valued tagged union missing integer tag type",
5253 .{},
5254 &[_]u32{
5255 try astgen.errNoteNode(
5256 member.ast.value_expr,
5257 "tag value specified here",
5258 .{},
5259 ),
5260 },
5261 );
5262 }
5263 if (auto_enum_tok == null) {
5264 return astgen.failNodeNotes(
5265 node,
5266 "explicitly valued tagged union requires inferred enum tag type",
5267 .{},
5268 &[_]u32{
5269 try astgen.errNoteNode(
5270 member.ast.value_expr,
5271 "tag value specified here",
5272 .{},
5273 ),
5274 },
5275 );
5276 }
5277 const tag_value = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr);
5278 wip_members.appendToField(@intFromEnum(tag_value));
5279 }
5280 }
5281
5282 if (any_duplicate) {
5283 var it = duplicate_names.iterator();
5284
5285 while (it.next()) |entry| {
5286 const record = entry.value_ptr.*;
5287 if (record.items.len > 1) {
5288 var error_notes = std.ArrayList(u32).init(astgen.arena);
5289
5290 for (record.items[1..]) |duplicate| {
5291 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate field here", .{}));
5292 }
5293
5294 try error_notes.append(try astgen.errNoteNode(node, "union declared here", .{}));
5295
5296 try astgen.appendErrorTokNotes(
5297 record.items[0],
5298 "duplicate union field name",
5299 .{},
5300 error_notes.items,
5301 );
5302 }
5303 }
5304
5305 return error.AnalysisFail;
5306 }
5307
5308 var fields_hash: std.zig.SrcHash = undefined;
5309 fields_hasher.final(&fields_hash);
5310
5311 if (!block_scope.isEmpty()) {
5312 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
5313 }
5314
5315 const body = block_scope.instructionsSlice();
5316 const body_len = astgen.countBodyLenAfterFixups(body);
5317
5318 try gz.setUnion(decl_inst, .{
5319 .src_node = node,
5320 .layout = layout,
5321 .tag_type = arg_inst,
5322 .body_len = body_len,
5323 .fields_len = field_count,
5324 .decls_len = decl_count,
5325 .auto_enum_tag = auto_enum_tok != null,
5326 .any_aligned_fields = any_aligned_fields,
5327 .fields_hash = fields_hash,
5328 });
5329
5330 wip_members.finishBits(bits_per_field);
5331 const decls_slice = wip_members.declsSlice();
5332 const fields_slice = wip_members.fieldsSlice();
5333 try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len);
5334 astgen.extra.appendSliceAssumeCapacity(decls_slice);
5335 astgen.appendBodyWithFixups(body);
5336 astgen.extra.appendSliceAssumeCapacity(fields_slice);
5337
5338 block_scope.unstack();
5339 try gz.addNamespaceCaptures(&namespace);
5340 return decl_inst.toRef();
5341}
5342
5343fn containerDecl(
5344 gz: *GenZir,
5345 scope: *Scope,
5346 ri: ResultInfo,
5347 node: Ast.Node.Index,
5348 container_decl: Ast.full.ContainerDecl,
5349) InnerError!Zir.Inst.Ref {
5350 const astgen = gz.astgen;
5351 const gpa = astgen.gpa;
5352 const tree = astgen.tree;
5353 const token_tags = tree.tokens.items(.tag);
5354
5355 const prev_fn_block = astgen.fn_block;
5356 astgen.fn_block = null;
5357 defer astgen.fn_block = prev_fn_block;
5358
5359 // We must not create any types until Sema. Here the goal is only to generate
5360 // ZIR for all the field types, alignments, and default value expressions.
5361
5362 switch (token_tags[container_decl.ast.main_token]) {
5363 .keyword_struct => {
5364 const layout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
5365 .keyword_packed => std.builtin.Type.ContainerLayout.Packed,
5366 .keyword_extern => std.builtin.Type.ContainerLayout.Extern,
5367 else => unreachable,
5368 } else std.builtin.Type.ContainerLayout.Auto;
5369
5370 const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg);
5371 return rvalue(gz, ri, result, node);
5372 },
5373 .keyword_union => {
5374 const layout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
5375 .keyword_packed => std.builtin.Type.ContainerLayout.Packed,
5376 .keyword_extern => std.builtin.Type.ContainerLayout.Extern,
5377 else => unreachable,
5378 } else std.builtin.Type.ContainerLayout.Auto;
5379
5380 const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token);
5381 return rvalue(gz, ri, result, node);
5382 },
5383 .keyword_enum => {
5384 if (container_decl.layout_token) |t| {
5385 return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{});
5386 }
5387 // Count total fields as well as how many have explicitly provided tag values.
5388 const counts = blk: {
5389 var values: usize = 0;
5390 var total_fields: usize = 0;
5391 var decls: usize = 0;
5392 var nonexhaustive_node: Ast.Node.Index = 0;
5393 var nonfinal_nonexhaustive = false;
5394 for (container_decl.ast.members) |member_node| {
5395 var member = tree.fullContainerField(member_node) orelse {
5396 decls += 1;
5397 continue;
5398 };
5399 member.convertToNonTupleLike(astgen.tree.nodes);
5400 if (member.ast.tuple_like) {
5401 return astgen.failTok(member.ast.main_token, "enum field missing name", .{});
5402 }
5403 if (member.comptime_token) |comptime_token| {
5404 return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});
5405 }
5406 if (member.ast.type_expr != 0) {
5407 return astgen.failNodeNotes(
5408 member.ast.type_expr,
5409 "enum fields do not have types",
5410 .{},
5411 &[_]u32{
5412 try astgen.errNoteNode(
5413 node,
5414 "consider 'union(enum)' here to make it a tagged union",
5415 .{},
5416 ),
5417 },
5418 );
5419 }
5420 if (member.ast.align_expr != 0) {
5421 return astgen.failNode(member.ast.align_expr, "enum fields cannot be aligned", .{});
5422 }
5423
5424 const name_token = member.ast.main_token;
5425 if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
5426 if (nonexhaustive_node != 0) {
5427 return astgen.failNodeNotes(
5428 member_node,
5429 "redundant non-exhaustive enum mark",
5430 .{},
5431 &[_]u32{
5432 try astgen.errNoteNode(
5433 nonexhaustive_node,
5434 "other mark here",
5435 .{},
5436 ),
5437 },
5438 );
5439 }
5440 nonexhaustive_node = member_node;
5441 if (member.ast.value_expr != 0) {
5442 return astgen.failNode(member.ast.value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{});
5443 }
5444 continue;
5445 } else if (nonexhaustive_node != 0) {
5446 nonfinal_nonexhaustive = true;
5447 }
5448 total_fields += 1;
5449 if (member.ast.value_expr != 0) {
5450 if (container_decl.ast.arg == 0) {
5451 return astgen.failNode(member.ast.value_expr, "value assigned to enum tag with inferred tag type", .{});
5452 }
5453 values += 1;
5454 }
5455 }
5456 if (nonfinal_nonexhaustive) {
5457 return astgen.failNode(nonexhaustive_node, "'_' field of non-exhaustive enum must be last", .{});
5458 }
5459 break :blk .{
5460 .total_fields = total_fields,
5461 .values = values,
5462 .decls = decls,
5463 .nonexhaustive_node = nonexhaustive_node,
5464 };
5465 };
5466 if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) {
5467 try astgen.appendErrorNodeNotes(
5468 node,
5469 "non-exhaustive enum missing integer tag type",
5470 .{},
5471 &[_]u32{
5472 try astgen.errNoteNode(
5473 counts.nonexhaustive_node,
5474 "marked non-exhaustive here",
5475 .{},
5476 ),
5477 },
5478 );
5479 }
5480 // In this case we must generate ZIR code for the tag values, similar to
5481 // how structs are handled above.
5482 const nonexhaustive = counts.nonexhaustive_node != 0;
5483
5484 const decl_inst = try gz.reserveInstructionIndex();
5485
5486 var namespace: Scope.Namespace = .{
5487 .parent = scope,
5488 .node = node,
5489 .inst = decl_inst,
5490 .declaring_gz = gz,
5491 };
5492 defer namespace.deinit(gpa);
5493
5494 // The enum_decl instruction introduces a scope in which the decls of the enum
5495 // are in scope, so that tag values can refer to decls within the enum itself.
5496 astgen.advanceSourceCursorToNode(node);
5497 var block_scope: GenZir = .{
5498 .parent = &namespace.base,
5499 .decl_node_index = node,
5500 .decl_line = gz.decl_line,
5501 .astgen = astgen,
5502 .is_comptime = true,
5503 .instructions = gz.instructions,
5504 .instructions_top = gz.instructions.items.len,
5505 };
5506 defer block_scope.unstack();
5507
5508 _ = try astgen.scanDecls(&namespace, container_decl.ast.members);
5509 namespace.base.tag = .enum_namespace;
5510
5511 const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0)
5512 try comptimeExpr(&block_scope, &namespace.base, coerced_type_ri, container_decl.ast.arg)
5513 else
5514 .none;
5515
5516 const bits_per_field = 1;
5517 const max_field_size = 3;
5518 var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size);
5519 defer wip_members.deinit();
5520
5521 var fields_hasher = std.zig.SrcHasher.init(.{});
5522 if (container_decl.ast.arg != 0) {
5523 fields_hasher.update(tree.getNodeSource(container_decl.ast.arg));
5524 }
5525 fields_hasher.update(&.{@intFromBool(nonexhaustive)});
5526
5527 var sfba = std.heap.stackFallback(256, astgen.arena);
5528 const sfba_allocator = sfba.get();
5529
5530 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
5531 try duplicate_names.ensureTotalCapacity(counts.total_fields);
5532
5533 // When there aren't errors, use this to avoid a second iteration.
5534 var any_duplicate = false;
5535
5536 for (container_decl.ast.members) |member_node| {
5537 if (member_node == counts.nonexhaustive_node)
5538 continue;
5539 fields_hasher.update(tree.getNodeSource(member_node));
5540 namespace.base.tag = .namespace;
5541 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
5542 .decl => continue,
5543 .field => |field| field,
5544 };
5545 member.convertToNonTupleLike(astgen.tree.nodes);
5546 assert(member.comptime_token == null);
5547 assert(member.ast.type_expr == 0);
5548 assert(member.ast.align_expr == 0);
5549
5550 const field_name = try astgen.identAsString(member.ast.main_token);
5551 wip_members.appendToField(@intFromEnum(field_name));
5552
5553 const gop = try duplicate_names.getOrPut(field_name);
5554
5555 if (gop.found_existing) {
5556 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5557 any_duplicate = true;
5558 } else {
5559 gop.value_ptr.* = .{};
5560 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5561 }
5562
5563 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
5564 wip_members.appendToField(@intFromEnum(doc_comment_index));
5565
5566 const have_value = member.ast.value_expr != 0;
5567 wip_members.nextField(bits_per_field, .{have_value});
5568
5569 if (have_value) {
5570 if (arg_inst == .none) {
5571 return astgen.failNodeNotes(
5572 node,
5573 "explicitly valued enum missing integer tag type",
5574 .{},
5575 &[_]u32{
5576 try astgen.errNoteNode(
5577 member.ast.value_expr,
5578 "tag value specified here",
5579 .{},
5580 ),
5581 },
5582 );
5583 }
5584 namespace.base.tag = .enum_namespace;
5585 const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr);
5586 wip_members.appendToField(@intFromEnum(tag_value_inst));
5587 }
5588 }
5589
5590 if (any_duplicate) {
5591 var it = duplicate_names.iterator();
5592
5593 while (it.next()) |entry| {
5594 const record = entry.value_ptr.*;
5595 if (record.items.len > 1) {
5596 var error_notes = std.ArrayList(u32).init(astgen.arena);
5597
5598 for (record.items[1..]) |duplicate| {
5599 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate field here", .{}));
5600 }
5601
5602 try error_notes.append(try astgen.errNoteNode(node, "enum declared here", .{}));
5603
5604 try astgen.appendErrorTokNotes(
5605 record.items[0],
5606 "duplicate enum field name",
5607 .{},
5608 error_notes.items,
5609 );
5610 }
5611 }
5612
5613 return error.AnalysisFail;
5614 }
5615
5616 if (!block_scope.isEmpty()) {
5617 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
5618 }
5619
5620 var fields_hash: std.zig.SrcHash = undefined;
5621 fields_hasher.final(&fields_hash);
5622
5623 const body = block_scope.instructionsSlice();
5624 const body_len = astgen.countBodyLenAfterFixups(body);
5625
5626 try gz.setEnum(decl_inst, .{
5627 .src_node = node,
5628 .nonexhaustive = nonexhaustive,
5629 .tag_type = arg_inst,
5630 .body_len = body_len,
5631 .fields_len = @intCast(counts.total_fields),
5632 .decls_len = @intCast(counts.decls),
5633 .fields_hash = fields_hash,
5634 });
5635
5636 wip_members.finishBits(bits_per_field);
5637 const decls_slice = wip_members.declsSlice();
5638 const fields_slice = wip_members.fieldsSlice();
5639 try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len);
5640 astgen.extra.appendSliceAssumeCapacity(decls_slice);
5641 astgen.appendBodyWithFixups(body);
5642 astgen.extra.appendSliceAssumeCapacity(fields_slice);
5643
5644 block_scope.unstack();
5645 try gz.addNamespaceCaptures(&namespace);
5646 return rvalue(gz, ri, decl_inst.toRef(), node);
5647 },
5648 .keyword_opaque => {
5649 assert(container_decl.ast.arg == 0);
5650
5651 const decl_inst = try gz.reserveInstructionIndex();
5652
5653 var namespace: Scope.Namespace = .{
5654 .parent = scope,
5655 .node = node,
5656 .inst = decl_inst,
5657 .declaring_gz = gz,
5658 };
5659 defer namespace.deinit(gpa);
5660
5661 astgen.advanceSourceCursorToNode(node);
5662 var block_scope: GenZir = .{
5663 .parent = &namespace.base,
5664 .decl_node_index = node,
5665 .decl_line = gz.decl_line,
5666 .astgen = astgen,
5667 .is_comptime = true,
5668 .instructions = gz.instructions,
5669 .instructions_top = gz.instructions.items.len,
5670 };
5671 defer block_scope.unstack();
5672
5673 const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
5674
5675 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0);
5676 defer wip_members.deinit();
5677
5678 for (container_decl.ast.members) |member_node| {
5679 const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node);
5680 if (res == .field) {
5681 return astgen.failNode(member_node, "opaque types cannot have fields", .{});
5682 }
5683 }
5684
5685 try gz.setOpaque(decl_inst, .{
5686 .src_node = node,
5687 .decls_len = decl_count,
5688 });
5689
5690 wip_members.finishBits(0);
5691 const decls_slice = wip_members.declsSlice();
5692 try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len);
5693 astgen.extra.appendSliceAssumeCapacity(decls_slice);
5694
5695 block_scope.unstack();
5696 try gz.addNamespaceCaptures(&namespace);
5697 return rvalue(gz, ri, decl_inst.toRef(), node);
5698 },
5699 else => unreachable,
5700 }
5701}
5702
5703const ContainerMemberResult = union(enum) { decl, field: Ast.full.ContainerField };
5704
5705fn containerMember(
5706 gz: *GenZir,
5707 scope: *Scope,
5708 wip_members: *WipMembers,
5709 member_node: Ast.Node.Index,
5710) InnerError!ContainerMemberResult {
5711 const astgen = gz.astgen;
5712 const tree = astgen.tree;
5713 const node_tags = tree.nodes.items(.tag);
5714 const node_datas = tree.nodes.items(.data);
5715 switch (node_tags[member_node]) {
5716 .container_field_init,
5717 .container_field_align,
5718 .container_field,
5719 => return ContainerMemberResult{ .field = tree.fullContainerField(member_node).? },
5720
5721 .fn_proto,
5722 .fn_proto_multi,
5723 .fn_proto_one,
5724 .fn_proto_simple,
5725 .fn_decl,
5726 => {
5727 var buf: [1]Ast.Node.Index = undefined;
5728 const full = tree.fullFnProto(&buf, member_node).?;
5729 const body = if (node_tags[member_node] == .fn_decl) node_datas[member_node].rhs else 0;
5730
5731 astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) {
5732 error.OutOfMemory => return error.OutOfMemory,
5733 error.AnalysisFail => {},
5734 };
5735 },
5736
5737 .global_var_decl,
5738 .local_var_decl,
5739 .simple_var_decl,
5740 .aligned_var_decl,
5741 => {
5742 astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.fullVarDecl(member_node).?) catch |err| switch (err) {
5743 error.OutOfMemory => return error.OutOfMemory,
5744 error.AnalysisFail => {},
5745 };
5746 },
5747
5748 .@"comptime" => {
5749 astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
5750 error.OutOfMemory => return error.OutOfMemory,
5751 error.AnalysisFail => {},
5752 };
5753 },
5754 .@"usingnamespace" => {
5755 astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
5756 error.OutOfMemory => return error.OutOfMemory,
5757 error.AnalysisFail => {},
5758 };
5759 },
5760 .test_decl => {
5761 astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
5762 error.OutOfMemory => return error.OutOfMemory,
5763 error.AnalysisFail => {},
5764 };
5765 },
5766 else => unreachable,
5767 }
5768 return .decl;
5769}
5770
5771fn errorSetDecl(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
5772 const astgen = gz.astgen;
5773 const gpa = astgen.gpa;
5774 const tree = astgen.tree;
5775 const main_tokens = tree.nodes.items(.main_token);
5776 const token_tags = tree.tokens.items(.tag);
5777
5778 const payload_index = try reserveExtra(astgen, @typeInfo(Zir.Inst.ErrorSetDecl).Struct.fields.len);
5779 var fields_len: usize = 0;
5780 {
5781 var idents: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .{};
5782 defer idents.deinit(gpa);
5783
5784 const error_token = main_tokens[node];
5785 var tok_i = error_token + 2;
5786 while (true) : (tok_i += 1) {
5787 switch (token_tags[tok_i]) {
5788 .doc_comment, .comma => {},
5789 .identifier => {
5790 const str_index = try astgen.identAsString(tok_i);
5791 const gop = try idents.getOrPut(gpa, str_index);
5792 if (gop.found_existing) {
5793 const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(str_index)));
5794 defer gpa.free(name);
5795 return astgen.failTokNotes(
5796 tok_i,
5797 "duplicate error set field '{s}'",
5798 .{name},
5799 &[_]u32{
5800 try astgen.errNoteTok(
5801 gop.value_ptr.*,
5802 "previous declaration here",
5803 .{},
5804 ),
5805 },
5806 );
5807 }
5808 gop.value_ptr.* = tok_i;
5809
5810 try astgen.extra.ensureUnusedCapacity(gpa, 2);
5811 astgen.extra.appendAssumeCapacity(@intFromEnum(str_index));
5812 const doc_comment_index = try astgen.docCommentAsString(tok_i);
5813 astgen.extra.appendAssumeCapacity(@intFromEnum(doc_comment_index));
5814 fields_len += 1;
5815 },
5816 .r_brace => break,
5817 else => unreachable,
5818 }
5819 }
5820 }
5821
5822 setExtra(astgen, payload_index, Zir.Inst.ErrorSetDecl{
5823 .fields_len = @intCast(fields_len),
5824 });
5825 const result = try gz.addPlNodePayloadIndex(.error_set_decl, node, payload_index);
5826 return rvalue(gz, ri, result, node);
5827}
5828
5829fn tryExpr(
5830 parent_gz: *GenZir,
5831 scope: *Scope,
5832 ri: ResultInfo,
5833 node: Ast.Node.Index,
5834 operand_node: Ast.Node.Index,
5835) InnerError!Zir.Inst.Ref {
5836 const astgen = parent_gz.astgen;
5837
5838 const fn_block = astgen.fn_block orelse {
5839 return astgen.failNode(node, "'try' outside function scope", .{});
5840 };
5841
5842 if (parent_gz.any_defer_node != 0) {
5843 return astgen.failNodeNotes(node, "'try' not allowed inside defer expression", .{}, &.{
5844 try astgen.errNoteNode(
5845 parent_gz.any_defer_node,
5846 "defer expression here",
5847 .{},
5848 ),
5849 });
5850 }
5851
5852 // Ensure debug line/column information is emitted for this try expression.
5853 // Then we will save the line/column so that we can emit another one that goes
5854 // "backwards" because we want to evaluate the operand, but then put the debug
5855 // info back at the try keyword for error return tracing.
5856 if (!parent_gz.is_comptime) {
5857 try emitDbgNode(parent_gz, node);
5858 }
5859 const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
5860
5861 const operand_ri: ResultInfo = switch (ri.rl) {
5862 .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = .error_handling_expr },
5863 else => .{ .rl = .none, .ctx = .error_handling_expr },
5864 };
5865 // This could be a pointer or value depending on the `ri` parameter.
5866 const operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, node);
5867 const block_tag: Zir.Inst.Tag = if (operand_ri.rl == .ref) .try_ptr else .@"try";
5868 const try_inst = try parent_gz.makeBlockInst(block_tag, node);
5869 try parent_gz.instructions.append(astgen.gpa, try_inst);
5870
5871 var else_scope = parent_gz.makeSubBlock(scope);
5872 defer else_scope.unstack();
5873
5874 const err_tag = switch (ri.rl) {
5875 .ref, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr,
5876 else => Zir.Inst.Tag.err_union_code,
5877 };
5878 const err_code = try else_scope.addUnNode(err_tag, operand, node);
5879 try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
5880 try emitDbgStmt(&else_scope, try_lc);
5881 _ = try else_scope.addUnNode(.ret_node, err_code, node);
5882
5883 try else_scope.setTryBody(try_inst, operand);
5884 const result = try_inst.toRef();
5885 switch (ri.rl) {
5886 .ref, .ref_coerced_ty => return result,
5887 else => return rvalue(parent_gz, ri, result, node),
5888 }
5889}
5890
5891fn orelseCatchExpr(
5892 parent_gz: *GenZir,
5893 scope: *Scope,
5894 ri: ResultInfo,
5895 node: Ast.Node.Index,
5896 lhs: Ast.Node.Index,
5897 cond_op: Zir.Inst.Tag,
5898 unwrap_op: Zir.Inst.Tag,
5899 unwrap_code_op: Zir.Inst.Tag,
5900 rhs: Ast.Node.Index,
5901 payload_token: ?Ast.TokenIndex,
5902) InnerError!Zir.Inst.Ref {
5903 const astgen = parent_gz.astgen;
5904 const tree = astgen.tree;
5905
5906 const need_rl = astgen.nodes_need_rl.contains(node);
5907 const block_ri: ResultInfo = if (need_rl) ri else .{
5908 .rl = switch (ri.rl) {
5909 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
5910 .inferred_ptr => .none,
5911 else => ri.rl,
5912 },
5913 .ctx = ri.ctx,
5914 };
5915 // We need to call `rvalue` to write through to the pointer only if we had a
5916 // result pointer and aren't forwarding it.
5917 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
5918 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
5919
5920 const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr);
5921
5922 var block_scope = parent_gz.makeSubBlock(scope);
5923 block_scope.setBreakResultInfo(block_ri);
5924 defer block_scope.unstack();
5925
5926 const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) {
5927 .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none },
5928 else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none },
5929 };
5930 // This could be a pointer or value depending on the `operand_ri` parameter.
5931 // We cannot use `block_scope.break_result_info` because that has the bare
5932 // type, whereas this expression has the optional type. Later we make
5933 // up for this fact by calling rvalue on the else branch.
5934 const operand = try reachableExpr(&block_scope, &block_scope.base, operand_ri, lhs, rhs);
5935 const cond = try block_scope.addUnNode(cond_op, operand, node);
5936 const condbr = try block_scope.addCondBr(.condbr, node);
5937
5938 const block = try parent_gz.makeBlockInst(.block, node);
5939 try block_scope.setBlockBody(block);
5940 // block_scope unstacked now, can add new instructions to parent_gz
5941 try parent_gz.instructions.append(astgen.gpa, block);
5942
5943 var then_scope = block_scope.makeSubBlock(scope);
5944 defer then_scope.unstack();
5945
5946 // This could be a pointer or value depending on `unwrap_op`.
5947 const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
5948 const then_result = switch (ri.rl) {
5949 .ref, .ref_coerced_ty => unwrapped_payload,
5950 else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node),
5951 };
5952 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node);
5953
5954 var else_scope = block_scope.makeSubBlock(scope);
5955 defer else_scope.unstack();
5956
5957 // We know that the operand (almost certainly) modified the error return trace,
5958 // so signal to Sema that it should save the new index for restoring later.
5959 if (do_err_trace and nodeMayAppendToErrorTrace(tree, lhs))
5960 _ = try else_scope.addSaveErrRetIndex(.always);
5961
5962 var err_val_scope: Scope.LocalVal = undefined;
5963 const else_sub_scope = blk: {
5964 const payload = payload_token orelse break :blk &else_scope.base;
5965 const err_str = tree.tokenSlice(payload);
5966 if (mem.eql(u8, err_str, "_")) {
5967 return astgen.failTok(payload, "discard of error capture; omit it instead", .{});
5968 }
5969 const err_name = try astgen.identAsString(payload);
5970
5971 try astgen.detectLocalShadowing(scope, err_name, payload, err_str, .capture);
5972
5973 err_val_scope = .{
5974 .parent = &else_scope.base,
5975 .gen_zir = &else_scope,
5976 .name = err_name,
5977 .inst = try else_scope.addUnNode(unwrap_code_op, operand, node),
5978 .token_src = payload,
5979 .id_cat = .capture,
5980 };
5981 break :blk &err_val_scope.base;
5982 };
5983
5984 const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs);
5985 if (!else_scope.endsWithNoReturn()) {
5986 // As our last action before the break, "pop" the error trace if needed
5987 if (do_err_trace)
5988 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, rhs, else_result);
5989
5990 _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, rhs);
5991 }
5992 try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
5993
5994 try setCondBrPayload(condbr, cond, &then_scope, &else_scope);
5995
5996 if (need_result_rvalue) {
5997 return rvalue(parent_gz, ri, block.toRef(), node);
5998 } else {
5999 return block.toRef();
6000 }
6001}
6002
6003/// Return whether the identifier names of two tokens are equal. Resolves @""
6004/// tokens without allocating.
6005/// OK in theory it could do it without allocating. This implementation
6006/// allocates when the @"" form is used.
6007fn tokenIdentEql(astgen: *AstGen, token1: Ast.TokenIndex, token2: Ast.TokenIndex) !bool {
6008 const ident_name_1 = try astgen.identifierTokenString(token1);
6009 const ident_name_2 = try astgen.identifierTokenString(token2);
6010 return mem.eql(u8, ident_name_1, ident_name_2);
6011}
6012
6013fn fieldAccess(
6014 gz: *GenZir,
6015 scope: *Scope,
6016 ri: ResultInfo,
6017 node: Ast.Node.Index,
6018) InnerError!Zir.Inst.Ref {
6019 switch (ri.rl) {
6020 .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node),
6021 else => {
6022 const access = try addFieldAccess(.field_val, gz, scope, .{ .rl = .none }, node);
6023 return rvalue(gz, ri, access, node);
6024 },
6025 }
6026}
6027
6028fn addFieldAccess(
6029 tag: Zir.Inst.Tag,
6030 gz: *GenZir,
6031 scope: *Scope,
6032 lhs_ri: ResultInfo,
6033 node: Ast.Node.Index,
6034) InnerError!Zir.Inst.Ref {
6035 const astgen = gz.astgen;
6036 const tree = astgen.tree;
6037 const main_tokens = tree.nodes.items(.main_token);
6038 const node_datas = tree.nodes.items(.data);
6039
6040 const object_node = node_datas[node].lhs;
6041 const dot_token = main_tokens[node];
6042 const field_ident = dot_token + 1;
6043 const str_index = try astgen.identAsString(field_ident);
6044 const lhs = try expr(gz, scope, lhs_ri, object_node);
6045
6046 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
6047 try emitDbgStmt(gz, cursor);
6048
6049 return gz.addPlNode(tag, node, Zir.Inst.Field{
6050 .lhs = lhs,
6051 .field_name_start = str_index,
6052 });
6053}
6054
6055fn arrayAccess(
6056 gz: *GenZir,
6057 scope: *Scope,
6058 ri: ResultInfo,
6059 node: Ast.Node.Index,
6060) InnerError!Zir.Inst.Ref {
6061 const tree = gz.astgen.tree;
6062 const node_datas = tree.nodes.items(.data);
6063 switch (ri.rl) {
6064 .ref, .ref_coerced_ty => {
6065 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
6066
6067 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
6068
6069 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
6070 try emitDbgStmt(gz, cursor);
6071
6072 return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
6073 },
6074 else => {
6075 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
6076
6077 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
6078
6079 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
6080 try emitDbgStmt(gz, cursor);
6081
6082 return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node);
6083 },
6084 }
6085}
6086
6087fn simpleBinOp(
6088 gz: *GenZir,
6089 scope: *Scope,
6090 ri: ResultInfo,
6091 node: Ast.Node.Index,
6092 op_inst_tag: Zir.Inst.Tag,
6093) InnerError!Zir.Inst.Ref {
6094 const astgen = gz.astgen;
6095 const tree = astgen.tree;
6096 const node_datas = tree.nodes.items(.data);
6097
6098 if (op_inst_tag == .cmp_neq or op_inst_tag == .cmp_eq) {
6099 const node_tags = tree.nodes.items(.tag);
6100 const str = if (op_inst_tag == .cmp_eq) "==" else "!=";
6101 if (node_tags[node_datas[node].lhs] == .string_literal or
6102 node_tags[node_datas[node].rhs] == .string_literal)
6103 return astgen.failNode(node, "cannot compare strings with {s}", .{str});
6104 }
6105
6106 const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node);
6107 const cursor = switch (op_inst_tag) {
6108 .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, node),
6109 else => undefined,
6110 };
6111 const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].rhs, node);
6112
6113 switch (op_inst_tag) {
6114 .add, .sub, .mul, .div, .mod_rem => {
6115 try emitDbgStmt(gz, cursor);
6116 },
6117 else => {},
6118 }
6119 const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
6120 return rvalue(gz, ri, result, node);
6121}
6122
6123fn simpleStrTok(
6124 gz: *GenZir,
6125 ri: ResultInfo,
6126 ident_token: Ast.TokenIndex,
6127 node: Ast.Node.Index,
6128 op_inst_tag: Zir.Inst.Tag,
6129) InnerError!Zir.Inst.Ref {
6130 const astgen = gz.astgen;
6131 const str_index = try astgen.identAsString(ident_token);
6132 const result = try gz.addStrTok(op_inst_tag, str_index, ident_token);
6133 return rvalue(gz, ri, result, node);
6134}
6135
6136fn boolBinOp(
6137 gz: *GenZir,
6138 scope: *Scope,
6139 ri: ResultInfo,
6140 node: Ast.Node.Index,
6141 zir_tag: Zir.Inst.Tag,
6142) InnerError!Zir.Inst.Ref {
6143 const astgen = gz.astgen;
6144 const tree = astgen.tree;
6145 const node_datas = tree.nodes.items(.data);
6146
6147 const lhs = try expr(gz, scope, coerced_bool_ri, node_datas[node].lhs);
6148 const bool_br = (try gz.addPlNodePayloadIndex(zir_tag, node, undefined)).toIndex().?;
6149
6150 var rhs_scope = gz.makeSubBlock(scope);
6151 defer rhs_scope.unstack();
6152 const rhs = try expr(&rhs_scope, &rhs_scope.base, coerced_bool_ri, node_datas[node].rhs);
6153 if (!gz.refIsNoReturn(rhs)) {
6154 _ = try rhs_scope.addBreakWithSrcNode(.break_inline, bool_br, rhs, node_datas[node].rhs);
6155 }
6156 try rhs_scope.setBoolBrBody(bool_br, lhs);
6157
6158 const block_ref = bool_br.toRef();
6159 return rvalue(gz, ri, block_ref, node);
6160}
6161
6162fn ifExpr(
6163 parent_gz: *GenZir,
6164 scope: *Scope,
6165 ri: ResultInfo,
6166 node: Ast.Node.Index,
6167 if_full: Ast.full.If,
6168) InnerError!Zir.Inst.Ref {
6169 const astgen = parent_gz.astgen;
6170 const tree = astgen.tree;
6171 const token_tags = tree.tokens.items(.tag);
6172
6173 const do_err_trace = astgen.fn_block != null and if_full.error_token != null;
6174
6175 const need_rl = astgen.nodes_need_rl.contains(node);
6176 const block_ri: ResultInfo = if (need_rl) ri else .{
6177 .rl = switch (ri.rl) {
6178 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
6179 .inferred_ptr => .none,
6180 else => ri.rl,
6181 },
6182 .ctx = ri.ctx,
6183 };
6184 // We need to call `rvalue` to write through to the pointer only if we had a
6185 // result pointer and aren't forwarding it.
6186 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
6187 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
6188
6189 var block_scope = parent_gz.makeSubBlock(scope);
6190 block_scope.setBreakResultInfo(block_ri);
6191 defer block_scope.unstack();
6192
6193 const payload_is_ref = if (if_full.payload_token) |payload_token|
6194 token_tags[payload_token] == .asterisk
6195 else
6196 false;
6197
6198 try emitDbgNode(parent_gz, if_full.ast.cond_expr);
6199 const cond: struct {
6200 inst: Zir.Inst.Ref,
6201 bool_bit: Zir.Inst.Ref,
6202 } = c: {
6203 if (if_full.error_token) |_| {
6204 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none, .ctx = .error_handling_expr };
6205 const err_union = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
6206 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
6207 break :c .{
6208 .inst = err_union,
6209 .bool_bit = try block_scope.addUnNode(tag, err_union, if_full.ast.cond_expr),
6210 };
6211 } else if (if_full.payload_token) |_| {
6212 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
6213 const optional = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
6214 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
6215 break :c .{
6216 .inst = optional,
6217 .bool_bit = try block_scope.addUnNode(tag, optional, if_full.ast.cond_expr),
6218 };
6219 } else {
6220 const cond = try expr(&block_scope, &block_scope.base, coerced_bool_ri, if_full.ast.cond_expr);
6221 break :c .{
6222 .inst = cond,
6223 .bool_bit = cond,
6224 };
6225 }
6226 };
6227
6228 const condbr = try block_scope.addCondBr(.condbr, node);
6229
6230 const block = try parent_gz.makeBlockInst(.block, node);
6231 try block_scope.setBlockBody(block);
6232 // block_scope unstacked now, can add new instructions to parent_gz
6233 try parent_gz.instructions.append(astgen.gpa, block);
6234
6235 var then_scope = parent_gz.makeSubBlock(scope);
6236 defer then_scope.unstack();
6237
6238 var payload_val_scope: Scope.LocalVal = undefined;
6239
6240 const then_node = if_full.ast.then_expr;
6241 const then_sub_scope = s: {
6242 if (if_full.error_token != null) {
6243 if (if_full.payload_token) |payload_token| {
6244 const tag: Zir.Inst.Tag = if (payload_is_ref)
6245 .err_union_payload_unsafe_ptr
6246 else
6247 .err_union_payload_unsafe;
6248 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node);
6249 const token_name_index = payload_token + @intFromBool(payload_is_ref);
6250 const ident_name = try astgen.identAsString(token_name_index);
6251 const token_name_str = tree.tokenSlice(token_name_index);
6252 if (mem.eql(u8, "_", token_name_str))
6253 break :s &then_scope.base;
6254 try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str, .capture);
6255 payload_val_scope = .{
6256 .parent = &then_scope.base,
6257 .gen_zir = &then_scope,
6258 .name = ident_name,
6259 .inst = payload_inst,
6260 .token_src = token_name_index,
6261 .id_cat = .capture,
6262 };
6263 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
6264 break :s &payload_val_scope.base;
6265 } else {
6266 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
6267 break :s &then_scope.base;
6268 }
6269 } else if (if_full.payload_token) |payload_token| {
6270 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
6271 const tag: Zir.Inst.Tag = if (payload_is_ref)
6272 .optional_payload_unsafe_ptr
6273 else
6274 .optional_payload_unsafe;
6275 const ident_bytes = tree.tokenSlice(ident_token);
6276 if (mem.eql(u8, "_", ident_bytes))
6277 break :s &then_scope.base;
6278 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node);
6279 const ident_name = try astgen.identAsString(ident_token);
6280 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
6281 payload_val_scope = .{
6282 .parent = &then_scope.base,
6283 .gen_zir = &then_scope,
6284 .name = ident_name,
6285 .inst = payload_inst,
6286 .token_src = ident_token,
6287 .id_cat = .capture,
6288 };
6289 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
6290 break :s &payload_val_scope.base;
6291 } else {
6292 break :s &then_scope.base;
6293 }
6294 };
6295
6296 const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node);
6297 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
6298 if (!then_scope.endsWithNoReturn()) {
6299 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node);
6300 }
6301
6302 var else_scope = parent_gz.makeSubBlock(scope);
6303 defer else_scope.unstack();
6304
6305 // We know that the operand (almost certainly) modified the error return trace,
6306 // so signal to Sema that it should save the new index for restoring later.
6307 if (do_err_trace and nodeMayAppendToErrorTrace(tree, if_full.ast.cond_expr))
6308 _ = try else_scope.addSaveErrRetIndex(.always);
6309
6310 const else_node = if_full.ast.else_expr;
6311 if (else_node != 0) {
6312 const sub_scope = s: {
6313 if (if_full.error_token) |error_token| {
6314 const tag: Zir.Inst.Tag = if (payload_is_ref)
6315 .err_union_code_ptr
6316 else
6317 .err_union_code;
6318 const payload_inst = try else_scope.addUnNode(tag, cond.inst, if_full.ast.cond_expr);
6319 const ident_name = try astgen.identAsString(error_token);
6320 const error_token_str = tree.tokenSlice(error_token);
6321 if (mem.eql(u8, "_", error_token_str))
6322 break :s &else_scope.base;
6323 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str, .capture);
6324 payload_val_scope = .{
6325 .parent = &else_scope.base,
6326 .gen_zir = &else_scope,
6327 .name = ident_name,
6328 .inst = payload_inst,
6329 .token_src = error_token,
6330 .id_cat = .capture,
6331 };
6332 try else_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
6333 break :s &payload_val_scope.base;
6334 } else {
6335 break :s &else_scope.base;
6336 }
6337 };
6338 const else_result = try expr(&else_scope, sub_scope, block_scope.break_result_info, else_node);
6339 if (!else_scope.endsWithNoReturn()) {
6340 // As our last action before the break, "pop" the error trace if needed
6341 if (do_err_trace)
6342 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, else_node, else_result);
6343 _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, else_node);
6344 }
6345 try checkUsed(parent_gz, &else_scope.base, sub_scope);
6346 } else {
6347 const result = try rvalue(&else_scope, ri, .void_value, node);
6348 _ = try else_scope.addBreak(.@"break", block, result);
6349 }
6350
6351 try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
6352
6353 if (need_result_rvalue) {
6354 return rvalue(parent_gz, ri, block.toRef(), node);
6355 } else {
6356 return block.toRef();
6357 }
6358}
6359
6360/// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
6361fn setCondBrPayload(
6362 condbr: Zir.Inst.Index,
6363 cond: Zir.Inst.Ref,
6364 then_scope: *GenZir,
6365 else_scope: *GenZir,
6366) !void {
6367 defer then_scope.unstack();
6368 defer else_scope.unstack();
6369 const astgen = then_scope.astgen;
6370 const then_body = then_scope.instructionsSliceUpto(else_scope);
6371 const else_body = else_scope.instructionsSlice();
6372 const then_body_len = astgen.countBodyLenAfterFixups(then_body);
6373 const else_body_len = astgen.countBodyLenAfterFixups(else_body);
6374 try astgen.extra.ensureUnusedCapacity(
6375 astgen.gpa,
6376 @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len,
6377 );
6378
6379 const zir_datas = astgen.instructions.items(.data);
6380 zir_datas[@intFromEnum(condbr)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
6381 .condition = cond,
6382 .then_body_len = then_body_len,
6383 .else_body_len = else_body_len,
6384 });
6385 astgen.appendBodyWithFixups(then_body);
6386 astgen.appendBodyWithFixups(else_body);
6387}
6388
6389fn whileExpr(
6390 parent_gz: *GenZir,
6391 scope: *Scope,
6392 ri: ResultInfo,
6393 node: Ast.Node.Index,
6394 while_full: Ast.full.While,
6395 is_statement: bool,
6396) InnerError!Zir.Inst.Ref {
6397 const astgen = parent_gz.astgen;
6398 const tree = astgen.tree;
6399 const token_tags = tree.tokens.items(.tag);
6400
6401 const need_rl = astgen.nodes_need_rl.contains(node);
6402 const block_ri: ResultInfo = if (need_rl) ri else .{
6403 .rl = switch (ri.rl) {
6404 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
6405 .inferred_ptr => .none,
6406 else => ri.rl,
6407 },
6408 .ctx = ri.ctx,
6409 };
6410 // We need to call `rvalue` to write through to the pointer only if we had a
6411 // result pointer and aren't forwarding it.
6412 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
6413 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
6414
6415 if (while_full.label_token) |label_token| {
6416 try astgen.checkLabelRedefinition(scope, label_token);
6417 }
6418
6419 const is_inline = while_full.inline_token != null;
6420 if (parent_gz.is_comptime and is_inline) {
6421 return astgen.failTok(while_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
6422 }
6423 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
6424 const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
6425 try parent_gz.instructions.append(astgen.gpa, loop_block);
6426
6427 var loop_scope = parent_gz.makeSubBlock(scope);
6428 loop_scope.is_inline = is_inline;
6429 loop_scope.setBreakResultInfo(block_ri);
6430 defer loop_scope.unstack();
6431
6432 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
6433 defer cond_scope.unstack();
6434
6435 const payload_is_ref = if (while_full.payload_token) |payload_token|
6436 token_tags[payload_token] == .asterisk
6437 else
6438 false;
6439
6440 try emitDbgNode(parent_gz, while_full.ast.cond_expr);
6441 const cond: struct {
6442 inst: Zir.Inst.Ref,
6443 bool_bit: Zir.Inst.Ref,
6444 } = c: {
6445 if (while_full.error_token) |_| {
6446 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
6447 const err_union = try expr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr);
6448 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
6449 break :c .{
6450 .inst = err_union,
6451 .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.cond_expr),
6452 };
6453 } else if (while_full.payload_token) |_| {
6454 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
6455 const optional = try expr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr);
6456 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
6457 break :c .{
6458 .inst = optional,
6459 .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.cond_expr),
6460 };
6461 } else {
6462 const cond = try expr(&cond_scope, &cond_scope.base, coerced_bool_ri, while_full.ast.cond_expr);
6463 break :c .{
6464 .inst = cond,
6465 .bool_bit = cond,
6466 };
6467 }
6468 };
6469
6470 const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
6471 const condbr = try cond_scope.addCondBr(condbr_tag, node);
6472 const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
6473 const cond_block = try loop_scope.makeBlockInst(block_tag, node);
6474 try cond_scope.setBlockBody(cond_block);
6475 // cond_scope unstacked now, can add new instructions to loop_scope
6476 try loop_scope.instructions.append(astgen.gpa, cond_block);
6477
6478 // make scope now but don't stack on parent_gz until loop_scope
6479 // gets unstacked after cont_expr is emitted and added below
6480 var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
6481 then_scope.instructions_top = GenZir.unstacked_top;
6482 defer then_scope.unstack();
6483
6484 var dbg_var_name: Zir.NullTerminatedString = .empty;
6485 var dbg_var_inst: Zir.Inst.Ref = undefined;
6486 var opt_payload_inst: Zir.Inst.OptionalIndex = .none;
6487 var payload_val_scope: Scope.LocalVal = undefined;
6488 const then_sub_scope = s: {
6489 if (while_full.error_token != null) {
6490 if (while_full.payload_token) |payload_token| {
6491 const tag: Zir.Inst.Tag = if (payload_is_ref)
6492 .err_union_payload_unsafe_ptr
6493 else
6494 .err_union_payload_unsafe;
6495 // will add this instruction to then_scope.instructions below
6496 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
6497 opt_payload_inst = payload_inst.toOptional();
6498 const ident_token = payload_token + @intFromBool(payload_is_ref);
6499 const ident_bytes = tree.tokenSlice(ident_token);
6500 if (mem.eql(u8, "_", ident_bytes))
6501 break :s &then_scope.base;
6502 const ident_name = try astgen.identAsString(ident_token);
6503 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
6504 payload_val_scope = .{
6505 .parent = &then_scope.base,
6506 .gen_zir = &then_scope,
6507 .name = ident_name,
6508 .inst = payload_inst.toRef(),
6509 .token_src = ident_token,
6510 .id_cat = .capture,
6511 };
6512 dbg_var_name = ident_name;
6513 dbg_var_inst = payload_inst.toRef();
6514 break :s &payload_val_scope.base;
6515 } else {
6516 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
6517 break :s &then_scope.base;
6518 }
6519 } else if (while_full.payload_token) |payload_token| {
6520 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
6521 const tag: Zir.Inst.Tag = if (payload_is_ref)
6522 .optional_payload_unsafe_ptr
6523 else
6524 .optional_payload_unsafe;
6525 // will add this instruction to then_scope.instructions below
6526 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
6527 opt_payload_inst = payload_inst.toOptional();
6528 const ident_name = try astgen.identAsString(ident_token);
6529 const ident_bytes = tree.tokenSlice(ident_token);
6530 if (mem.eql(u8, "_", ident_bytes))
6531 break :s &then_scope.base;
6532 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
6533 payload_val_scope = .{
6534 .parent = &then_scope.base,
6535 .gen_zir = &then_scope,
6536 .name = ident_name,
6537 .inst = payload_inst.toRef(),
6538 .token_src = ident_token,
6539 .id_cat = .capture,
6540 };
6541 dbg_var_name = ident_name;
6542 dbg_var_inst = payload_inst.toRef();
6543 break :s &payload_val_scope.base;
6544 } else {
6545 break :s &then_scope.base;
6546 }
6547 };
6548
6549 var continue_scope = parent_gz.makeSubBlock(then_sub_scope);
6550 continue_scope.instructions_top = GenZir.unstacked_top;
6551 defer continue_scope.unstack();
6552 const continue_block = try then_scope.makeBlockInst(block_tag, node);
6553
6554 const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
6555 _ = try loop_scope.addNode(repeat_tag, node);
6556
6557 try loop_scope.setBlockBody(loop_block);
6558 loop_scope.break_block = loop_block.toOptional();
6559 loop_scope.continue_block = continue_block.toOptional();
6560 if (while_full.label_token) |label_token| {
6561 loop_scope.label = .{
6562 .token = label_token,
6563 .block_inst = loop_block,
6564 };
6565 }
6566
6567 // done adding instructions to loop_scope, can now stack then_scope
6568 then_scope.instructions_top = then_scope.instructions.items.len;
6569
6570 const then_node = while_full.ast.then_expr;
6571 if (opt_payload_inst.unwrap()) |payload_inst| {
6572 try then_scope.instructions.append(astgen.gpa, payload_inst);
6573 }
6574 if (dbg_var_name != .empty) try then_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
6575 try then_scope.instructions.append(astgen.gpa, continue_block);
6576 // This code could be improved to avoid emitting the continue expr when there
6577 // are no jumps to it. This happens when the last statement of a while body is noreturn
6578 // and there are no `continue` statements.
6579 // Tracking issue: https://github.com/ziglang/zig/issues/9185
6580 if (while_full.ast.cont_expr != 0) {
6581 _ = try unusedResultExpr(&then_scope, then_sub_scope, while_full.ast.cont_expr);
6582 }
6583
6584 continue_scope.instructions_top = continue_scope.instructions.items.len;
6585 _ = try unusedResultExpr(&continue_scope, &continue_scope.base, then_node);
6586 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
6587 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
6588 if (!continue_scope.endsWithNoReturn()) {
6589 _ = try continue_scope.addBreak(break_tag, continue_block, .void_value);
6590 }
6591 try continue_scope.setBlockBody(continue_block);
6592 _ = try then_scope.addBreak(break_tag, cond_block, .void_value);
6593
6594 var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
6595 defer else_scope.unstack();
6596
6597 const else_node = while_full.ast.else_expr;
6598 if (else_node != 0) {
6599 const sub_scope = s: {
6600 if (while_full.error_token) |error_token| {
6601 const tag: Zir.Inst.Tag = if (payload_is_ref)
6602 .err_union_code_ptr
6603 else
6604 .err_union_code;
6605 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, while_full.ast.cond_expr);
6606 const ident_name = try astgen.identAsString(error_token);
6607 const ident_bytes = tree.tokenSlice(error_token);
6608 if (mem.eql(u8, ident_bytes, "_"))
6609 break :s &else_scope.base;
6610 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes, .capture);
6611 payload_val_scope = .{
6612 .parent = &else_scope.base,
6613 .gen_zir = &else_scope,
6614 .name = ident_name,
6615 .inst = else_payload_inst,
6616 .token_src = error_token,
6617 .id_cat = .capture,
6618 };
6619 try else_scope.addDbgVar(.dbg_var_val, ident_name, else_payload_inst);
6620 break :s &payload_val_scope.base;
6621 } else {
6622 break :s &else_scope.base;
6623 }
6624 };
6625 // Remove the continue block and break block so that `continue` and `break`
6626 // control flow apply to outer loops; not this one.
6627 loop_scope.continue_block = .none;
6628 loop_scope.break_block = .none;
6629 const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
6630 if (is_statement) {
6631 _ = try addEnsureResult(&else_scope, else_result, else_node);
6632 }
6633
6634 try checkUsed(parent_gz, &else_scope.base, sub_scope);
6635 if (!else_scope.endsWithNoReturn()) {
6636 _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node);
6637 }
6638 } else {
6639 const result = try rvalue(&else_scope, ri, .void_value, node);
6640 _ = try else_scope.addBreak(break_tag, loop_block, result);
6641 }
6642
6643 if (loop_scope.label) |some| {
6644 if (!some.used) {
6645 try astgen.appendErrorTok(some.token, "unused while loop label", .{});
6646 }
6647 }
6648
6649 try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
6650
6651 const result = if (need_result_rvalue)
6652 try rvalue(parent_gz, ri, loop_block.toRef(), node)
6653 else
6654 loop_block.toRef();
6655
6656 if (is_statement) {
6657 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
6658 }
6659
6660 return result;
6661}
6662
6663fn forExpr(
6664 parent_gz: *GenZir,
6665 scope: *Scope,
6666 ri: ResultInfo,
6667 node: Ast.Node.Index,
6668 for_full: Ast.full.For,
6669 is_statement: bool,
6670) InnerError!Zir.Inst.Ref {
6671 const astgen = parent_gz.astgen;
6672
6673 if (for_full.label_token) |label_token| {
6674 try astgen.checkLabelRedefinition(scope, label_token);
6675 }
6676
6677 const need_rl = astgen.nodes_need_rl.contains(node);
6678 const block_ri: ResultInfo = if (need_rl) ri else .{
6679 .rl = switch (ri.rl) {
6680 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
6681 .inferred_ptr => .none,
6682 else => ri.rl,
6683 },
6684 .ctx = ri.ctx,
6685 };
6686 // We need to call `rvalue` to write through to the pointer only if we had a
6687 // result pointer and aren't forwarding it.
6688 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
6689 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
6690
6691 const is_inline = for_full.inline_token != null;
6692 if (parent_gz.is_comptime and is_inline) {
6693 return astgen.failTok(for_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
6694 }
6695 const tree = astgen.tree;
6696 const token_tags = tree.tokens.items(.tag);
6697 const node_tags = tree.nodes.items(.tag);
6698 const node_data = tree.nodes.items(.data);
6699 const gpa = astgen.gpa;
6700
6701 // For counters, this is the start value; for indexables, this is the base
6702 // pointer that can be used with elem_ptr and similar instructions.
6703 // Special value `none` means that this is a counter and its start value is
6704 // zero, indicating that the main index counter can be used directly.
6705 const indexables = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len);
6706 defer gpa.free(indexables);
6707 // elements of this array can be `none`, indicating no length check.
6708 const lens = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len);
6709 defer gpa.free(lens);
6710
6711 // We will use a single zero-based counter no matter how many indexables there are.
6712 const index_ptr = blk: {
6713 const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc;
6714 const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);
6715 // initialize to zero
6716 _ = try parent_gz.addPlNode(.store_node, node, Zir.Inst.Bin{
6717 .lhs = index_ptr,
6718 .rhs = .zero_usize,
6719 });
6720 break :blk index_ptr;
6721 };
6722
6723 var any_len_checks = false;
6724
6725 {
6726 var capture_token = for_full.payload_token;
6727 for (for_full.ast.inputs, indexables, lens) |input, *indexable_ref, *len_ref| {
6728 const capture_is_ref = token_tags[capture_token] == .asterisk;
6729 const ident_tok = capture_token + @intFromBool(capture_is_ref);
6730 const is_discard = mem.eql(u8, tree.tokenSlice(ident_tok), "_");
6731
6732 if (is_discard and capture_is_ref) {
6733 return astgen.failTok(capture_token, "pointer modifier invalid on discard", .{});
6734 }
6735 // Skip over the comma, and on to the next capture (or the ending pipe character).
6736 capture_token = ident_tok + 2;
6737
6738 try emitDbgNode(parent_gz, input);
6739 if (node_tags[input] == .for_range) {
6740 if (capture_is_ref) {
6741 return astgen.failTok(ident_tok, "cannot capture reference to range", .{});
6742 }
6743 const start_node = node_data[input].lhs;
6744 const start_val = try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, start_node);
6745
6746 const end_node = node_data[input].rhs;
6747 const end_val = if (end_node != 0)
6748 try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_data[input].rhs)
6749 else
6750 .none;
6751
6752 if (end_val == .none and is_discard) {
6753 return astgen.failTok(ident_tok, "discard of unbounded counter", .{});
6754 }
6755
6756 const start_is_zero = nodeIsTriviallyZero(tree, start_node);
6757 const range_len = if (end_val == .none or start_is_zero)
6758 end_val
6759 else
6760 try parent_gz.addPlNode(.sub, input, Zir.Inst.Bin{
6761 .lhs = end_val,
6762 .rhs = start_val,
6763 });
6764
6765 any_len_checks = any_len_checks or range_len != .none;
6766 indexable_ref.* = if (start_is_zero) .none else start_val;
6767 len_ref.* = range_len;
6768 } else {
6769 const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input);
6770
6771 any_len_checks = true;
6772 indexable_ref.* = indexable;
6773 len_ref.* = indexable;
6774 }
6775 }
6776 }
6777
6778 if (!any_len_checks) {
6779 return astgen.failNode(node, "unbounded for loop", .{});
6780 }
6781
6782 // We use a dedicated ZIR instruction to assert the lengths to assist with
6783 // nicer error reporting as well as fewer ZIR bytes emitted.
6784 const len: Zir.Inst.Ref = len: {
6785 const lens_len: u32 = @intCast(lens.len);
6786 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).Struct.fields.len + lens_len);
6787 const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{
6788 .operands_len = lens_len,
6789 });
6790 appendRefsAssumeCapacity(astgen, lens);
6791 break :len len;
6792 };
6793
6794 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
6795 const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
6796 try parent_gz.instructions.append(gpa, loop_block);
6797
6798 var loop_scope = parent_gz.makeSubBlock(scope);
6799 loop_scope.is_inline = is_inline;
6800 loop_scope.setBreakResultInfo(block_ri);
6801 defer loop_scope.unstack();
6802
6803 // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the
6804 // load must be removed from instructions in the meantime or it appears to be part of parent_gz.
6805 const index = try loop_scope.addUnNode(.load, index_ptr, node);
6806 _ = loop_scope.instructions.pop();
6807
6808 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
6809 defer cond_scope.unstack();
6810
6811 // Check the condition.
6812 const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{
6813 .lhs = index,
6814 .rhs = len,
6815 });
6816
6817 const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
6818 const condbr = try cond_scope.addCondBr(condbr_tag, node);
6819 const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
6820 const cond_block = try loop_scope.makeBlockInst(block_tag, node);
6821 try cond_scope.setBlockBody(cond_block);
6822
6823 loop_scope.break_block = loop_block.toOptional();
6824 loop_scope.continue_block = cond_block.toOptional();
6825 if (for_full.label_token) |label_token| {
6826 loop_scope.label = .{
6827 .token = label_token,
6828 .block_inst = loop_block,
6829 };
6830 }
6831
6832 const then_node = for_full.ast.then_expr;
6833 var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
6834 defer then_scope.unstack();
6835
6836 const capture_scopes = try gpa.alloc(Scope.LocalVal, for_full.ast.inputs.len);
6837 defer gpa.free(capture_scopes);
6838
6839 const then_sub_scope = blk: {
6840 var capture_token = for_full.payload_token;
6841 var capture_sub_scope: *Scope = &then_scope.base;
6842 for (for_full.ast.inputs, indexables, capture_scopes) |input, indexable_ref, *capture_scope| {
6843 const capture_is_ref = token_tags[capture_token] == .asterisk;
6844 const ident_tok = capture_token + @intFromBool(capture_is_ref);
6845 const capture_name = tree.tokenSlice(ident_tok);
6846 // Skip over the comma, and on to the next capture (or the ending pipe character).
6847 capture_token = ident_tok + 2;
6848
6849 if (mem.eql(u8, capture_name, "_")) continue;
6850
6851 const name_str_index = try astgen.identAsString(ident_tok);
6852 try astgen.detectLocalShadowing(capture_sub_scope, name_str_index, ident_tok, capture_name, .capture);
6853
6854 const capture_inst = inst: {
6855 const is_counter = node_tags[input] == .for_range;
6856
6857 if (indexable_ref == .none) {
6858 // Special case: the main index can be used directly.
6859 assert(is_counter);
6860 assert(!capture_is_ref);
6861 break :inst index;
6862 }
6863
6864 // For counters, we add the index variable to the start value; for
6865 // indexables, we use it as an element index. This is so similar
6866 // that they can share the same code paths, branching only on the
6867 // ZIR tag.
6868 const switch_cond = (@as(u2, @intFromBool(capture_is_ref)) << 1) | @intFromBool(is_counter);
6869 const tag: Zir.Inst.Tag = switch (switch_cond) {
6870 0b00 => .elem_val,
6871 0b01 => .add,
6872 0b10 => .elem_ptr,
6873 0b11 => unreachable, // compile error emitted already
6874 };
6875 break :inst try then_scope.addPlNode(tag, input, Zir.Inst.Bin{
6876 .lhs = indexable_ref,
6877 .rhs = index,
6878 });
6879 };
6880
6881 capture_scope.* = .{
6882 .parent = capture_sub_scope,
6883 .gen_zir = &then_scope,
6884 .name = name_str_index,
6885 .inst = capture_inst,
6886 .token_src = ident_tok,
6887 .id_cat = .capture,
6888 };
6889
6890 try then_scope.addDbgVar(.dbg_var_val, name_str_index, capture_inst);
6891 capture_sub_scope = &capture_scope.base;
6892 }
6893
6894 break :blk capture_sub_scope;
6895 };
6896
6897 const then_result = try expr(&then_scope, then_sub_scope, .{ .rl = .none }, then_node);
6898 _ = try addEnsureResult(&then_scope, then_result, then_node);
6899
6900 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
6901
6902 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
6903
6904 _ = try then_scope.addBreak(break_tag, cond_block, .void_value);
6905
6906 var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
6907 defer else_scope.unstack();
6908
6909 const else_node = for_full.ast.else_expr;
6910 if (else_node != 0) {
6911 const sub_scope = &else_scope.base;
6912 // Remove the continue block and break block so that `continue` and `break`
6913 // control flow apply to outer loops; not this one.
6914 loop_scope.continue_block = .none;
6915 loop_scope.break_block = .none;
6916 const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
6917 if (is_statement) {
6918 _ = try addEnsureResult(&else_scope, else_result, else_node);
6919 }
6920 if (!else_scope.endsWithNoReturn()) {
6921 _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node);
6922 }
6923 } else {
6924 const result = try rvalue(&else_scope, ri, .void_value, node);
6925 _ = try else_scope.addBreak(break_tag, loop_block, result);
6926 }
6927
6928 if (loop_scope.label) |some| {
6929 if (!some.used) {
6930 try astgen.appendErrorTok(some.token, "unused for loop label", .{});
6931 }
6932 }
6933
6934 try setCondBrPayload(condbr, cond, &then_scope, &else_scope);
6935
6936 // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it
6937 {
6938 loop_scope.instructions_top = loop_scope.instructions.items.len;
6939 try loop_scope.instructions.appendSlice(gpa, &.{ index.toIndex().?, cond_block });
6940
6941 // Increment the index variable.
6942 const index_plus_one = try loop_scope.addPlNode(.add_unsafe, node, Zir.Inst.Bin{
6943 .lhs = index,
6944 .rhs = .one_usize,
6945 });
6946 _ = try loop_scope.addPlNode(.store_node, node, Zir.Inst.Bin{
6947 .lhs = index_ptr,
6948 .rhs = index_plus_one,
6949 });
6950 const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
6951 _ = try loop_scope.addNode(repeat_tag, node);
6952
6953 try loop_scope.setBlockBody(loop_block);
6954 }
6955
6956 const result = if (need_result_rvalue)
6957 try rvalue(parent_gz, ri, loop_block.toRef(), node)
6958 else
6959 loop_block.toRef();
6960
6961 if (is_statement) {
6962 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
6963 }
6964 return result;
6965}
6966
6967fn switchExprErrUnion(
6968 parent_gz: *GenZir,
6969 scope: *Scope,
6970 ri: ResultInfo,
6971 catch_or_if_node: Ast.Node.Index,
6972 node_ty: enum { @"catch", @"if" },
6973) InnerError!Zir.Inst.Ref {
6974 const astgen = parent_gz.astgen;
6975 const gpa = astgen.gpa;
6976 const tree = astgen.tree;
6977 const node_datas = tree.nodes.items(.data);
6978 const node_tags = tree.nodes.items(.tag);
6979 const main_tokens = tree.nodes.items(.main_token);
6980 const token_tags = tree.tokens.items(.tag);
6981
6982 const if_full = switch (node_ty) {
6983 .@"catch" => undefined,
6984 .@"if" => tree.fullIf(catch_or_if_node).?,
6985 };
6986
6987 const switch_node, const operand_node, const error_payload = switch (node_ty) {
6988 .@"catch" => .{
6989 node_datas[catch_or_if_node].rhs,
6990 node_datas[catch_or_if_node].lhs,
6991 main_tokens[catch_or_if_node] + 2,
6992 },
6993 .@"if" => .{
6994 if_full.ast.else_expr,
6995 if_full.ast.cond_expr,
6996 if_full.error_token.?,
6997 },
6998 };
6999 assert(node_tags[switch_node] == .@"switch" or node_tags[switch_node] == .switch_comma);
7000
7001 const do_err_trace = astgen.fn_block != null;
7002
7003 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
7004 const case_nodes = tree.extra_data[extra.start..extra.end];
7005
7006 const need_rl = astgen.nodes_need_rl.contains(catch_or_if_node);
7007 const block_ri: ResultInfo = if (need_rl) ri else .{
7008 .rl = switch (ri.rl) {
7009 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, catch_or_if_node)).? },
7010 .inferred_ptr => .none,
7011 else => ri.rl,
7012 },
7013 .ctx = ri.ctx,
7014 };
7015
7016 const payload_is_ref = node_ty == .@"if" and
7017 if_full.payload_token != null and token_tags[if_full.payload_token.?] == .asterisk;
7018
7019 // We need to call `rvalue` to write through to the pointer only if we had a
7020 // result pointer and aren't forwarding it.
7021 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
7022 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
7023 var scalar_cases_len: u32 = 0;
7024 var multi_cases_len: u32 = 0;
7025 var inline_cases_len: u32 = 0;
7026 var has_else = false;
7027 var else_node: Ast.Node.Index = 0;
7028 var else_src: ?Ast.TokenIndex = null;
7029 for (case_nodes) |case_node| {
7030 const case = tree.fullSwitchCase(case_node).?;
7031
7032 if (case.ast.values.len == 0) {
7033 const case_src = case.ast.arrow_token - 1;
7034 if (else_src) |src| {
7035 return astgen.failTokNotes(
7036 case_src,
7037 "multiple else prongs in switch expression",
7038 .{},
7039 &[_]u32{
7040 try astgen.errNoteTok(
7041 src,
7042 "previous else prong here",
7043 .{},
7044 ),
7045 },
7046 );
7047 }
7048 has_else = true;
7049 else_node = case_node;
7050 else_src = case_src;
7051 continue;
7052 } else if (case.ast.values.len == 1 and
7053 node_tags[case.ast.values[0]] == .identifier and
7054 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
7055 {
7056 const case_src = case.ast.arrow_token - 1;
7057 return astgen.failTokNotes(
7058 case_src,
7059 "'_' prong is not allowed when switching on errors",
7060 .{},
7061 &[_]u32{
7062 try astgen.errNoteTok(
7063 case_src,
7064 "consider using 'else'",
7065 .{},
7066 ),
7067 },
7068 );
7069 }
7070
7071 for (case.ast.values) |val| {
7072 if (node_tags[val] == .string_literal)
7073 return astgen.failNode(val, "cannot switch on strings", .{});
7074 }
7075
7076 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) {
7077 scalar_cases_len += 1;
7078 } else {
7079 multi_cases_len += 1;
7080 }
7081 if (case.inline_token != null) {
7082 inline_cases_len += 1;
7083 }
7084 }
7085
7086 const operand_ri: ResultInfo = .{
7087 .rl = if (payload_is_ref) .ref else .none,
7088 .ctx = .error_handling_expr,
7089 };
7090
7091 astgen.advanceSourceCursorToNode(operand_node);
7092 const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
7093
7094 const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node);
7095 const item_ri: ResultInfo = .{ .rl = .none };
7096
7097 // This contains the data that goes into the `extra` array for the SwitchBlockErrUnion, except
7098 // the first cases_nodes.len slots are a table that indexes payloads later in the array,
7099 // with the non-error and else case indices coming first, then scalar_cases_len indexes, then
7100 // multi_cases_len indexes
7101 const payloads = &astgen.scratch;
7102 const scratch_top = astgen.scratch.items.len;
7103 const case_table_start = scratch_top;
7104 const scalar_case_table = case_table_start + 1 + @intFromBool(has_else);
7105 const multi_case_table = scalar_case_table + scalar_cases_len;
7106 const case_table_end = multi_case_table + multi_cases_len;
7107
7108 try astgen.scratch.resize(gpa, case_table_end);
7109 defer astgen.scratch.items.len = scratch_top;
7110
7111 var block_scope = parent_gz.makeSubBlock(scope);
7112 // block_scope not used for collecting instructions
7113 block_scope.instructions_top = GenZir.unstacked_top;
7114 block_scope.setBreakResultInfo(block_ri);
7115
7116 // Sema expects a dbg_stmt immediately before switch_block_err_union
7117 try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
7118 // This gets added to the parent block later, after the item expressions.
7119 const switch_block = try parent_gz.makeBlockInst(.switch_block_err_union, switch_node);
7120
7121 // We re-use this same scope for all cases, including the special prong, if any.
7122 var case_scope = parent_gz.makeSubBlock(&block_scope.base);
7123 case_scope.instructions_top = GenZir.unstacked_top;
7124
7125 {
7126 const body_len_index: u32 = @intCast(payloads.items.len);
7127 payloads.items[case_table_start] = body_len_index;
7128 try payloads.resize(gpa, body_len_index + 1); // body_len
7129
7130 case_scope.instructions_top = parent_gz.instructions.items.len;
7131 defer case_scope.unstack();
7132
7133 const unwrap_payload_tag: Zir.Inst.Tag = if (payload_is_ref)
7134 .err_union_payload_unsafe_ptr
7135 else
7136 .err_union_payload_unsafe;
7137
7138 const unwrapped_payload = try case_scope.addUnNode(
7139 unwrap_payload_tag,
7140 raw_operand,
7141 catch_or_if_node,
7142 );
7143
7144 switch (node_ty) {
7145 .@"catch" => {
7146 const case_result = switch (ri.rl) {
7147 .ref, .ref_coerced_ty => unwrapped_payload,
7148 else => try rvalue(
7149 &case_scope,
7150 block_scope.break_result_info,
7151 unwrapped_payload,
7152 catch_or_if_node,
7153 ),
7154 };
7155 _ = try case_scope.addBreakWithSrcNode(
7156 .@"break",
7157 switch_block,
7158 case_result,
7159 catch_or_if_node,
7160 );
7161 },
7162 .@"if" => {
7163 var payload_val_scope: Scope.LocalVal = undefined;
7164
7165 const then_node = if_full.ast.then_expr;
7166 const then_sub_scope = s: {
7167 assert(if_full.error_token != null);
7168 if (if_full.payload_token) |payload_token| {
7169 const token_name_index = payload_token + @intFromBool(payload_is_ref);
7170 const ident_name = try astgen.identAsString(token_name_index);
7171 const token_name_str = tree.tokenSlice(token_name_index);
7172 if (mem.eql(u8, "_", token_name_str))
7173 break :s &case_scope.base;
7174 try astgen.detectLocalShadowing(
7175 &case_scope.base,
7176 ident_name,
7177 token_name_index,
7178 token_name_str,
7179 .capture,
7180 );
7181 payload_val_scope = .{
7182 .parent = &case_scope.base,
7183 .gen_zir = &case_scope,
7184 .name = ident_name,
7185 .inst = unwrapped_payload,
7186 .token_src = token_name_index,
7187 .id_cat = .capture,
7188 };
7189 try case_scope.addDbgVar(.dbg_var_val, ident_name, unwrapped_payload);
7190 break :s &payload_val_scope.base;
7191 } else {
7192 _ = try case_scope.addUnNode(
7193 .ensure_err_union_payload_void,
7194 raw_operand,
7195 catch_or_if_node,
7196 );
7197 break :s &case_scope.base;
7198 }
7199 };
7200 const then_result = try expr(
7201 &case_scope,
7202 then_sub_scope,
7203 block_scope.break_result_info,
7204 then_node,
7205 );
7206 try checkUsed(parent_gz, &case_scope.base, then_sub_scope);
7207 if (!case_scope.endsWithNoReturn()) {
7208 _ = try case_scope.addBreakWithSrcNode(
7209 .@"break",
7210 switch_block,
7211 then_result,
7212 then_node,
7213 );
7214 }
7215 },
7216 }
7217
7218 const case_slice = case_scope.instructionsSlice();
7219 // Since we use the switch_block_err_union instruction itself to refer
7220 // to the capture, which will not be added to the child block, we need
7221 // to handle ref_table manually.
7222 const refs_len = refs: {
7223 var n: usize = 0;
7224 var check_inst = switch_block;
7225 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7226 n += 1;
7227 check_inst = ref_inst;
7228 }
7229 break :refs n;
7230 };
7231 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
7232 try payloads.ensureUnusedCapacity(gpa, body_len);
7233 const capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = switch (node_ty) {
7234 .@"catch" => .none,
7235 .@"if" => if (if_full.payload_token == null)
7236 .none
7237 else if (payload_is_ref)
7238 .by_ref
7239 else
7240 .by_val,
7241 };
7242 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
7243 .body_len = @intCast(body_len),
7244 .capture = capture,
7245 .is_inline = false,
7246 .has_tag_capture = false,
7247 });
7248 if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
7249 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7250 }
7251 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
7252 }
7253
7254 const err_name = blk: {
7255 const err_str = tree.tokenSlice(error_payload);
7256 if (mem.eql(u8, err_str, "_")) {
7257 return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{});
7258 }
7259 const err_name = try astgen.identAsString(error_payload);
7260 try astgen.detectLocalShadowing(scope, err_name, error_payload, err_str, .capture);
7261
7262 break :blk err_name;
7263 };
7264
7265 // allocate a shared dummy instruction for the error capture
7266 const err_inst = err_inst: {
7267 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
7268 try astgen.instructions.append(astgen.gpa, .{
7269 .tag = .extended,
7270 .data = .{ .extended = .{
7271 .opcode = .value_placeholder,
7272 .small = undefined,
7273 .operand = undefined,
7274 } },
7275 });
7276 break :err_inst inst;
7277 };
7278
7279 // In this pass we generate all the item and prong expressions for error cases.
7280 var multi_case_index: u32 = 0;
7281 var scalar_case_index: u32 = 0;
7282 var any_uses_err_capture = false;
7283 for (case_nodes) |case_node| {
7284 const case = tree.fullSwitchCase(case_node).?;
7285
7286 const is_multi_case = case.ast.values.len > 1 or
7287 (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
7288
7289 var dbg_var_name: Zir.NullTerminatedString = .empty;
7290 var dbg_var_inst: Zir.Inst.Ref = undefined;
7291 var err_scope: Scope.LocalVal = undefined;
7292 var capture_scope: Scope.LocalVal = undefined;
7293
7294 const sub_scope = blk: {
7295 err_scope = .{
7296 .parent = &case_scope.base,
7297 .gen_zir = &case_scope,
7298 .name = err_name,
7299 .inst = err_inst.toRef(),
7300 .token_src = error_payload,
7301 .id_cat = .capture,
7302 };
7303
7304 const capture_token = case.payload_token orelse break :blk &err_scope.base;
7305 if (token_tags[capture_token] != .identifier) {
7306 return astgen.failTok(capture_token + 1, "error set cannot be captured by reference", .{});
7307 }
7308
7309 const capture_slice = tree.tokenSlice(capture_token);
7310 if (mem.eql(u8, capture_slice, "_")) {
7311 return astgen.failTok(capture_token, "discard of error capture; omit it instead", .{});
7312 }
7313 const tag_name = try astgen.identAsString(capture_token);
7314 try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture);
7315
7316 capture_scope = .{
7317 .parent = &case_scope.base,
7318 .gen_zir = &case_scope,
7319 .name = tag_name,
7320 .inst = switch_block.toRef(),
7321 .token_src = capture_token,
7322 .id_cat = .capture,
7323 };
7324 dbg_var_name = tag_name;
7325 dbg_var_inst = switch_block.toRef();
7326
7327 err_scope.parent = &capture_scope.base;
7328
7329 break :blk &err_scope.base;
7330 };
7331
7332 const header_index: u32 = @intCast(payloads.items.len);
7333 const body_len_index = if (is_multi_case) blk: {
7334 payloads.items[multi_case_table + multi_case_index] = header_index;
7335 multi_case_index += 1;
7336 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
7337
7338 // items
7339 var items_len: u32 = 0;
7340 for (case.ast.values) |item_node| {
7341 if (node_tags[item_node] == .switch_range) continue;
7342 items_len += 1;
7343
7344 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
7345 try payloads.append(gpa, @intFromEnum(item_inst));
7346 }
7347
7348 // ranges
7349 var ranges_len: u32 = 0;
7350 for (case.ast.values) |range| {
7351 if (node_tags[range] != .switch_range) continue;
7352 ranges_len += 1;
7353
7354 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs);
7355 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs);
7356 try payloads.appendSlice(gpa, &[_]u32{
7357 @intFromEnum(first), @intFromEnum(last),
7358 });
7359 }
7360
7361 payloads.items[header_index] = items_len;
7362 payloads.items[header_index + 1] = ranges_len;
7363 break :blk header_index + 2;
7364 } else if (case_node == else_node) blk: {
7365 payloads.items[case_table_start + 1] = header_index;
7366 try payloads.resize(gpa, header_index + 1); // body_len
7367 break :blk header_index;
7368 } else blk: {
7369 payloads.items[scalar_case_table + scalar_case_index] = header_index;
7370 scalar_case_index += 1;
7371 try payloads.resize(gpa, header_index + 2); // item, body_len
7372 const item_node = case.ast.values[0];
7373 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
7374 payloads.items[header_index] = @intFromEnum(item_inst);
7375 break :blk header_index + 1;
7376 };
7377
7378 {
7379 // temporarily stack case_scope on parent_gz
7380 case_scope.instructions_top = parent_gz.instructions.items.len;
7381 defer case_scope.unstack();
7382
7383 if (do_err_trace and nodeMayAppendToErrorTrace(tree, operand_node))
7384 _ = try case_scope.addSaveErrRetIndex(.always);
7385
7386 if (dbg_var_name != .empty) {
7387 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
7388 }
7389
7390 const target_expr_node = case.ast.target_expr;
7391 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node);
7392 // check capture_scope, not err_scope to avoid false positive unused error capture
7393 try checkUsed(parent_gz, &case_scope.base, err_scope.parent);
7394 const uses_err = err_scope.used != 0 or err_scope.discarded != 0;
7395 if (uses_err) {
7396 try case_scope.addDbgVar(.dbg_var_val, err_name, err_inst.toRef());
7397 any_uses_err_capture = true;
7398 }
7399
7400 if (!parent_gz.refIsNoReturn(case_result)) {
7401 if (do_err_trace)
7402 try restoreErrRetIndex(
7403 &case_scope,
7404 .{ .block = switch_block },
7405 block_scope.break_result_info,
7406 target_expr_node,
7407 case_result,
7408 );
7409
7410 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
7411 }
7412
7413 const case_slice = case_scope.instructionsSlice();
7414 // Since we use the switch_block_err_union instruction itself to refer
7415 // to the capture, which will not be added to the child block, we need
7416 // to handle ref_table manually.
7417 const refs_len = refs: {
7418 var n: usize = 0;
7419 var check_inst = switch_block;
7420 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7421 n += 1;
7422 check_inst = ref_inst;
7423 }
7424 if (uses_err) {
7425 check_inst = err_inst;
7426 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7427 n += 1;
7428 check_inst = ref_inst;
7429 }
7430 }
7431 break :refs n;
7432 };
7433 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
7434 try payloads.ensureUnusedCapacity(gpa, body_len);
7435 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
7436 .body_len = @intCast(body_len),
7437 .capture = if (case.payload_token != null) .by_val else .none,
7438 .is_inline = case.inline_token != null,
7439 .has_tag_capture = false,
7440 });
7441 if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
7442 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7443 }
7444 if (uses_err) {
7445 if (astgen.ref_table.fetchRemove(err_inst)) |kv| {
7446 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7447 }
7448 }
7449 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
7450 }
7451 }
7452 // Now that the item expressions are generated we can add this.
7453 try parent_gz.instructions.append(gpa, switch_block);
7454
7455 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlockErrUnion).Struct.fields.len +
7456 @intFromBool(multi_cases_len != 0) +
7457 payloads.items.len - case_table_end +
7458 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).Struct.fields.len);
7459
7460 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlockErrUnion{
7461 .operand = raw_operand,
7462 .bits = Zir.Inst.SwitchBlockErrUnion.Bits{
7463 .has_multi_cases = multi_cases_len != 0,
7464 .has_else = has_else,
7465 .scalar_cases_len = @intCast(scalar_cases_len),
7466 .any_uses_err_capture = any_uses_err_capture,
7467 .payload_is_ref = payload_is_ref,
7468 },
7469 .main_src_node_offset = parent_gz.nodeIndexToRelative(catch_or_if_node),
7470 });
7471
7472 if (multi_cases_len != 0) {
7473 astgen.extra.appendAssumeCapacity(multi_cases_len);
7474 }
7475
7476 if (any_uses_err_capture) {
7477 astgen.extra.appendAssumeCapacity(@intFromEnum(err_inst));
7478 }
7479
7480 const zir_datas = astgen.instructions.items(.data);
7481 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
7482
7483 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
7484 var body_len_index = start_index;
7485 var end_index = start_index;
7486 const table_index = case_table_start + i;
7487 if (table_index < scalar_case_table) {
7488 end_index += 1;
7489 } else if (table_index < multi_case_table) {
7490 body_len_index += 1;
7491 end_index += 2;
7492 } else {
7493 body_len_index += 2;
7494 const items_len = payloads.items[start_index];
7495 const ranges_len = payloads.items[start_index + 1];
7496 end_index += 3 + items_len + 2 * ranges_len;
7497 }
7498 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
7499 end_index += prong_info.body_len;
7500 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
7501 }
7502
7503 if (need_result_rvalue) {
7504 return rvalue(parent_gz, ri, switch_block.toRef(), switch_node);
7505 } else {
7506 return switch_block.toRef();
7507 }
7508}
7509
7510fn switchExpr(
7511 parent_gz: *GenZir,
7512 scope: *Scope,
7513 ri: ResultInfo,
7514 switch_node: Ast.Node.Index,
7515) InnerError!Zir.Inst.Ref {
7516 const astgen = parent_gz.astgen;
7517 const gpa = astgen.gpa;
7518 const tree = astgen.tree;
7519 const node_datas = tree.nodes.items(.data);
7520 const node_tags = tree.nodes.items(.tag);
7521 const main_tokens = tree.nodes.items(.main_token);
7522 const token_tags = tree.tokens.items(.tag);
7523 const operand_node = node_datas[switch_node].lhs;
7524 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
7525 const case_nodes = tree.extra_data[extra.start..extra.end];
7526
7527 const need_rl = astgen.nodes_need_rl.contains(switch_node);
7528 const block_ri: ResultInfo = if (need_rl) ri else .{
7529 .rl = switch (ri.rl) {
7530 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, switch_node)).? },
7531 .inferred_ptr => .none,
7532 else => ri.rl,
7533 },
7534 .ctx = ri.ctx,
7535 };
7536 // We need to call `rvalue` to write through to the pointer only if we had a
7537 // result pointer and aren't forwarding it.
7538 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
7539 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
7540
7541 // We perform two passes over the AST. This first pass is to collect information
7542 // for the following variables, make note of the special prong AST node index,
7543 // and bail out with a compile error if there are multiple special prongs present.
7544 var any_payload_is_ref = false;
7545 var any_has_tag_capture = false;
7546 var scalar_cases_len: u32 = 0;
7547 var multi_cases_len: u32 = 0;
7548 var inline_cases_len: u32 = 0;
7549 var special_prong: Zir.SpecialProng = .none;
7550 var special_node: Ast.Node.Index = 0;
7551 var else_src: ?Ast.TokenIndex = null;
7552 var underscore_src: ?Ast.TokenIndex = null;
7553 for (case_nodes) |case_node| {
7554 const case = tree.fullSwitchCase(case_node).?;
7555 if (case.payload_token) |payload_token| {
7556 const ident = if (token_tags[payload_token] == .asterisk) blk: {
7557 any_payload_is_ref = true;
7558 break :blk payload_token + 1;
7559 } else payload_token;
7560 if (token_tags[ident + 1] == .comma) {
7561 any_has_tag_capture = true;
7562 }
7563 }
7564 // Check for else/`_` prong.
7565 if (case.ast.values.len == 0) {
7566 const case_src = case.ast.arrow_token - 1;
7567 if (else_src) |src| {
7568 return astgen.failTokNotes(
7569 case_src,
7570 "multiple else prongs in switch expression",
7571 .{},
7572 &[_]u32{
7573 try astgen.errNoteTok(
7574 src,
7575 "previous else prong here",
7576 .{},
7577 ),
7578 },
7579 );
7580 } else if (underscore_src) |some_underscore| {
7581 return astgen.failNodeNotes(
7582 switch_node,
7583 "else and '_' prong in switch expression",
7584 .{},
7585 &[_]u32{
7586 try astgen.errNoteTok(
7587 case_src,
7588 "else prong here",
7589 .{},
7590 ),
7591 try astgen.errNoteTok(
7592 some_underscore,
7593 "'_' prong here",
7594 .{},
7595 ),
7596 },
7597 );
7598 }
7599 special_node = case_node;
7600 special_prong = .@"else";
7601 else_src = case_src;
7602 continue;
7603 } else if (case.ast.values.len == 1 and
7604 node_tags[case.ast.values[0]] == .identifier and
7605 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
7606 {
7607 const case_src = case.ast.arrow_token - 1;
7608 if (underscore_src) |src| {
7609 return astgen.failTokNotes(
7610 case_src,
7611 "multiple '_' prongs in switch expression",
7612 .{},
7613 &[_]u32{
7614 try astgen.errNoteTok(
7615 src,
7616 "previous '_' prong here",
7617 .{},
7618 ),
7619 },
7620 );
7621 } else if (else_src) |some_else| {
7622 return astgen.failNodeNotes(
7623 switch_node,
7624 "else and '_' prong in switch expression",
7625 .{},
7626 &[_]u32{
7627 try astgen.errNoteTok(
7628 some_else,
7629 "else prong here",
7630 .{},
7631 ),
7632 try astgen.errNoteTok(
7633 case_src,
7634 "'_' prong here",
7635 .{},
7636 ),
7637 },
7638 );
7639 }
7640 if (case.inline_token != null) {
7641 return astgen.failTok(case_src, "cannot inline '_' prong", .{});
7642 }
7643 special_node = case_node;
7644 special_prong = .under;
7645 underscore_src = case_src;
7646 continue;
7647 }
7648
7649 for (case.ast.values) |val| {
7650 if (node_tags[val] == .string_literal)
7651 return astgen.failNode(val, "cannot switch on strings", .{});
7652 }
7653
7654 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) {
7655 scalar_cases_len += 1;
7656 } else {
7657 multi_cases_len += 1;
7658 }
7659 if (case.inline_token != null) {
7660 inline_cases_len += 1;
7661 }
7662 }
7663
7664 const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };
7665
7666 astgen.advanceSourceCursorToNode(operand_node);
7667 const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
7668
7669 const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
7670 const item_ri: ResultInfo = .{ .rl = .none };
7671
7672 // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
7673 // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
7674 // the special case index coming first, then scalar_case_len indexes, then multi_cases_len indexes
7675 const payloads = &astgen.scratch;
7676 const scratch_top = astgen.scratch.items.len;
7677 const case_table_start = scratch_top;
7678 const scalar_case_table = case_table_start + @intFromBool(special_prong != .none);
7679 const multi_case_table = scalar_case_table + scalar_cases_len;
7680 const case_table_end = multi_case_table + multi_cases_len;
7681 try astgen.scratch.resize(gpa, case_table_end);
7682 defer astgen.scratch.items.len = scratch_top;
7683
7684 var block_scope = parent_gz.makeSubBlock(scope);
7685 // block_scope not used for collecting instructions
7686 block_scope.instructions_top = GenZir.unstacked_top;
7687 block_scope.setBreakResultInfo(block_ri);
7688
7689 // Sema expects a dbg_stmt immediately before switch_block(_ref)
7690 try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
7691 // This gets added to the parent block later, after the item expressions.
7692 const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block;
7693 const switch_block = try parent_gz.makeBlockInst(switch_tag, switch_node);
7694
7695 // We re-use this same scope for all cases, including the special prong, if any.
7696 var case_scope = parent_gz.makeSubBlock(&block_scope.base);
7697 case_scope.instructions_top = GenZir.unstacked_top;
7698
7699 // If any prong has an inline tag capture, allocate a shared dummy instruction for it
7700 const tag_inst = if (any_has_tag_capture) tag_inst: {
7701 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
7702 try astgen.instructions.append(astgen.gpa, .{
7703 .tag = .extended,
7704 .data = .{ .extended = .{
7705 .opcode = .value_placeholder,
7706 .small = undefined,
7707 .operand = undefined,
7708 } },
7709 });
7710 break :tag_inst inst;
7711 } else undefined;
7712
7713 // In this pass we generate all the item and prong expressions.
7714 var multi_case_index: u32 = 0;
7715 var scalar_case_index: u32 = 0;
7716 for (case_nodes) |case_node| {
7717 const case = tree.fullSwitchCase(case_node).?;
7718
7719 const is_multi_case = case.ast.values.len > 1 or
7720 (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
7721
7722 var dbg_var_name: Zir.NullTerminatedString = .empty;
7723 var dbg_var_inst: Zir.Inst.Ref = undefined;
7724 var dbg_var_tag_name: Zir.NullTerminatedString = .empty;
7725 var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
7726 var has_tag_capture = false;
7727 var capture_val_scope: Scope.LocalVal = undefined;
7728 var tag_scope: Scope.LocalVal = undefined;
7729
7730 var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none;
7731
7732 const sub_scope = blk: {
7733 const payload_token = case.payload_token orelse break :blk &case_scope.base;
7734 const ident = if (token_tags[payload_token] == .asterisk)
7735 payload_token + 1
7736 else
7737 payload_token;
7738
7739 const is_ptr = ident != payload_token;
7740 capture = if (is_ptr) .by_ref else .by_val;
7741
7742 const ident_slice = tree.tokenSlice(ident);
7743 var payload_sub_scope: *Scope = undefined;
7744 if (mem.eql(u8, ident_slice, "_")) {
7745 if (is_ptr) {
7746 return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
7747 }
7748 payload_sub_scope = &case_scope.base;
7749 } else {
7750 const capture_name = try astgen.identAsString(ident);
7751 try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture);
7752 capture_val_scope = .{
7753 .parent = &case_scope.base,
7754 .gen_zir = &case_scope,
7755 .name = capture_name,
7756 .inst = switch_block.toRef(),
7757 .token_src = ident,
7758 .id_cat = .capture,
7759 };
7760 dbg_var_name = capture_name;
7761 dbg_var_inst = switch_block.toRef();
7762 payload_sub_scope = &capture_val_scope.base;
7763 }
7764
7765 const tag_token = if (token_tags[ident + 1] == .comma)
7766 ident + 2
7767 else
7768 break :blk payload_sub_scope;
7769 const tag_slice = tree.tokenSlice(tag_token);
7770 if (mem.eql(u8, tag_slice, "_")) {
7771 return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{});
7772 } else if (case.inline_token == null) {
7773 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
7774 }
7775 const tag_name = try astgen.identAsString(tag_token);
7776 try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture");
7777
7778 assert(any_has_tag_capture);
7779 has_tag_capture = true;
7780
7781 tag_scope = .{
7782 .parent = payload_sub_scope,
7783 .gen_zir = &case_scope,
7784 .name = tag_name,
7785 .inst = tag_inst.toRef(),
7786 .token_src = tag_token,
7787 .id_cat = .@"switch tag capture",
7788 };
7789 dbg_var_tag_name = tag_name;
7790 dbg_var_tag_inst = tag_inst.toRef();
7791 break :blk &tag_scope.base;
7792 };
7793
7794 const header_index: u32 = @intCast(payloads.items.len);
7795 const body_len_index = if (is_multi_case) blk: {
7796 payloads.items[multi_case_table + multi_case_index] = header_index;
7797 multi_case_index += 1;
7798 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
7799
7800 // items
7801 var items_len: u32 = 0;
7802 for (case.ast.values) |item_node| {
7803 if (node_tags[item_node] == .switch_range) continue;
7804 items_len += 1;
7805
7806 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
7807 try payloads.append(gpa, @intFromEnum(item_inst));
7808 }
7809
7810 // ranges
7811 var ranges_len: u32 = 0;
7812 for (case.ast.values) |range| {
7813 if (node_tags[range] != .switch_range) continue;
7814 ranges_len += 1;
7815
7816 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs);
7817 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs);
7818 try payloads.appendSlice(gpa, &[_]u32{
7819 @intFromEnum(first), @intFromEnum(last),
7820 });
7821 }
7822
7823 payloads.items[header_index] = items_len;
7824 payloads.items[header_index + 1] = ranges_len;
7825 break :blk header_index + 2;
7826 } else if (case_node == special_node) blk: {
7827 payloads.items[case_table_start] = header_index;
7828 try payloads.resize(gpa, header_index + 1); // body_len
7829 break :blk header_index;
7830 } else blk: {
7831 payloads.items[scalar_case_table + scalar_case_index] = header_index;
7832 scalar_case_index += 1;
7833 try payloads.resize(gpa, header_index + 2); // item, body_len
7834 const item_node = case.ast.values[0];
7835 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
7836 payloads.items[header_index] = @intFromEnum(item_inst);
7837 break :blk header_index + 1;
7838 };
7839
7840 {
7841 // temporarily stack case_scope on parent_gz
7842 case_scope.instructions_top = parent_gz.instructions.items.len;
7843 defer case_scope.unstack();
7844
7845 if (dbg_var_name != .empty) {
7846 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
7847 }
7848 if (dbg_var_tag_name != .empty) {
7849 try case_scope.addDbgVar(.dbg_var_val, dbg_var_tag_name, dbg_var_tag_inst);
7850 }
7851 const target_expr_node = case.ast.target_expr;
7852 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node);
7853 try checkUsed(parent_gz, &case_scope.base, sub_scope);
7854 if (!parent_gz.refIsNoReturn(case_result)) {
7855 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
7856 }
7857
7858 const case_slice = case_scope.instructionsSlice();
7859 // Since we use the switch_block instruction itself to refer to the
7860 // capture, which will not be added to the child block, we need to
7861 // handle ref_table manually, and the same for the inline tag
7862 // capture instruction.
7863 const refs_len = refs: {
7864 var n: usize = 0;
7865 var check_inst = switch_block;
7866 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7867 n += 1;
7868 check_inst = ref_inst;
7869 }
7870 if (has_tag_capture) {
7871 check_inst = tag_inst;
7872 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7873 n += 1;
7874 check_inst = ref_inst;
7875 }
7876 }
7877 break :refs n;
7878 };
7879 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
7880 try payloads.ensureUnusedCapacity(gpa, body_len);
7881 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
7882 .body_len = @intCast(body_len),
7883 .capture = capture,
7884 .is_inline = case.inline_token != null,
7885 .has_tag_capture = has_tag_capture,
7886 });
7887 if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
7888 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7889 }
7890 if (has_tag_capture) {
7891 if (astgen.ref_table.fetchRemove(tag_inst)) |kv| {
7892 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7893 }
7894 }
7895 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
7896 }
7897 }
7898 // Now that the item expressions are generated we can add this.
7899 try parent_gz.instructions.append(gpa, switch_block);
7900
7901 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
7902 @intFromBool(multi_cases_len != 0) +
7903 @intFromBool(any_has_tag_capture) +
7904 payloads.items.len - case_table_end +
7905 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).Struct.fields.len);
7906
7907 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
7908 .operand = raw_operand,
7909 .bits = Zir.Inst.SwitchBlock.Bits{
7910 .has_multi_cases = multi_cases_len != 0,
7911 .has_else = special_prong == .@"else",
7912 .has_under = special_prong == .under,
7913 .any_has_tag_capture = any_has_tag_capture,
7914 .scalar_cases_len = @intCast(scalar_cases_len),
7915 },
7916 });
7917
7918 if (multi_cases_len != 0) {
7919 astgen.extra.appendAssumeCapacity(multi_cases_len);
7920 }
7921
7922 if (any_has_tag_capture) {
7923 astgen.extra.appendAssumeCapacity(@intFromEnum(tag_inst));
7924 }
7925
7926 const zir_datas = astgen.instructions.items(.data);
7927 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
7928
7929 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
7930 var body_len_index = start_index;
7931 var end_index = start_index;
7932 const table_index = case_table_start + i;
7933 if (table_index < scalar_case_table) {
7934 end_index += 1;
7935 } else if (table_index < multi_case_table) {
7936 body_len_index += 1;
7937 end_index += 2;
7938 } else {
7939 body_len_index += 2;
7940 const items_len = payloads.items[start_index];
7941 const ranges_len = payloads.items[start_index + 1];
7942 end_index += 3 + items_len + 2 * ranges_len;
7943 }
7944 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
7945 end_index += prong_info.body_len;
7946 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
7947 }
7948
7949 if (need_result_rvalue) {
7950 return rvalue(parent_gz, ri, switch_block.toRef(), switch_node);
7951 } else {
7952 return switch_block.toRef();
7953 }
7954}
7955
7956fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
7957 const astgen = gz.astgen;
7958 const tree = astgen.tree;
7959 const node_datas = tree.nodes.items(.data);
7960 const node_tags = tree.nodes.items(.tag);
7961
7962 if (astgen.fn_block == null) {
7963 return astgen.failNode(node, "'return' outside function scope", .{});
7964 }
7965
7966 if (gz.any_defer_node != 0) {
7967 return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{
7968 try astgen.errNoteNode(
7969 gz.any_defer_node,
7970 "defer expression here",
7971 .{},
7972 ),
7973 });
7974 }
7975
7976 // Ensure debug line/column information is emitted for this return expression.
7977 // Then we will save the line/column so that we can emit another one that goes
7978 // "backwards" because we want to evaluate the operand, but then put the debug
7979 // info back at the return keyword for error return tracing.
7980 if (!gz.is_comptime) {
7981 try emitDbgNode(gz, node);
7982 }
7983 const ret_lc = LineColumn{ astgen.source_line - gz.decl_line, astgen.source_column };
7984
7985 const defer_outer = &astgen.fn_block.?.base;
7986
7987 const operand_node = node_datas[node].lhs;
7988 if (operand_node == 0) {
7989 // Returning a void value; skip error defers.
7990 try genDefers(gz, defer_outer, scope, .normal_only);
7991
7992 // As our last action before the return, "pop" the error trace if needed
7993 _ = try gz.addRestoreErrRetIndex(.ret, .always, node);
7994
7995 _ = try gz.addUnNode(.ret_node, .void_value, node);
7996 return Zir.Inst.Ref.unreachable_value;
7997 }
7998
7999 if (node_tags[operand_node] == .error_value) {
8000 // Hot path for `return error.Foo`. This bypasses result location logic as well as logic
8001 // for detecting whether to add something to the function's inferred error set.
8002 const ident_token = node_datas[operand_node].rhs;
8003 const err_name_str_index = try astgen.identAsString(ident_token);
8004 const defer_counts = countDefers(defer_outer, scope);
8005 if (!defer_counts.need_err_code) {
8006 try genDefers(gz, defer_outer, scope, .both_sans_err);
8007 try emitDbgStmt(gz, ret_lc);
8008 _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
8009 return Zir.Inst.Ref.unreachable_value;
8010 }
8011 const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
8012 try genDefers(gz, defer_outer, scope, .{ .both = err_code });
8013 try emitDbgStmt(gz, ret_lc);
8014 _ = try gz.addUnNode(.ret_node, err_code, node);
8015 return Zir.Inst.Ref.unreachable_value;
8016 }
8017
8018 const ri: ResultInfo = if (astgen.nodes_need_rl.contains(node)) .{
8019 .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } },
8020 .ctx = .@"return",
8021 } else .{
8022 .rl = .{ .coerced_ty = astgen.fn_ret_ty },
8023 .ctx = .@"return",
8024 };
8025 const prev_anon_name_strategy = gz.anon_name_strategy;
8026 gz.anon_name_strategy = .func;
8027 const operand = try reachableExpr(gz, scope, ri, operand_node, node);
8028 gz.anon_name_strategy = prev_anon_name_strategy;
8029
8030 switch (nodeMayEvalToError(tree, operand_node)) {
8031 .never => {
8032 // Returning a value that cannot be an error; skip error defers.
8033 try genDefers(gz, defer_outer, scope, .normal_only);
8034
8035 // As our last action before the return, "pop" the error trace if needed
8036 _ = try gz.addRestoreErrRetIndex(.ret, .always, node);
8037
8038 try emitDbgStmt(gz, ret_lc);
8039 try gz.addRet(ri, operand, node);
8040 return Zir.Inst.Ref.unreachable_value;
8041 },
8042 .always => {
8043 // Value is always an error. Emit both error defers and regular defers.
8044 const err_code = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
8045 try genDefers(gz, defer_outer, scope, .{ .both = err_code });
8046 try emitDbgStmt(gz, ret_lc);
8047 try gz.addRet(ri, operand, node);
8048 return Zir.Inst.Ref.unreachable_value;
8049 },
8050 .maybe => {
8051 const defer_counts = countDefers(defer_outer, scope);
8052 if (!defer_counts.have_err) {
8053 // Only regular defers; no branch needed.
8054 try genDefers(gz, defer_outer, scope, .normal_only);
8055 try emitDbgStmt(gz, ret_lc);
8056
8057 // As our last action before the return, "pop" the error trace if needed
8058 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
8059 _ = try gz.addRestoreErrRetIndex(.ret, .{ .if_non_error = result }, node);
8060
8061 try gz.addRet(ri, operand, node);
8062 return Zir.Inst.Ref.unreachable_value;
8063 }
8064
8065 // Emit conditional branch for generating errdefers.
8066 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
8067 const is_non_err = try gz.addUnNode(.ret_is_non_err, result, node);
8068 const condbr = try gz.addCondBr(.condbr, node);
8069
8070 var then_scope = gz.makeSubBlock(scope);
8071 defer then_scope.unstack();
8072
8073 try genDefers(&then_scope, defer_outer, scope, .normal_only);
8074
8075 // As our last action before the return, "pop" the error trace if needed
8076 _ = try then_scope.addRestoreErrRetIndex(.ret, .always, node);
8077
8078 try emitDbgStmt(&then_scope, ret_lc);
8079 try then_scope.addRet(ri, operand, node);
8080
8081 var else_scope = gz.makeSubBlock(scope);
8082 defer else_scope.unstack();
8083
8084 const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
8085 .both = try else_scope.addUnNode(.err_union_code, result, node),
8086 };
8087 try genDefers(&else_scope, defer_outer, scope, which_ones);
8088 try emitDbgStmt(&else_scope, ret_lc);
8089 try else_scope.addRet(ri, operand, node);
8090
8091 try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope);
8092
8093 return Zir.Inst.Ref.unreachable_value;
8094 },
8095 }
8096}
8097
8098/// Parses the string `buf` as a base 10 integer of type `u16`.
8099///
8100/// Unlike std.fmt.parseInt, does not allow the '_' character in `buf`.
8101fn parseBitCount(buf: []const u8) std.fmt.ParseIntError!u16 {
8102 if (buf.len == 0) return error.InvalidCharacter;
8103
8104 var x: u16 = 0;
8105
8106 for (buf) |c| {
8107 const digit = switch (c) {
8108 '0'...'9' => c - '0',
8109 else => return error.InvalidCharacter,
8110 };
8111
8112 if (x != 0) x = try std.math.mul(u16, x, 10);
8113 x = try std.math.add(u16, x, digit);
8114 }
8115
8116 return x;
8117}
8118
8119fn identifier(
8120 gz: *GenZir,
8121 scope: *Scope,
8122 ri: ResultInfo,
8123 ident: Ast.Node.Index,
8124) InnerError!Zir.Inst.Ref {
8125 const astgen = gz.astgen;
8126 const tree = astgen.tree;
8127 const main_tokens = tree.nodes.items(.main_token);
8128
8129 const ident_token = main_tokens[ident];
8130 const ident_name_raw = tree.tokenSlice(ident_token);
8131 if (mem.eql(u8, ident_name_raw, "_")) {
8132 return astgen.failNode(ident, "'_' used as an identifier without @\"_\" syntax", .{});
8133 }
8134
8135 // if not @"" syntax, just use raw token slice
8136 if (ident_name_raw[0] != '@') {
8137 if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
8138 return rvalue(gz, ri, zir_const_ref, ident);
8139 }
8140
8141 if (ident_name_raw.len >= 2) integer: {
8142 const first_c = ident_name_raw[0];
8143 if (first_c == 'i' or first_c == 'u') {
8144 const signedness: std.builtin.Signedness = switch (first_c == 'i') {
8145 true => .signed,
8146 false => .unsigned,
8147 };
8148 if (ident_name_raw.len >= 3 and ident_name_raw[1] == '0') {
8149 return astgen.failNode(
8150 ident,
8151 "primitive integer type '{s}' has leading zero",
8152 .{ident_name_raw},
8153 );
8154 }
8155 const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
8156 error.Overflow => return astgen.failNode(
8157 ident,
8158 "primitive integer type '{s}' exceeds maximum bit width of 65535",
8159 .{ident_name_raw},
8160 ),
8161 error.InvalidCharacter => break :integer,
8162 };
8163 const result = try gz.add(.{
8164 .tag = .int_type,
8165 .data = .{ .int_type = .{
8166 .src_node = gz.nodeIndexToRelative(ident),
8167 .signedness = signedness,
8168 .bit_count = bit_count,
8169 } },
8170 });
8171 return rvalue(gz, ri, result, ident);
8172 }
8173 }
8174 }
8175
8176 // Local variables, including function parameters.
8177 return localVarRef(gz, scope, ri, ident, ident_token);
8178}
8179
8180fn localVarRef(
8181 gz: *GenZir,
8182 scope: *Scope,
8183 ri: ResultInfo,
8184 ident: Ast.Node.Index,
8185 ident_token: Ast.TokenIndex,
8186) InnerError!Zir.Inst.Ref {
8187 const astgen = gz.astgen;
8188 const gpa = astgen.gpa;
8189 const name_str_index = try astgen.identAsString(ident_token);
8190 var s = scope;
8191 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
8192 var num_namespaces_out: u32 = 0;
8193 var capturing_namespace: ?*Scope.Namespace = null;
8194 while (true) switch (s.tag) {
8195 .local_val => {
8196 const local_val = s.cast(Scope.LocalVal).?;
8197
8198 if (local_val.name == name_str_index) {
8199 // Locals cannot shadow anything, so we do not need to look for ambiguous
8200 // references in this case.
8201 if (ri.rl == .discard and ri.ctx == .assignment) {
8202 local_val.discarded = ident_token;
8203 } else {
8204 local_val.used = ident_token;
8205 }
8206
8207 const value_inst = try tunnelThroughClosure(
8208 gz,
8209 ident,
8210 num_namespaces_out,
8211 capturing_namespace,
8212 local_val.inst,
8213 local_val.token_src,
8214 gpa,
8215 );
8216
8217 return rvalueNoCoercePreRef(gz, ri, value_inst, ident);
8218 }
8219 s = local_val.parent;
8220 },
8221 .local_ptr => {
8222 const local_ptr = s.cast(Scope.LocalPtr).?;
8223 if (local_ptr.name == name_str_index) {
8224 if (ri.rl == .discard and ri.ctx == .assignment) {
8225 local_ptr.discarded = ident_token;
8226 } else {
8227 local_ptr.used = ident_token;
8228 }
8229
8230 // Can't close over a runtime variable
8231 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) {
8232 const ident_name = try astgen.identifierTokenString(ident_token);
8233 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
8234 try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
8235 try astgen.errNoteNode(capturing_namespace.?.node, "crosses namespace boundary here", .{}),
8236 });
8237 }
8238
8239 const ptr_inst = try tunnelThroughClosure(
8240 gz,
8241 ident,
8242 num_namespaces_out,
8243 capturing_namespace,
8244 local_ptr.ptr,
8245 local_ptr.token_src,
8246 gpa,
8247 );
8248
8249 switch (ri.rl) {
8250 .ref, .ref_coerced_ty => {
8251 local_ptr.used_as_lvalue = true;
8252 return ptr_inst;
8253 },
8254 else => {
8255 const loaded = try gz.addUnNode(.load, ptr_inst, ident);
8256 return rvalueNoCoercePreRef(gz, ri, loaded, ident);
8257 },
8258 }
8259 }
8260 s = local_ptr.parent;
8261 },
8262 .gen_zir => s = s.cast(GenZir).?.parent,
8263 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
8264 .namespace, .enum_namespace => {
8265 const ns = s.cast(Scope.Namespace).?;
8266 if (ns.decls.get(name_str_index)) |i| {
8267 if (found_already) |f| {
8268 return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{
8269 try astgen.errNoteNode(f, "declared here", .{}),
8270 try astgen.errNoteNode(i, "also declared here", .{}),
8271 });
8272 }
8273 // We found a match but must continue looking for ambiguous references to decls.
8274 found_already = i;
8275 }
8276 if (s.tag == .namespace) num_namespaces_out += 1;
8277 capturing_namespace = ns;
8278 s = ns.parent;
8279 },
8280 .top => break,
8281 };
8282 if (found_already == null) {
8283 const ident_name = try astgen.identifierTokenString(ident_token);
8284 return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
8285 }
8286
8287 // Decl references happen by name rather than ZIR index so that when unrelated
8288 // decls are modified, ZIR code containing references to them can be unmodified.
8289 switch (ri.rl) {
8290 .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
8291 else => {
8292 const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);
8293 return rvalueNoCoercePreRef(gz, ri, result, ident);
8294 },
8295 }
8296}
8297
8298/// Adds a capture to a namespace, if needed.
8299/// Returns the index of the closure_capture instruction.
8300fn tunnelThroughClosure(
8301 gz: *GenZir,
8302 inner_ref_node: Ast.Node.Index,
8303 num_tunnels: u32,
8304 ns: ?*Scope.Namespace,
8305 value: Zir.Inst.Ref,
8306 token: Ast.TokenIndex,
8307 gpa: Allocator,
8308) !Zir.Inst.Ref {
8309 // For trivial values, we don't need a tunnel.
8310 // Just return the ref.
8311 if (num_tunnels == 0 or value.toIndex() == null) {
8312 return value;
8313 }
8314
8315 // Otherwise we need a tunnel. Check if this namespace
8316 // already has one for this value.
8317 const gop = try ns.?.captures.getOrPut(gpa, value.toIndex().?);
8318 if (!gop.found_existing) {
8319 // Make a new capture for this value but don't add it to the declaring_gz yet
8320 try gz.astgen.instructions.append(gz.astgen.gpa, .{
8321 .tag = .closure_capture,
8322 .data = .{ .un_tok = .{
8323 .operand = value,
8324 .src_tok = ns.?.declaring_gz.?.tokenIndexToRelative(token),
8325 } },
8326 });
8327 gop.value_ptr.* = @enumFromInt(gz.astgen.instructions.len - 1);
8328 }
8329
8330 // Add an instruction to get the value from the closure into
8331 // our current context
8332 return try gz.addInstNode(.closure_get, gop.value_ptr.*, inner_ref_node);
8333}
8334
8335fn stringLiteral(
8336 gz: *GenZir,
8337 ri: ResultInfo,
8338 node: Ast.Node.Index,
8339) InnerError!Zir.Inst.Ref {
8340 const astgen = gz.astgen;
8341 const tree = astgen.tree;
8342 const main_tokens = tree.nodes.items(.main_token);
8343 const str_lit_token = main_tokens[node];
8344 const str = try astgen.strLitAsString(str_lit_token);
8345 const result = try gz.add(.{
8346 .tag = .str,
8347 .data = .{ .str = .{
8348 .start = str.index,
8349 .len = str.len,
8350 } },
8351 });
8352 return rvalue(gz, ri, result, node);
8353}
8354
8355fn multilineStringLiteral(
8356 gz: *GenZir,
8357 ri: ResultInfo,
8358 node: Ast.Node.Index,
8359) InnerError!Zir.Inst.Ref {
8360 const astgen = gz.astgen;
8361 const str = try astgen.strLitNodeAsString(node);
8362 const result = try gz.add(.{
8363 .tag = .str,
8364 .data = .{ .str = .{
8365 .start = str.index,
8366 .len = str.len,
8367 } },
8368 });
8369 return rvalue(gz, ri, result, node);
8370}
8371
8372fn charLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
8373 const astgen = gz.astgen;
8374 const tree = astgen.tree;
8375 const main_tokens = tree.nodes.items(.main_token);
8376 const main_token = main_tokens[node];
8377 const slice = tree.tokenSlice(main_token);
8378
8379 switch (std.zig.parseCharLiteral(slice)) {
8380 .success => |codepoint| {
8381 const result = try gz.addInt(codepoint);
8382 return rvalue(gz, ri, result, node);
8383 },
8384 .failure => |err| return astgen.failWithStrLitError(err, main_token, slice, 0),
8385 }
8386}
8387
8388const Sign = enum { negative, positive };
8389
8390fn numberLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index, source_node: Ast.Node.Index, sign: Sign) InnerError!Zir.Inst.Ref {
8391 const astgen = gz.astgen;
8392 const tree = astgen.tree;
8393 const main_tokens = tree.nodes.items(.main_token);
8394 const num_token = main_tokens[node];
8395 const bytes = tree.tokenSlice(num_token);
8396
8397 const result: Zir.Inst.Ref = switch (std.zig.parseNumberLiteral(bytes)) {
8398 .int => |num| switch (num) {
8399 0 => if (sign == .positive) .zero else return astgen.failTokNotes(
8400 num_token,
8401 "integer literal '-0' is ambiguous",
8402 .{},
8403 &.{
8404 try astgen.errNoteTok(num_token, "use '0' for an integer zero", .{}),
8405 try astgen.errNoteTok(num_token, "use '-0.0' for a floating-point signed zero", .{}),
8406 },
8407 ),
8408 1 => .one,
8409 else => try gz.addInt(num),
8410 },
8411 .big_int => |base| big: {
8412 const gpa = astgen.gpa;
8413 var big_int = try std.math.big.int.Managed.init(gpa);
8414 defer big_int.deinit();
8415 const prefix_offset: usize = if (base == .decimal) 0 else 2;
8416 big_int.setString(@intFromEnum(base), bytes[prefix_offset..]) catch |err| switch (err) {
8417 error.InvalidCharacter => unreachable, // caught in `parseNumberLiteral`
8418 error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above
8419 error.OutOfMemory => return error.OutOfMemory,
8420 };
8421
8422 const limbs = big_int.limbs[0..big_int.len()];
8423 assert(big_int.isPositive());
8424 break :big try gz.addIntBig(limbs);
8425 },
8426 .float => {
8427 const unsigned_float_number = std.fmt.parseFloat(f128, bytes) catch |err| switch (err) {
8428 error.InvalidCharacter => unreachable, // validated by tokenizer
8429 };
8430 const float_number = switch (sign) {
8431 .negative => -unsigned_float_number,
8432 .positive => unsigned_float_number,
8433 };
8434 // If the value fits into a f64 without losing any precision, store it that way.
8435 @setFloatMode(.Strict);
8436 const smaller_float: f64 = @floatCast(float_number);
8437 const bigger_again: f128 = smaller_float;
8438 if (bigger_again == float_number) {
8439 const result = try gz.addFloat(smaller_float);
8440 return rvalue(gz, ri, result, source_node);
8441 }
8442 // We need to use 128 bits. Break the float into 4 u32 values so we can
8443 // put it into the `extra` array.
8444 const int_bits: u128 = @bitCast(float_number);
8445 const result = try gz.addPlNode(.float128, node, Zir.Inst.Float128{
8446 .piece0 = @truncate(int_bits),
8447 .piece1 = @truncate(int_bits >> 32),
8448 .piece2 = @truncate(int_bits >> 64),
8449 .piece3 = @truncate(int_bits >> 96),
8450 });
8451 return rvalue(gz, ri, result, source_node);
8452 },
8453 .failure => |err| return astgen.failWithNumberError(err, num_token, bytes),
8454 };
8455
8456 if (sign == .positive) {
8457 return rvalue(gz, ri, result, source_node);
8458 } else {
8459 const negated = try gz.addUnNode(.negate, result, source_node);
8460 return rvalue(gz, ri, negated, source_node);
8461 }
8462}
8463
8464fn failWithNumberError(astgen: *AstGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) InnerError {
8465 const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null;
8466 switch (err) {
8467 .leading_zero => if (is_float) {
8468 return astgen.failTok(token, "number '{s}' has leading zero", .{bytes});
8469 } else {
8470 return astgen.failTokNotes(token, "number '{s}' has leading zero", .{bytes}, &.{
8471 try astgen.errNoteTok(token, "use '0o' prefix for octal literals", .{}),
8472 });
8473 },
8474 .digit_after_base => return astgen.failTok(token, "expected a digit after base prefix", .{}),
8475 .upper_case_base => |i| return astgen.failOff(token, @intCast(i), "base prefix must be lowercase", .{}),
8476 .invalid_float_base => |i| return astgen.failOff(token, @intCast(i), "invalid base for float literal", .{}),
8477 .repeated_underscore => |i| return astgen.failOff(token, @intCast(i), "repeated digit separator", .{}),
8478 .invalid_underscore_after_special => |i| return astgen.failOff(token, @intCast(i), "expected digit before digit separator", .{}),
8479 .invalid_digit => |info| return astgen.failOff(token, @intCast(info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }),
8480 .invalid_digit_exponent => |i| return astgen.failOff(token, @intCast(i), "invalid digit '{c}' in exponent", .{bytes[i]}),
8481 .duplicate_exponent => |i| return astgen.failOff(token, @intCast(i), "duplicate exponent", .{}),
8482 .exponent_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before exponent", .{}),
8483 .special_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before '{c}'", .{bytes[i]}),
8484 .trailing_special => |i| return astgen.failOff(token, @intCast(i), "expected digit after '{c}'", .{bytes[i - 1]}),
8485 .trailing_underscore => |i| return astgen.failOff(token, @intCast(i), "trailing digit separator", .{}),
8486 .duplicate_period => unreachable, // Validated by tokenizer
8487 .invalid_character => unreachable, // Validated by tokenizer
8488 .invalid_exponent_sign => |i| {
8489 assert(bytes.len >= 2 and bytes[0] == '0' and bytes[1] == 'x'); // Validated by tokenizer
8490 return astgen.failOff(token, @intCast(i), "sign '{c}' cannot follow digit '{c}' in hex base", .{ bytes[i], bytes[i - 1] });
8491 },
8492 }
8493}
8494
8495fn asmExpr(
8496 gz: *GenZir,
8497 scope: *Scope,
8498 ri: ResultInfo,
8499 node: Ast.Node.Index,
8500 full: Ast.full.Asm,
8501) InnerError!Zir.Inst.Ref {
8502 const astgen = gz.astgen;
8503 const tree = astgen.tree;
8504 const main_tokens = tree.nodes.items(.main_token);
8505 const node_datas = tree.nodes.items(.data);
8506 const node_tags = tree.nodes.items(.tag);
8507 const token_tags = tree.tokens.items(.tag);
8508
8509 const TagAndTmpl = struct { tag: Zir.Inst.Extended, tmpl: Zir.NullTerminatedString };
8510 const tag_and_tmpl: TagAndTmpl = switch (node_tags[full.ast.template]) {
8511 .string_literal => .{
8512 .tag = .@"asm",
8513 .tmpl = (try astgen.strLitAsString(main_tokens[full.ast.template])).index,
8514 },
8515 .multiline_string_literal => .{
8516 .tag = .@"asm",
8517 .tmpl = (try astgen.strLitNodeAsString(full.ast.template)).index,
8518 },
8519 else => .{
8520 .tag = .asm_expr,
8521 .tmpl = @enumFromInt(@intFromEnum(try comptimeExpr(gz, scope, .{ .rl = .none }, full.ast.template))),
8522 },
8523 };
8524
8525 // See https://github.com/ziglang/zig/issues/215 and related issues discussing
8526 // possible inline assembly improvements. Until then here is status quo AstGen
8527 // for assembly syntax. It's used by std lib crypto aesni.zig.
8528 const is_container_asm = astgen.fn_block == null;
8529 if (is_container_asm) {
8530 if (full.volatile_token) |t|
8531 return astgen.failTok(t, "volatile is meaningless on global assembly", .{});
8532 if (full.outputs.len != 0 or full.inputs.len != 0 or full.first_clobber != null)
8533 return astgen.failNode(node, "global assembly cannot have inputs, outputs, or clobbers", .{});
8534 } else {
8535 if (full.outputs.len == 0 and full.volatile_token == null) {
8536 return astgen.failNode(node, "assembly expression with no output must be marked volatile", .{});
8537 }
8538 }
8539 if (full.outputs.len > 32) {
8540 return astgen.failNode(full.outputs[32], "too many asm outputs", .{});
8541 }
8542 var outputs_buffer: [32]Zir.Inst.Asm.Output = undefined;
8543 const outputs = outputs_buffer[0..full.outputs.len];
8544
8545 var output_type_bits: u32 = 0;
8546
8547 for (full.outputs, 0..) |output_node, i| {
8548 const symbolic_name = main_tokens[output_node];
8549 const name = try astgen.identAsString(symbolic_name);
8550 const constraint_token = symbolic_name + 2;
8551 const constraint = (try astgen.strLitAsString(constraint_token)).index;
8552 const has_arrow = token_tags[symbolic_name + 4] == .arrow;
8553 if (has_arrow) {
8554 if (output_type_bits != 0) {
8555 return astgen.failNode(output_node, "inline assembly allows up to one output value", .{});
8556 }
8557 output_type_bits |= @as(u32, 1) << @intCast(i);
8558 const out_type_node = node_datas[output_node].lhs;
8559 const out_type_inst = try typeExpr(gz, scope, out_type_node);
8560 outputs[i] = .{
8561 .name = name,
8562 .constraint = constraint,
8563 .operand = out_type_inst,
8564 };
8565 } else {
8566 const ident_token = symbolic_name + 4;
8567 // TODO have a look at #215 and related issues and decide how to
8568 // handle outputs. Do we want this to be identifiers?
8569 // Or maybe we want to force this to be expressions with a pointer type.
8570 outputs[i] = .{
8571 .name = name,
8572 .constraint = constraint,
8573 .operand = try localVarRef(gz, scope, .{ .rl = .ref }, node, ident_token),
8574 };
8575 }
8576 }
8577
8578 if (full.inputs.len > 32) {
8579 return astgen.failNode(full.inputs[32], "too many asm inputs", .{});
8580 }
8581 var inputs_buffer: [32]Zir.Inst.Asm.Input = undefined;
8582 const inputs = inputs_buffer[0..full.inputs.len];
8583
8584 for (full.inputs, 0..) |input_node, i| {
8585 const symbolic_name = main_tokens[input_node];
8586 const name = try astgen.identAsString(symbolic_name);
8587 const constraint_token = symbolic_name + 2;
8588 const constraint = (try astgen.strLitAsString(constraint_token)).index;
8589 const operand = try expr(gz, scope, .{ .rl = .none }, node_datas[input_node].lhs);
8590 inputs[i] = .{
8591 .name = name,
8592 .constraint = constraint,
8593 .operand = operand,
8594 };
8595 }
8596
8597 var clobbers_buffer: [32]u32 = undefined;
8598 var clobber_i: usize = 0;
8599 if (full.first_clobber) |first_clobber| clobbers: {
8600 // asm ("foo" ::: "a", "b")
8601 // asm ("foo" ::: "a", "b",)
8602 var tok_i = first_clobber;
8603 while (true) : (tok_i += 1) {
8604 if (clobber_i >= clobbers_buffer.len) {
8605 return astgen.failTok(tok_i, "too many asm clobbers", .{});
8606 }
8607 clobbers_buffer[clobber_i] = @intFromEnum((try astgen.strLitAsString(tok_i)).index);
8608 clobber_i += 1;
8609 tok_i += 1;
8610 switch (token_tags[tok_i]) {
8611 .r_paren => break :clobbers,
8612 .comma => {
8613 if (token_tags[tok_i + 1] == .r_paren) {
8614 break :clobbers;
8615 } else {
8616 continue;
8617 }
8618 },
8619 else => unreachable,
8620 }
8621 }
8622 }
8623
8624 const result = try gz.addAsm(.{
8625 .tag = tag_and_tmpl.tag,
8626 .node = node,
8627 .asm_source = tag_and_tmpl.tmpl,
8628 .is_volatile = full.volatile_token != null,
8629 .output_type_bits = output_type_bits,
8630 .outputs = outputs,
8631 .inputs = inputs,
8632 .clobbers = clobbers_buffer[0..clobber_i],
8633 });
8634 return rvalue(gz, ri, result, node);
8635}
8636
8637fn as(
8638 gz: *GenZir,
8639 scope: *Scope,
8640 ri: ResultInfo,
8641 node: Ast.Node.Index,
8642 lhs: Ast.Node.Index,
8643 rhs: Ast.Node.Index,
8644) InnerError!Zir.Inst.Ref {
8645 const dest_type = try typeExpr(gz, scope, lhs);
8646 const result = try reachableExpr(gz, scope, .{ .rl = .{ .ty = dest_type } }, rhs, node);
8647 return rvalue(gz, ri, result, node);
8648}
8649
8650fn unionInit(
8651 gz: *GenZir,
8652 scope: *Scope,
8653 ri: ResultInfo,
8654 node: Ast.Node.Index,
8655 params: []const Ast.Node.Index,
8656) InnerError!Zir.Inst.Ref {
8657 const union_type = try typeExpr(gz, scope, params[0]);
8658 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]);
8659 const field_type = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{
8660 .container_type = union_type,
8661 .field_name = field_name,
8662 });
8663 const init = try reachableExpr(gz, scope, .{ .rl = .{ .ty = field_type } }, params[2], node);
8664 const result = try gz.addPlNode(.union_init, node, Zir.Inst.UnionInit{
8665 .union_type = union_type,
8666 .init = init,
8667 .field_name = field_name,
8668 });
8669 return rvalue(gz, ri, result, node);
8670}
8671
8672fn bitCast(
8673 gz: *GenZir,
8674 scope: *Scope,
8675 ri: ResultInfo,
8676 node: Ast.Node.Index,
8677 operand_node: Ast.Node.Index,
8678) InnerError!Zir.Inst.Ref {
8679 const dest_type = try ri.rl.resultTypeForCast(gz, node, "@bitCast");
8680 const operand = try reachableExpr(gz, scope, .{ .rl = .none }, operand_node, node);
8681 const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{
8682 .lhs = dest_type,
8683 .rhs = operand,
8684 });
8685 return rvalue(gz, ri, result, node);
8686}
8687
8688/// Handle one or more nested pointer cast builtins:
8689/// * @ptrCast
8690/// * @alignCast
8691/// * @addrSpaceCast
8692/// * @constCast
8693/// * @volatileCast
8694/// Any sequence of such builtins is treated as a single operation. This allowed
8695/// for sequences like `@ptrCast(@alignCast(ptr))` to work correctly despite the
8696/// intermediate result type being unknown.
8697fn ptrCast(
8698 gz: *GenZir,
8699 scope: *Scope,
8700 ri: ResultInfo,
8701 root_node: Ast.Node.Index,
8702) InnerError!Zir.Inst.Ref {
8703 const astgen = gz.astgen;
8704 const tree = astgen.tree;
8705 const main_tokens = tree.nodes.items(.main_token);
8706 const node_datas = tree.nodes.items(.data);
8707 const node_tags = tree.nodes.items(.tag);
8708
8709 var flags: Zir.Inst.FullPtrCastFlags = .{};
8710
8711 // Note that all pointer cast builtins have one parameter, so we only need
8712 // to handle `builtin_call_two`.
8713 var node = root_node;
8714 while (true) {
8715 switch (node_tags[node]) {
8716 .builtin_call_two, .builtin_call_two_comma => {},
8717 .grouped_expression => {
8718 // Handle the chaining even with redundant parentheses
8719 node = node_datas[node].lhs;
8720 continue;
8721 },
8722 else => break,
8723 }
8724
8725 if (node_datas[node].lhs == 0) break; // 0 args
8726 if (node_datas[node].rhs != 0) break; // 2 args
8727
8728 const builtin_token = main_tokens[node];
8729 const builtin_name = tree.tokenSlice(builtin_token);
8730 const info = BuiltinFn.list.get(builtin_name) orelse break;
8731 if (info.param_count != 1) break;
8732
8733 switch (info.tag) {
8734 else => break,
8735 inline .ptr_cast,
8736 .align_cast,
8737 .addrspace_cast,
8738 .const_cast,
8739 .volatile_cast,
8740 => |tag| {
8741 if (@field(flags, @tagName(tag))) {
8742 return astgen.failNode(node, "redundant {s}", .{builtin_name});
8743 }
8744 @field(flags, @tagName(tag)) = true;
8745 },
8746 }
8747
8748 node = node_datas[node].lhs;
8749 }
8750
8751 const flags_i: u5 = @bitCast(flags);
8752 assert(flags_i != 0);
8753
8754 const ptr_only: Zir.Inst.FullPtrCastFlags = .{ .ptr_cast = true };
8755 if (flags_i == @as(u5, @bitCast(ptr_only))) {
8756 // Special case: simpler representation
8757 return typeCast(gz, scope, ri, root_node, node, .ptr_cast, "@ptrCast");
8758 }
8759
8760 const no_result_ty_flags: Zir.Inst.FullPtrCastFlags = .{
8761 .const_cast = true,
8762 .volatile_cast = true,
8763 };
8764 if ((flags_i & ~@as(u5, @bitCast(no_result_ty_flags))) == 0) {
8765 // Result type not needed
8766 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
8767 const operand = try expr(gz, scope, .{ .rl = .none }, node);
8768 try emitDbgStmt(gz, cursor);
8769 const result = try gz.addExtendedPayloadSmall(.ptr_cast_no_dest, flags_i, Zir.Inst.UnNode{
8770 .node = gz.nodeIndexToRelative(root_node),
8771 .operand = operand,
8772 });
8773 return rvalue(gz, ri, result, root_node);
8774 }
8775
8776 // Full cast including result type
8777
8778 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
8779 const result_type = try ri.rl.resultTypeForCast(gz, root_node, flags.needResultTypeBuiltinName());
8780 const operand = try expr(gz, scope, .{ .rl = .none }, node);
8781 try emitDbgStmt(gz, cursor);
8782 const result = try gz.addExtendedPayloadSmall(.ptr_cast_full, flags_i, Zir.Inst.BinNode{
8783 .node = gz.nodeIndexToRelative(root_node),
8784 .lhs = result_type,
8785 .rhs = operand,
8786 });
8787 return rvalue(gz, ri, result, root_node);
8788}
8789
8790fn typeOf(
8791 gz: *GenZir,
8792 scope: *Scope,
8793 ri: ResultInfo,
8794 node: Ast.Node.Index,
8795 args: []const Ast.Node.Index,
8796) InnerError!Zir.Inst.Ref {
8797 const astgen = gz.astgen;
8798 if (args.len < 1) {
8799 return astgen.failNode(node, "expected at least 1 argument, found 0", .{});
8800 }
8801 const gpa = astgen.gpa;
8802 if (args.len == 1) {
8803 const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node);
8804
8805 var typeof_scope = gz.makeSubBlock(scope);
8806 typeof_scope.is_comptime = false;
8807 typeof_scope.is_typeof = true;
8808 typeof_scope.c_import = false;
8809 defer typeof_scope.unstack();
8810
8811 const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, args[0], node);
8812 if (!gz.refIsNoReturn(ty_expr)) {
8813 _ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr);
8814 }
8815 try typeof_scope.setBlockBody(typeof_inst);
8816
8817 // typeof_scope unstacked now, can add new instructions to gz
8818 try gz.instructions.append(gpa, typeof_inst);
8819 return rvalue(gz, ri, typeof_inst.toRef(), node);
8820 }
8821 const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len;
8822 const payload_index = try reserveExtra(astgen, payload_size + args.len);
8823 const args_index = payload_index + payload_size;
8824
8825 const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len);
8826
8827 var typeof_scope = gz.makeSubBlock(scope);
8828 typeof_scope.is_comptime = false;
8829
8830 for (args, 0..) |arg, i| {
8831 const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, arg, node);
8832 astgen.extra.items[args_index + i] = @intFromEnum(param_ref);
8833 }
8834 _ = try typeof_scope.addBreak(.break_inline, typeof_inst.toIndex().?, .void_value);
8835
8836 const body = typeof_scope.instructionsSlice();
8837 const body_len = astgen.countBodyLenAfterFixups(body);
8838 astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{
8839 .body_len = @intCast(body_len),
8840 .body_index = @intCast(astgen.extra.items.len),
8841 .src_node = gz.nodeIndexToRelative(node),
8842 });
8843 try astgen.extra.ensureUnusedCapacity(gpa, body_len);
8844 astgen.appendBodyWithFixups(body);
8845 typeof_scope.unstack();
8846
8847 return rvalue(gz, ri, typeof_inst, node);
8848}
8849
8850fn minMax(
8851 gz: *GenZir,
8852 scope: *Scope,
8853 ri: ResultInfo,
8854 node: Ast.Node.Index,
8855 args: []const Ast.Node.Index,
8856 comptime op: enum { min, max },
8857) InnerError!Zir.Inst.Ref {
8858 const astgen = gz.astgen;
8859 if (args.len < 2) {
8860 return astgen.failNode(node, "expected at least 2 arguments, found 0", .{});
8861 }
8862 if (args.len == 2) {
8863 const tag: Zir.Inst.Tag = switch (op) {
8864 .min => .min,
8865 .max => .max,
8866 };
8867 const a = try expr(gz, scope, .{ .rl = .none }, args[0]);
8868 const b = try expr(gz, scope, .{ .rl = .none }, args[1]);
8869 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
8870 .lhs = a,
8871 .rhs = b,
8872 });
8873 return rvalue(gz, ri, result, node);
8874 }
8875 const payload_index = try addExtra(astgen, Zir.Inst.NodeMultiOp{
8876 .src_node = gz.nodeIndexToRelative(node),
8877 });
8878 var extra_index = try reserveExtra(gz.astgen, args.len);
8879 for (args) |arg| {
8880 const arg_ref = try expr(gz, scope, .{ .rl = .none }, arg);
8881 astgen.extra.items[extra_index] = @intFromEnum(arg_ref);
8882 extra_index += 1;
8883 }
8884 const tag: Zir.Inst.Extended = switch (op) {
8885 .min => .min_multi,
8886 .max => .max_multi,
8887 };
8888 const result = try gz.addExtendedMultiOpPayloadIndex(tag, payload_index, args.len);
8889 return rvalue(gz, ri, result, node);
8890}
8891
8892fn builtinCall(
8893 gz: *GenZir,
8894 scope: *Scope,
8895 ri: ResultInfo,
8896 node: Ast.Node.Index,
8897 params: []const Ast.Node.Index,
8898) InnerError!Zir.Inst.Ref {
8899 const astgen = gz.astgen;
8900 const tree = astgen.tree;
8901 const main_tokens = tree.nodes.items(.main_token);
8902
8903 const builtin_token = main_tokens[node];
8904 const builtin_name = tree.tokenSlice(builtin_token);
8905
8906 // We handle the different builtins manually because they have different semantics depending
8907 // on the function. For example, `@as` and others participate in result location semantics,
8908 // and `@cImport` creates a special scope that collects a .c source code text buffer.
8909 // Also, some builtins have a variable number of parameters.
8910
8911 const info = BuiltinFn.list.get(builtin_name) orelse {
8912 return astgen.failNode(node, "invalid builtin function: '{s}'", .{
8913 builtin_name,
8914 });
8915 };
8916 if (info.param_count) |expected| {
8917 if (expected != params.len) {
8918 const s = if (expected == 1) "" else "s";
8919 return astgen.failNode(node, "expected {d} argument{s}, found {d}", .{
8920 expected, s, params.len,
8921 });
8922 }
8923 }
8924
8925 // Check function scope-only builtins
8926
8927 if (astgen.fn_block == null and info.illegal_outside_function)
8928 return astgen.failNode(node, "'{s}' outside function scope", .{builtin_name});
8929
8930 switch (info.tag) {
8931 .import => {
8932 const node_tags = tree.nodes.items(.tag);
8933 const operand_node = params[0];
8934
8935 if (node_tags[operand_node] != .string_literal) {
8936 // Spec reference: https://github.com/ziglang/zig/issues/2206
8937 return astgen.failNode(operand_node, "@import operand must be a string literal", .{});
8938 }
8939 const str_lit_token = main_tokens[operand_node];
8940 const str = try astgen.strLitAsString(str_lit_token);
8941 const str_slice = astgen.string_bytes.items[@intFromEnum(str.index)..][0..str.len];
8942 if (mem.indexOfScalar(u8, str_slice, 0) != null) {
8943 return astgen.failTok(str_lit_token, "import path cannot contain null bytes", .{});
8944 } else if (str.len == 0) {
8945 return astgen.failTok(str_lit_token, "import path cannot be empty", .{});
8946 }
8947 const result = try gz.addStrTok(.import, str.index, str_lit_token);
8948 const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
8949 if (!gop.found_existing) {
8950 gop.value_ptr.* = str_lit_token;
8951 }
8952 return rvalue(gz, ri, result, node);
8953 },
8954 .compile_log => {
8955 const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{
8956 .src_node = gz.nodeIndexToRelative(node),
8957 });
8958 var extra_index = try reserveExtra(gz.astgen, params.len);
8959 for (params) |param| {
8960 const param_ref = try expr(gz, scope, .{ .rl = .none }, param);
8961 astgen.extra.items[extra_index] = @intFromEnum(param_ref);
8962 extra_index += 1;
8963 }
8964 const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len);
8965 return rvalue(gz, ri, result, node);
8966 },
8967 .field => {
8968 if (ri.rl == .ref or ri.rl == .ref_coerced_ty) {
8969 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
8970 .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]),
8971 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]),
8972 });
8973 }
8974 const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
8975 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
8976 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]),
8977 });
8978 return rvalue(gz, ri, result, node);
8979 },
8980
8981 // zig fmt: off
8982 .as => return as( gz, scope, ri, node, params[0], params[1]),
8983 .bit_cast => return bitCast( gz, scope, ri, node, params[0]),
8984 .TypeOf => return typeOf( gz, scope, ri, node, params),
8985 .union_init => return unionInit(gz, scope, ri, node, params),
8986 .c_import => return cImport( gz, scope, node, params[0]),
8987 .min => return minMax( gz, scope, ri, node, params, .min),
8988 .max => return minMax( gz, scope, ri, node, params, .max),
8989 // zig fmt: on
8990
8991 .@"export" => {
8992 const node_tags = tree.nodes.items(.tag);
8993 const node_datas = tree.nodes.items(.data);
8994 // This function causes a Decl to be exported. The first parameter is not an expression,
8995 // but an identifier of the Decl to be exported.
8996 var namespace: Zir.Inst.Ref = .none;
8997 var decl_name: Zir.NullTerminatedString = .empty;
8998 switch (node_tags[params[0]]) {
8999 .identifier => {
9000 const ident_token = main_tokens[params[0]];
9001 if (isPrimitive(tree.tokenSlice(ident_token))) {
9002 return astgen.failTok(ident_token, "unable to export primitive value", .{});
9003 }
9004 decl_name = try astgen.identAsString(ident_token);
9005
9006 var s = scope;
9007 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
9008 while (true) switch (s.tag) {
9009 .local_val => {
9010 const local_val = s.cast(Scope.LocalVal).?;
9011 if (local_val.name == decl_name) {
9012 local_val.used = ident_token;
9013 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
9014 .operand = local_val.inst,
9015 .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]),
9016 });
9017 return rvalue(gz, ri, .void_value, node);
9018 }
9019 s = local_val.parent;
9020 },
9021 .local_ptr => {
9022 const local_ptr = s.cast(Scope.LocalPtr).?;
9023 if (local_ptr.name == decl_name) {
9024 if (!local_ptr.maybe_comptime)
9025 return astgen.failNode(params[0], "unable to export runtime-known value", .{});
9026 local_ptr.used = ident_token;
9027 const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);
9028 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
9029 .operand = loaded,
9030 .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]),
9031 });
9032 return rvalue(gz, ri, .void_value, node);
9033 }
9034 s = local_ptr.parent;
9035 },
9036 .gen_zir => s = s.cast(GenZir).?.parent,
9037 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
9038 .namespace, .enum_namespace => {
9039 const ns = s.cast(Scope.Namespace).?;
9040 if (ns.decls.get(decl_name)) |i| {
9041 if (found_already) |f| {
9042 return astgen.failNodeNotes(node, "ambiguous reference", .{}, &.{
9043 try astgen.errNoteNode(f, "declared here", .{}),
9044 try astgen.errNoteNode(i, "also declared here", .{}),
9045 });
9046 }
9047 // We found a match but must continue looking for ambiguous references to decls.
9048 found_already = i;
9049 }
9050 s = ns.parent;
9051 },
9052 .top => break,
9053 };
9054 if (found_already == null) {
9055 const ident_name = try astgen.identifierTokenString(ident_token);
9056 return astgen.failNode(params[0], "use of undeclared identifier '{s}'", .{ident_name});
9057 }
9058 },
9059 .field_access => {
9060 const namespace_node = node_datas[params[0]].lhs;
9061 namespace = try typeExpr(gz, scope, namespace_node);
9062 const dot_token = main_tokens[params[0]];
9063 const field_ident = dot_token + 1;
9064 decl_name = try astgen.identAsString(field_ident);
9065 },
9066 else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),
9067 }
9068 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]);
9069 _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
9070 .namespace = namespace,
9071 .decl_name = decl_name,
9072 .options = options,
9073 });
9074 return rvalue(gz, ri, .void_value, node);
9075 },
9076 .@"extern" => {
9077 const type_inst = try typeExpr(gz, scope, params[0]);
9078 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .extern_options_type } }, params[1]);
9079 const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{
9080 .node = gz.nodeIndexToRelative(node),
9081 .lhs = type_inst,
9082 .rhs = options,
9083 });
9084 return rvalue(gz, ri, result, node);
9085 },
9086 .fence => {
9087 const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[0]);
9088 _ = try gz.addExtendedPayload(.fence, Zir.Inst.UnNode{
9089 .node = gz.nodeIndexToRelative(node),
9090 .operand = order,
9091 });
9092 return rvalue(gz, ri, .void_value, node);
9093 },
9094 .set_float_mode => {
9095 const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .float_mode_type } }, params[0]);
9096 _ = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{
9097 .node = gz.nodeIndexToRelative(node),
9098 .operand = order,
9099 });
9100 return rvalue(gz, ri, .void_value, node);
9101 },
9102 .set_align_stack => {
9103 const order = try expr(gz, scope, coerced_align_ri, params[0]);
9104 _ = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{
9105 .node = gz.nodeIndexToRelative(node),
9106 .operand = order,
9107 });
9108 return rvalue(gz, ri, .void_value, node);
9109 },
9110 .set_cold => {
9111 const order = try expr(gz, scope, ri, params[0]);
9112 _ = try gz.addExtendedPayload(.set_cold, Zir.Inst.UnNode{
9113 .node = gz.nodeIndexToRelative(node),
9114 .operand = order,
9115 });
9116 return rvalue(gz, ri, .void_value, node);
9117 },
9118
9119 .src => {
9120 const token_starts = tree.tokens.items(.start);
9121 const node_start = token_starts[tree.firstToken(node)];
9122 astgen.advanceSourceCursor(node_start);
9123 const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.Src{
9124 .node = gz.nodeIndexToRelative(node),
9125 .line = astgen.source_line,
9126 .column = astgen.source_column,
9127 });
9128 return rvalue(gz, ri, result, node);
9129 },
9130
9131 // zig fmt: off
9132 .This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node),
9133 .return_address => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr, node), node),
9134 .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),
9135 .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node),
9136 .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node),
9137 .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),
9138 .in_comptime => return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime, node), node),
9139
9140 .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
9141 .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
9142 .bit_size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .bit_size_of),
9143 .align_of => return simpleUnOpType(gz, scope, ri, node, params[0], .align_of),
9144
9145 .int_from_ptr => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_ptr),
9146 .compile_error => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .compile_error),
9147 .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota),
9148 .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum),
9149 .int_from_bool => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_bool),
9150 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file),
9151 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name),
9152 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety),
9153 .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt),
9154 .sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin),
9155 .cos => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .cos),
9156 .tan => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tan),
9157 .exp => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .exp),
9158 .exp2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .exp2),
9159 .log => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log),
9160 .log2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log2),
9161 .log10 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log10),
9162 .abs => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .abs),
9163 .floor => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .floor),
9164 .ceil => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .ceil),
9165 .trunc => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .trunc),
9166 .round => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .round),
9167 .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name),
9168 .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name),
9169 .Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type),
9170 .frame_size => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_size),
9171
9172 .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),
9173 .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),
9174 .ptr_from_int => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name),
9175 .enum_from_int => return typeCast(gz, scope, ri, node, params[0], .enum_from_int, builtin_name),
9176 .float_cast => return typeCast(gz, scope, ri, node, params[0], .float_cast, builtin_name),
9177 .int_cast => return typeCast(gz, scope, ri, node, params[0], .int_cast, builtin_name),
9178 .truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name),
9179 // zig fmt: on
9180
9181 .Type => {
9182 const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .type_info_type } }, params[0]);
9183
9184 const gpa = gz.astgen.gpa;
9185
9186 try gz.instructions.ensureUnusedCapacity(gpa, 1);
9187 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
9188
9189 const payload_index = try gz.astgen.addExtra(Zir.Inst.UnNode{
9190 .node = gz.nodeIndexToRelative(node),
9191 .operand = operand,
9192 });
9193 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
9194 gz.astgen.instructions.appendAssumeCapacity(.{
9195 .tag = .extended,
9196 .data = .{ .extended = .{
9197 .opcode = .reify,
9198 .small = @intFromEnum(gz.anon_name_strategy),
9199 .operand = payload_index,
9200 } },
9201 });
9202 gz.instructions.appendAssumeCapacity(new_index);
9203 const result = new_index.toRef();
9204 return rvalue(gz, ri, result, node);
9205 },
9206 .panic => {
9207 try emitDbgNode(gz, node);
9208 return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .panic);
9209 },
9210 .trap => {
9211 try emitDbgNode(gz, node);
9212 _ = try gz.addNode(.trap, node);
9213 return rvalue(gz, ri, .unreachable_value, node);
9214 },
9215 .int_from_error => {
9216 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
9217 const result = try gz.addExtendedPayload(.int_from_error, Zir.Inst.UnNode{
9218 .node = gz.nodeIndexToRelative(node),
9219 .operand = operand,
9220 });
9221 return rvalue(gz, ri, result, node);
9222 },
9223 .error_from_int => {
9224 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
9225 const result = try gz.addExtendedPayload(.error_from_int, Zir.Inst.UnNode{
9226 .node = gz.nodeIndexToRelative(node),
9227 .operand = operand,
9228 });
9229 return rvalue(gz, ri, result, node);
9230 },
9231 .error_cast => {
9232 try emitDbgNode(gz, node);
9233
9234 const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{
9235 .lhs = try ri.rl.resultTypeForCast(gz, node, "@errorCast"),
9236 .rhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
9237 .node = gz.nodeIndexToRelative(node),
9238 });
9239 return rvalue(gz, ri, result, node);
9240 },
9241 .ptr_cast,
9242 .align_cast,
9243 .addrspace_cast,
9244 .const_cast,
9245 .volatile_cast,
9246 => return ptrCast(gz, scope, ri, node),
9247
9248 // zig fmt: off
9249 .has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
9250 .has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field),
9251
9252 .clz => return bitBuiltin(gz, scope, ri, node, params[0], .clz),
9253 .ctz => return bitBuiltin(gz, scope, ri, node, params[0], .ctz),
9254 .pop_count => return bitBuiltin(gz, scope, ri, node, params[0], .pop_count),
9255 .byte_swap => return bitBuiltin(gz, scope, ri, node, params[0], .byte_swap),
9256 .bit_reverse => return bitBuiltin(gz, scope, ri, node, params[0], .bit_reverse),
9257
9258 .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),
9259 .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor),
9260 .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),
9261 .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod),
9262 .rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem),
9263
9264 .shl_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shl_exact),
9265 .shr_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shr_exact),
9266
9267 .bit_offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .bit_offset_of),
9268 .offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .offset_of),
9269
9270 .c_undef => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_undef),
9271 .c_include => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_include),
9272
9273 .cmpxchg_strong => return cmpxchg(gz, scope, ri, node, params, 1),
9274 .cmpxchg_weak => return cmpxchg(gz, scope, ri, node, params, 0),
9275 // zig fmt: on
9276
9277 .wasm_memory_size => {
9278 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
9279 const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{
9280 .node = gz.nodeIndexToRelative(node),
9281 .operand = operand,
9282 });
9283 return rvalue(gz, ri, result, node);
9284 },
9285 .wasm_memory_grow => {
9286 const index_arg = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
9287 const delta_arg = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[1]);
9288 const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{
9289 .node = gz.nodeIndexToRelative(node),
9290 .lhs = index_arg,
9291 .rhs = delta_arg,
9292 });
9293 return rvalue(gz, ri, result, node);
9294 },
9295 .c_define => {
9296 if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{});
9297 const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0]);
9298 const value = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]);
9299 const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{
9300 .node = gz.nodeIndexToRelative(node),
9301 .lhs = name,
9302 .rhs = value,
9303 });
9304 return rvalue(gz, ri, result, node);
9305 },
9306
9307 .splat => {
9308 const result_type = try ri.rl.resultTypeForCast(gz, node, "@splat");
9309 const elem_type = try gz.addUnNode(.vector_elem_type, result_type, node);
9310 const scalar = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, params[0]);
9311 const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{
9312 .lhs = result_type,
9313 .rhs = scalar,
9314 });
9315 return rvalue(gz, ri, result, node);
9316 },
9317 .reduce => {
9318 const op = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .reduce_op_type } }, params[0]);
9319 const scalar = try expr(gz, scope, .{ .rl = .none }, params[1]);
9320 const result = try gz.addPlNode(.reduce, node, Zir.Inst.Bin{
9321 .lhs = op,
9322 .rhs = scalar,
9323 });
9324 return rvalue(gz, ri, result, node);
9325 },
9326
9327 .add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow),
9328 .sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow),
9329 .mul_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .mul_with_overflow),
9330 .shl_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .shl_with_overflow),
9331
9332 .atomic_load => {
9333 const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{
9334 // zig fmt: off
9335 .elem_type = try typeExpr(gz, scope, params[0]),
9336 .ptr = try expr (gz, scope, .{ .rl = .none }, params[1]),
9337 .ordering = try expr (gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[2]),
9338 // zig fmt: on
9339 });
9340 return rvalue(gz, ri, result, node);
9341 },
9342 .atomic_rmw => {
9343 const int_type = try typeExpr(gz, scope, params[0]);
9344 const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{
9345 // zig fmt: off
9346 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
9347 .operation = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_rmw_op_type } }, params[2]),
9348 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[3]),
9349 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[4]),
9350 // zig fmt: on
9351 });
9352 return rvalue(gz, ri, result, node);
9353 },
9354 .atomic_store => {
9355 const int_type = try typeExpr(gz, scope, params[0]);
9356 _ = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{
9357 // zig fmt: off
9358 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
9359 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]),
9360 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[3]),
9361 // zig fmt: on
9362 });
9363 return rvalue(gz, ri, .void_value, node);
9364 },
9365 .mul_add => {
9366 const float_type = try typeExpr(gz, scope, params[0]);
9367 const mulend1 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[1]);
9368 const mulend2 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[2]);
9369 const addend = try expr(gz, scope, .{ .rl = .{ .ty = float_type } }, params[3]);
9370 const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{
9371 .mulend1 = mulend1,
9372 .mulend2 = mulend2,
9373 .addend = addend,
9374 });
9375 return rvalue(gz, ri, result, node);
9376 },
9377 .call => {
9378 const modifier = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .call_modifier_type } }, params[0]);
9379 const callee = try expr(gz, scope, .{ .rl = .none }, params[1]);
9380 const args = try expr(gz, scope, .{ .rl = .none }, params[2]);
9381 const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{
9382 .modifier = modifier,
9383 .callee = callee,
9384 .args = args,
9385 .flags = .{
9386 .is_nosuspend = gz.nosuspend_node != 0,
9387 .ensure_result_used = false,
9388 },
9389 });
9390 return rvalue(gz, ri, result, node);
9391 },
9392 .field_parent_ptr => {
9393 const parent_type = try typeExpr(gz, scope, params[0]);
9394 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]);
9395 const result = try gz.addPlNode(.field_parent_ptr, node, Zir.Inst.FieldParentPtr{
9396 .parent_type = parent_type,
9397 .field_name = field_name,
9398 .field_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]),
9399 });
9400 return rvalue(gz, ri, result, node);
9401 },
9402 .memcpy => {
9403 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{
9404 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
9405 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
9406 });
9407 return rvalue(gz, ri, .void_value, node);
9408 },
9409 .memset => {
9410 const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]);
9411 const lhs_ty = try gz.addUnNode(.typeof, lhs, params[0]);
9412 const elem_ty = try gz.addUnNode(.indexable_ptr_elem_type, lhs_ty, params[0]);
9413 _ = try gz.addPlNode(.memset, node, Zir.Inst.Bin{
9414 .lhs = lhs,
9415 .rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = elem_ty } }, params[1]),
9416 });
9417 return rvalue(gz, ri, .void_value, node);
9418 },
9419 .shuffle => {
9420 const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
9421 .elem_type = try typeExpr(gz, scope, params[0]),
9422 .a = try expr(gz, scope, .{ .rl = .none }, params[1]),
9423 .b = try expr(gz, scope, .{ .rl = .none }, params[2]),
9424 .mask = try comptimeExpr(gz, scope, .{ .rl = .none }, params[3]),
9425 });
9426 return rvalue(gz, ri, result, node);
9427 },
9428 .select => {
9429 const result = try gz.addExtendedPayload(.select, Zir.Inst.Select{
9430 .node = gz.nodeIndexToRelative(node),
9431 .elem_type = try typeExpr(gz, scope, params[0]),
9432 .pred = try expr(gz, scope, .{ .rl = .none }, params[1]),
9433 .a = try expr(gz, scope, .{ .rl = .none }, params[2]),
9434 .b = try expr(gz, scope, .{ .rl = .none }, params[3]),
9435 });
9436 return rvalue(gz, ri, result, node);
9437 },
9438 .async_call => {
9439 const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{
9440 .node = gz.nodeIndexToRelative(node),
9441 .frame_buffer = try expr(gz, scope, .{ .rl = .none }, params[0]),
9442 .result_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
9443 .fn_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]),
9444 .args = try expr(gz, scope, .{ .rl = .none }, params[3]),
9445 });
9446 return rvalue(gz, ri, result, node);
9447 },
9448 .Vector => {
9449 const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{
9450 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]),
9451 .rhs = try typeExpr(gz, scope, params[1]),
9452 });
9453 return rvalue(gz, ri, result, node);
9454 },
9455 .prefetch => {
9456 const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);
9457 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .prefetch_options_type } }, params[1]);
9458 _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
9459 .node = gz.nodeIndexToRelative(node),
9460 .lhs = ptr,
9461 .rhs = options,
9462 });
9463 return rvalue(gz, ri, .void_value, node);
9464 },
9465 .c_va_arg => {
9466 const result = try gz.addExtendedPayload(.c_va_arg, Zir.Inst.BinNode{
9467 .node = gz.nodeIndexToRelative(node),
9468 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
9469 .rhs = try typeExpr(gz, scope, params[1]),
9470 });
9471 return rvalue(gz, ri, result, node);
9472 },
9473 .c_va_copy => {
9474 const result = try gz.addExtendedPayload(.c_va_copy, Zir.Inst.UnNode{
9475 .node = gz.nodeIndexToRelative(node),
9476 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
9477 });
9478 return rvalue(gz, ri, result, node);
9479 },
9480 .c_va_end => {
9481 const result = try gz.addExtendedPayload(.c_va_end, Zir.Inst.UnNode{
9482 .node = gz.nodeIndexToRelative(node),
9483 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
9484 });
9485 return rvalue(gz, ri, result, node);
9486 },
9487 .c_va_start => {
9488 if (!astgen.fn_var_args) {
9489 return astgen.failNode(node, "'@cVaStart' in a non-variadic function", .{});
9490 }
9491 return rvalue(gz, ri, try gz.addNodeExtended(.c_va_start, node), node);
9492 },
9493
9494 .work_item_id => {
9495 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
9496 const result = try gz.addExtendedPayload(.work_item_id, Zir.Inst.UnNode{
9497 .node = gz.nodeIndexToRelative(node),
9498 .operand = operand,
9499 });
9500 return rvalue(gz, ri, result, node);
9501 },
9502 .work_group_size => {
9503 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
9504 const result = try gz.addExtendedPayload(.work_group_size, Zir.Inst.UnNode{
9505 .node = gz.nodeIndexToRelative(node),
9506 .operand = operand,
9507 });
9508 return rvalue(gz, ri, result, node);
9509 },
9510 .work_group_id => {
9511 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
9512 const result = try gz.addExtendedPayload(.work_group_id, Zir.Inst.UnNode{
9513 .node = gz.nodeIndexToRelative(node),
9514 .operand = operand,
9515 });
9516 return rvalue(gz, ri, result, node);
9517 },
9518 }
9519}
9520
9521fn hasDeclOrField(
9522 gz: *GenZir,
9523 scope: *Scope,
9524 ri: ResultInfo,
9525 node: Ast.Node.Index,
9526 lhs_node: Ast.Node.Index,
9527 rhs_node: Ast.Node.Index,
9528 tag: Zir.Inst.Tag,
9529) InnerError!Zir.Inst.Ref {
9530 const container_type = try typeExpr(gz, scope, lhs_node);
9531 const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node);
9532 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
9533 .lhs = container_type,
9534 .rhs = name,
9535 });
9536 return rvalue(gz, ri, result, node);
9537}
9538
9539fn typeCast(
9540 gz: *GenZir,
9541 scope: *Scope,
9542 ri: ResultInfo,
9543 node: Ast.Node.Index,
9544 operand_node: Ast.Node.Index,
9545 tag: Zir.Inst.Tag,
9546 builtin_name: []const u8,
9547) InnerError!Zir.Inst.Ref {
9548 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
9549 const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);
9550 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
9551
9552 try emitDbgStmt(gz, cursor);
9553 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
9554 .lhs = result_type,
9555 .rhs = operand,
9556 });
9557 return rvalue(gz, ri, result, node);
9558}
9559
9560fn simpleUnOpType(
9561 gz: *GenZir,
9562 scope: *Scope,
9563 ri: ResultInfo,
9564 node: Ast.Node.Index,
9565 operand_node: Ast.Node.Index,
9566 tag: Zir.Inst.Tag,
9567) InnerError!Zir.Inst.Ref {
9568 const operand = try typeExpr(gz, scope, operand_node);
9569 const result = try gz.addUnNode(tag, operand, node);
9570 return rvalue(gz, ri, result, node);
9571}
9572
9573fn simpleUnOp(
9574 gz: *GenZir,
9575 scope: *Scope,
9576 ri: ResultInfo,
9577 node: Ast.Node.Index,
9578 operand_ri: ResultInfo,
9579 operand_node: Ast.Node.Index,
9580 tag: Zir.Inst.Tag,
9581) InnerError!Zir.Inst.Ref {
9582 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
9583 const operand = if (tag == .compile_error)
9584 try comptimeExpr(gz, scope, operand_ri, operand_node)
9585 else
9586 try expr(gz, scope, operand_ri, operand_node);
9587 switch (tag) {
9588 .tag_name, .error_name, .int_from_ptr => try emitDbgStmt(gz, cursor),
9589 else => {},
9590 }
9591 const result = try gz.addUnNode(tag, operand, node);
9592 return rvalue(gz, ri, result, node);
9593}
9594
9595fn negation(
9596 gz: *GenZir,
9597 scope: *Scope,
9598 ri: ResultInfo,
9599 node: Ast.Node.Index,
9600) InnerError!Zir.Inst.Ref {
9601 const astgen = gz.astgen;
9602 const tree = astgen.tree;
9603 const node_tags = tree.nodes.items(.tag);
9604 const node_datas = tree.nodes.items(.data);
9605
9606 // Check for float literal as the sub-expression because we want to preserve
9607 // its negativity rather than having it go through comptime subtraction.
9608 const operand_node = node_datas[node].lhs;
9609 if (node_tags[operand_node] == .number_literal) {
9610 return numberLiteral(gz, ri, operand_node, node, .negative);
9611 }
9612
9613 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
9614 const result = try gz.addUnNode(.negate, operand, node);
9615 return rvalue(gz, ri, result, node);
9616}
9617
9618fn cmpxchg(
9619 gz: *GenZir,
9620 scope: *Scope,
9621 ri: ResultInfo,
9622 node: Ast.Node.Index,
9623 params: []const Ast.Node.Index,
9624 small: u16,
9625) InnerError!Zir.Inst.Ref {
9626 const int_type = try typeExpr(gz, scope, params[0]);
9627 const result = try gz.addExtendedPayloadSmall(.cmpxchg, small, Zir.Inst.Cmpxchg{
9628 // zig fmt: off
9629 .node = gz.nodeIndexToRelative(node),
9630 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
9631 .expected_value = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]),
9632 .new_value = try expr(gz, scope, .{ .rl = .{ .coerced_ty = int_type } }, params[3]),
9633 .success_order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[4]),
9634 .failure_order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[5]),
9635 // zig fmt: on
9636 });
9637 return rvalue(gz, ri, result, node);
9638}
9639
9640fn bitBuiltin(
9641 gz: *GenZir,
9642 scope: *Scope,
9643 ri: ResultInfo,
9644 node: Ast.Node.Index,
9645 operand_node: Ast.Node.Index,
9646 tag: Zir.Inst.Tag,
9647) InnerError!Zir.Inst.Ref {
9648 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
9649 const result = try gz.addUnNode(tag, operand, node);
9650 return rvalue(gz, ri, result, node);
9651}
9652
9653fn divBuiltin(
9654 gz: *GenZir,
9655 scope: *Scope,
9656 ri: ResultInfo,
9657 node: Ast.Node.Index,
9658 lhs_node: Ast.Node.Index,
9659 rhs_node: Ast.Node.Index,
9660 tag: Zir.Inst.Tag,
9661) InnerError!Zir.Inst.Ref {
9662 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
9663 const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
9664 const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node);
9665
9666 try emitDbgStmt(gz, cursor);
9667 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
9668 return rvalue(gz, ri, result, node);
9669}
9670
9671fn simpleCBuiltin(
9672 gz: *GenZir,
9673 scope: *Scope,
9674 ri: ResultInfo,
9675 node: Ast.Node.Index,
9676 operand_node: Ast.Node.Index,
9677 tag: Zir.Inst.Extended,
9678) InnerError!Zir.Inst.Ref {
9679 const name: []const u8 = if (tag == .c_undef) "C undef" else "C include";
9680 if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name});
9681 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, operand_node);
9682 _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{
9683 .node = gz.nodeIndexToRelative(node),
9684 .operand = operand,
9685 });
9686 return rvalue(gz, ri, .void_value, node);
9687}
9688
9689fn offsetOf(
9690 gz: *GenZir,
9691 scope: *Scope,
9692 ri: ResultInfo,
9693 node: Ast.Node.Index,
9694 lhs_node: Ast.Node.Index,
9695 rhs_node: Ast.Node.Index,
9696 tag: Zir.Inst.Tag,
9697) InnerError!Zir.Inst.Ref {
9698 const type_inst = try typeExpr(gz, scope, lhs_node);
9699 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node);
9700 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
9701 .lhs = type_inst,
9702 .rhs = field_name,
9703 });
9704 return rvalue(gz, ri, result, node);
9705}
9706
9707fn shiftOp(
9708 gz: *GenZir,
9709 scope: *Scope,
9710 ri: ResultInfo,
9711 node: Ast.Node.Index,
9712 lhs_node: Ast.Node.Index,
9713 rhs_node: Ast.Node.Index,
9714 tag: Zir.Inst.Tag,
9715) InnerError!Zir.Inst.Ref {
9716 const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
9717
9718 const cursor = switch (gz.astgen.tree.nodes.items(.tag)[node]) {
9719 .shl, .shr => maybeAdvanceSourceCursorToMainToken(gz, node),
9720 else => undefined,
9721 };
9722
9723 const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node);
9724 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node);
9725
9726 switch (gz.astgen.tree.nodes.items(.tag)[node]) {
9727 .shl, .shr => try emitDbgStmt(gz, cursor),
9728 else => undefined,
9729 }
9730
9731 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
9732 .lhs = lhs,
9733 .rhs = rhs,
9734 });
9735 return rvalue(gz, ri, result, node);
9736}
9737
9738fn cImport(
9739 gz: *GenZir,
9740 scope: *Scope,
9741 node: Ast.Node.Index,
9742 body_node: Ast.Node.Index,
9743) InnerError!Zir.Inst.Ref {
9744 const astgen = gz.astgen;
9745 const gpa = astgen.gpa;
9746
9747 if (gz.c_import) return gz.astgen.failNode(node, "cannot nest @cImport", .{});
9748
9749 var block_scope = gz.makeSubBlock(scope);
9750 block_scope.is_comptime = true;
9751 block_scope.c_import = true;
9752 defer block_scope.unstack();
9753
9754 const block_inst = try gz.makeBlockInst(.c_import, node);
9755 const block_result = try expr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node);
9756 _ = try gz.addUnNode(.ensure_result_used, block_result, node);
9757 if (!gz.refIsNoReturn(block_result)) {
9758 _ = try block_scope.addBreak(.break_inline, block_inst, .void_value);
9759 }
9760 try block_scope.setBlockBody(block_inst);
9761 // block_scope unstacked now, can add new instructions to gz
9762 try gz.instructions.append(gpa, block_inst);
9763
9764 return block_inst.toRef();
9765}
9766
9767fn overflowArithmetic(
9768 gz: *GenZir,
9769 scope: *Scope,
9770 ri: ResultInfo,
9771 node: Ast.Node.Index,
9772 params: []const Ast.Node.Index,
9773 tag: Zir.Inst.Extended,
9774) InnerError!Zir.Inst.Ref {
9775 const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]);
9776 const rhs = try expr(gz, scope, .{ .rl = .none }, params[1]);
9777 const result = try gz.addExtendedPayload(tag, Zir.Inst.BinNode{
9778 .node = gz.nodeIndexToRelative(node),
9779 .lhs = lhs,
9780 .rhs = rhs,
9781 });
9782 return rvalue(gz, ri, result, node);
9783}
9784
9785fn callExpr(
9786 gz: *GenZir,
9787 scope: *Scope,
9788 ri: ResultInfo,
9789 node: Ast.Node.Index,
9790 call: Ast.full.Call,
9791) InnerError!Zir.Inst.Ref {
9792 const astgen = gz.astgen;
9793
9794 const callee = try calleeExpr(gz, scope, call.ast.fn_expr);
9795 const modifier: std.builtin.CallModifier = blk: {
9796 if (gz.is_comptime) {
9797 break :blk .compile_time;
9798 }
9799 if (call.async_token != null) {
9800 break :blk .async_kw;
9801 }
9802 if (gz.nosuspend_node != 0) {
9803 break :blk .no_async;
9804 }
9805 break :blk .auto;
9806 };
9807
9808 {
9809 astgen.advanceSourceCursor(astgen.tree.tokens.items(.start)[call.ast.lparen]);
9810 const line = astgen.source_line - gz.decl_line;
9811 const column = astgen.source_column;
9812 // Sema expects a dbg_stmt immediately before call,
9813 try emitDbgStmtForceCurrentIndex(gz, .{ line, column });
9814 }
9815
9816 switch (callee) {
9817 .direct => |obj| assert(obj != .none),
9818 .field => |field| assert(field.obj_ptr != .none),
9819 }
9820 assert(node != 0);
9821
9822 const call_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
9823 const call_inst = call_index.toRef();
9824 try gz.astgen.instructions.append(astgen.gpa, undefined);
9825 try gz.instructions.append(astgen.gpa, call_index);
9826
9827 const scratch_top = astgen.scratch.items.len;
9828 defer astgen.scratch.items.len = scratch_top;
9829
9830 var scratch_index = scratch_top;
9831 try astgen.scratch.resize(astgen.gpa, scratch_top + call.ast.params.len);
9832
9833 for (call.ast.params) |param_node| {
9834 var arg_block = gz.makeSubBlock(scope);
9835 defer arg_block.unstack();
9836
9837 // `call_inst` is reused to provide the param type.
9838 const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node);
9839 _ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node);
9840
9841 const body = arg_block.instructionsSlice();
9842 try astgen.scratch.ensureUnusedCapacity(astgen.gpa, countBodyLenAfterFixups(astgen, body));
9843 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
9844
9845 astgen.scratch.items[scratch_index] = @intCast(astgen.scratch.items.len - scratch_top);
9846 scratch_index += 1;
9847 }
9848
9849 // If our result location is a try/catch/error-union-if/return, a function argument,
9850 // or an initializer for a `const` variable, the error trace propagates.
9851 // Otherwise, it should always be popped (handled in Sema).
9852 const propagate_error_trace = switch (ri.ctx) {
9853 .error_handling_expr, .@"return", .fn_arg, .const_init => true,
9854 else => false,
9855 };
9856
9857 switch (callee) {
9858 .direct => |callee_obj| {
9859 const payload_index = try addExtra(astgen, Zir.Inst.Call{
9860 .callee = callee_obj,
9861 .flags = .{
9862 .pop_error_return_trace = !propagate_error_trace,
9863 .packed_modifier = @intCast(@intFromEnum(modifier)),
9864 .args_len = @intCast(call.ast.params.len),
9865 },
9866 });
9867 if (call.ast.params.len != 0) {
9868 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
9869 }
9870 gz.astgen.instructions.set(@intFromEnum(call_index), .{
9871 .tag = .call,
9872 .data = .{ .pl_node = .{
9873 .src_node = gz.nodeIndexToRelative(node),
9874 .payload_index = payload_index,
9875 } },
9876 });
9877 },
9878 .field => |callee_field| {
9879 const payload_index = try addExtra(astgen, Zir.Inst.FieldCall{
9880 .obj_ptr = callee_field.obj_ptr,
9881 .field_name_start = callee_field.field_name_start,
9882 .flags = .{
9883 .pop_error_return_trace = !propagate_error_trace,
9884 .packed_modifier = @intCast(@intFromEnum(modifier)),
9885 .args_len = @intCast(call.ast.params.len),
9886 },
9887 });
9888 if (call.ast.params.len != 0) {
9889 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
9890 }
9891 gz.astgen.instructions.set(@intFromEnum(call_index), .{
9892 .tag = .field_call,
9893 .data = .{ .pl_node = .{
9894 .src_node = gz.nodeIndexToRelative(node),
9895 .payload_index = payload_index,
9896 } },
9897 });
9898 },
9899 }
9900 return rvalue(gz, ri, call_inst, node); // TODO function call with result location
9901}
9902
9903const Callee = union(enum) {
9904 field: struct {
9905 /// A *pointer* to the object the field is fetched on, so that we can
9906 /// promote the lvalue to an address if the first parameter requires it.
9907 obj_ptr: Zir.Inst.Ref,
9908 /// Offset into `string_bytes`.
9909 field_name_start: Zir.NullTerminatedString,
9910 },
9911 direct: Zir.Inst.Ref,
9912};
9913
9914/// calleeExpr generates the function part of a call expression (f in f(x)), but
9915/// *not* the callee argument to the @call() builtin. Its purpose is to
9916/// distinguish between standard calls and method call syntax `a.b()`. Thus, if
9917/// the lhs is a field access, we return using the `field` union field;
9918/// otherwise, we use the `direct` union field.
9919fn calleeExpr(
9920 gz: *GenZir,
9921 scope: *Scope,
9922 node: Ast.Node.Index,
9923) InnerError!Callee {
9924 const astgen = gz.astgen;
9925 const tree = astgen.tree;
9926
9927 const tag = tree.nodes.items(.tag)[node];
9928 switch (tag) {
9929 .field_access => {
9930 const main_tokens = tree.nodes.items(.main_token);
9931 const node_datas = tree.nodes.items(.data);
9932 const object_node = node_datas[node].lhs;
9933 const dot_token = main_tokens[node];
9934 const field_ident = dot_token + 1;
9935 const str_index = try astgen.identAsString(field_ident);
9936 // Capture the object by reference so we can promote it to an
9937 // address in Sema if needed.
9938 const lhs = try expr(gz, scope, .{ .rl = .ref }, object_node);
9939
9940 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
9941 try emitDbgStmt(gz, cursor);
9942
9943 return .{ .field = .{
9944 .obj_ptr = lhs,
9945 .field_name_start = str_index,
9946 } };
9947 },
9948 else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) },
9949 }
9950}
9951
9952const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{
9953 .{ "anyerror", .anyerror_type },
9954 .{ "anyframe", .anyframe_type },
9955 .{ "anyopaque", .anyopaque_type },
9956 .{ "bool", .bool_type },
9957 .{ "c_int", .c_int_type },
9958 .{ "c_long", .c_long_type },
9959 .{ "c_longdouble", .c_longdouble_type },
9960 .{ "c_longlong", .c_longlong_type },
9961 .{ "c_char", .c_char_type },
9962 .{ "c_short", .c_short_type },
9963 .{ "c_uint", .c_uint_type },
9964 .{ "c_ulong", .c_ulong_type },
9965 .{ "c_ulonglong", .c_ulonglong_type },
9966 .{ "c_ushort", .c_ushort_type },
9967 .{ "comptime_float", .comptime_float_type },
9968 .{ "comptime_int", .comptime_int_type },
9969 .{ "f128", .f128_type },
9970 .{ "f16", .f16_type },
9971 .{ "f32", .f32_type },
9972 .{ "f64", .f64_type },
9973 .{ "f80", .f80_type },
9974 .{ "false", .bool_false },
9975 .{ "i16", .i16_type },
9976 .{ "i32", .i32_type },
9977 .{ "i64", .i64_type },
9978 .{ "i128", .i128_type },
9979 .{ "i8", .i8_type },
9980 .{ "isize", .isize_type },
9981 .{ "noreturn", .noreturn_type },
9982 .{ "null", .null_value },
9983 .{ "true", .bool_true },
9984 .{ "type", .type_type },
9985 .{ "u16", .u16_type },
9986 .{ "u29", .u29_type },
9987 .{ "u32", .u32_type },
9988 .{ "u64", .u64_type },
9989 .{ "u128", .u128_type },
9990 .{ "u1", .u1_type },
9991 .{ "u8", .u8_type },
9992 .{ "undefined", .undef },
9993 .{ "usize", .usize_type },
9994 .{ "void", .void_type },
9995});
9996
9997comptime {
9998 // These checks ensure that std.zig.primitives stays in sync with the primitive->Zir map.
9999 const primitives = std.zig.primitives;
10000 for (primitive_instrs.kvs) |kv| {
10001 if (!primitives.isPrimitive(kv.key)) {
10002 @compileError("std.zig.isPrimitive() is not aware of Zir instr '" ++ @tagName(kv.value) ++ "'");
10003 }
10004 }
10005 for (primitives.names.kvs) |kv| {
10006 if (primitive_instrs.get(kv.key) == null) {
10007 @compileError("std.zig.primitives entry '" ++ kv.key ++ "' does not have a corresponding Zir instr");
10008 }
10009 }
10010}
10011
10012fn nodeIsTriviallyZero(tree: *const Ast, node: Ast.Node.Index) bool {
10013 const node_tags = tree.nodes.items(.tag);
10014 const main_tokens = tree.nodes.items(.main_token);
10015
10016 switch (node_tags[node]) {
10017 .number_literal => {
10018 const ident = main_tokens[node];
10019 return switch (std.zig.parseNumberLiteral(tree.tokenSlice(ident))) {
10020 .int => |number| switch (number) {
10021 0 => true,
10022 else => false,
10023 },
10024 else => false,
10025 };
10026 },
10027 else => return false,
10028 }
10029}
10030
10031fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool {
10032 const node_tags = tree.nodes.items(.tag);
10033 const node_datas = tree.nodes.items(.data);
10034
10035 var node = start_node;
10036 while (true) {
10037 switch (node_tags[node]) {
10038 // These don't have the opportunity to call any runtime functions.
10039 .error_value,
10040 .identifier,
10041 .@"comptime",
10042 => return false,
10043
10044 // Forward the question to the LHS sub-expression.
10045 .grouped_expression,
10046 .@"try",
10047 .@"nosuspend",
10048 .unwrap_optional,
10049 => node = node_datas[node].lhs,
10050
10051 // Anything that does not eval to an error is guaranteed to pop any
10052 // additions to the error trace, so it effectively does not append.
10053 else => return nodeMayEvalToError(tree, start_node) != .never,
10054 }
10055 }
10056}
10057
10058fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.EvalToError {
10059 const node_tags = tree.nodes.items(.tag);
10060 const node_datas = tree.nodes.items(.data);
10061 const main_tokens = tree.nodes.items(.main_token);
10062 const token_tags = tree.tokens.items(.tag);
10063
10064 var node = start_node;
10065 while (true) {
10066 switch (node_tags[node]) {
10067 .root,
10068 .@"usingnamespace",
10069 .test_decl,
10070 .switch_case,
10071 .switch_case_inline,
10072 .switch_case_one,
10073 .switch_case_inline_one,
10074 .container_field_init,
10075 .container_field_align,
10076 .container_field,
10077 .asm_output,
10078 .asm_input,
10079 => unreachable,
10080
10081 .error_value => return .always,
10082
10083 .@"asm",
10084 .asm_simple,
10085 .identifier,
10086 .field_access,
10087 .deref,
10088 .array_access,
10089 .while_simple,
10090 .while_cont,
10091 .for_simple,
10092 .if_simple,
10093 .@"while",
10094 .@"if",
10095 .@"for",
10096 .@"switch",
10097 .switch_comma,
10098 .call_one,
10099 .call_one_comma,
10100 .async_call_one,
10101 .async_call_one_comma,
10102 .call,
10103 .call_comma,
10104 .async_call,
10105 .async_call_comma,
10106 => return .maybe,
10107
10108 .@"return",
10109 .@"break",
10110 .@"continue",
10111 .bit_not,
10112 .bool_not,
10113 .global_var_decl,
10114 .local_var_decl,
10115 .simple_var_decl,
10116 .aligned_var_decl,
10117 .@"defer",
10118 .@"errdefer",
10119 .address_of,
10120 .optional_type,
10121 .negation,
10122 .negation_wrap,
10123 .@"resume",
10124 .array_type,
10125 .array_type_sentinel,
10126 .ptr_type_aligned,
10127 .ptr_type_sentinel,
10128 .ptr_type,
10129 .ptr_type_bit_range,
10130 .@"suspend",
10131 .fn_proto_simple,
10132 .fn_proto_multi,
10133 .fn_proto_one,
10134 .fn_proto,
10135 .fn_decl,
10136 .anyframe_type,
10137 .anyframe_literal,
10138 .number_literal,
10139 .enum_literal,
10140 .string_literal,
10141 .multiline_string_literal,
10142 .char_literal,
10143 .unreachable_literal,
10144 .error_set_decl,
10145 .container_decl,
10146 .container_decl_trailing,
10147 .container_decl_two,
10148 .container_decl_two_trailing,
10149 .container_decl_arg,
10150 .container_decl_arg_trailing,
10151 .tagged_union,
10152 .tagged_union_trailing,
10153 .tagged_union_two,
10154 .tagged_union_two_trailing,
10155 .tagged_union_enum_tag,
10156 .tagged_union_enum_tag_trailing,
10157 .add,
10158 .add_wrap,
10159 .add_sat,
10160 .array_cat,
10161 .array_mult,
10162 .assign,
10163 .assign_destructure,
10164 .assign_bit_and,
10165 .assign_bit_or,
10166 .assign_shl,
10167 .assign_shl_sat,
10168 .assign_shr,
10169 .assign_bit_xor,
10170 .assign_div,
10171 .assign_sub,
10172 .assign_sub_wrap,
10173 .assign_sub_sat,
10174 .assign_mod,
10175 .assign_add,
10176 .assign_add_wrap,
10177 .assign_add_sat,
10178 .assign_mul,
10179 .assign_mul_wrap,
10180 .assign_mul_sat,
10181 .bang_equal,
10182 .bit_and,
10183 .bit_or,
10184 .shl,
10185 .shl_sat,
10186 .shr,
10187 .bit_xor,
10188 .bool_and,
10189 .bool_or,
10190 .div,
10191 .equal_equal,
10192 .error_union,
10193 .greater_or_equal,
10194 .greater_than,
10195 .less_or_equal,
10196 .less_than,
10197 .merge_error_sets,
10198 .mod,
10199 .mul,
10200 .mul_wrap,
10201 .mul_sat,
10202 .switch_range,
10203 .for_range,
10204 .sub,
10205 .sub_wrap,
10206 .sub_sat,
10207 .slice,
10208 .slice_open,
10209 .slice_sentinel,
10210 .array_init_one,
10211 .array_init_one_comma,
10212 .array_init_dot_two,
10213 .array_init_dot_two_comma,
10214 .array_init_dot,
10215 .array_init_dot_comma,
10216 .array_init,
10217 .array_init_comma,
10218 .struct_init_one,
10219 .struct_init_one_comma,
10220 .struct_init_dot_two,
10221 .struct_init_dot_two_comma,
10222 .struct_init_dot,
10223 .struct_init_dot_comma,
10224 .struct_init,
10225 .struct_init_comma,
10226 => return .never,
10227
10228 // Forward the question to the LHS sub-expression.
10229 .grouped_expression,
10230 .@"try",
10231 .@"await",
10232 .@"comptime",
10233 .@"nosuspend",
10234 .unwrap_optional,
10235 => node = node_datas[node].lhs,
10236
10237 // LHS sub-expression may still be an error under the outer optional or error union
10238 .@"catch",
10239 .@"orelse",
10240 => return .maybe,
10241
10242 .block_two,
10243 .block_two_semicolon,
10244 .block,
10245 .block_semicolon,
10246 => {
10247 const lbrace = main_tokens[node];
10248 if (token_tags[lbrace - 1] == .colon) {
10249 // Labeled blocks may need a memory location to forward
10250 // to their break statements.
10251 return .maybe;
10252 } else {
10253 return .never;
10254 }
10255 },
10256
10257 .builtin_call,
10258 .builtin_call_comma,
10259 .builtin_call_two,
10260 .builtin_call_two_comma,
10261 => {
10262 const builtin_token = main_tokens[node];
10263 const builtin_name = tree.tokenSlice(builtin_token);
10264 // If the builtin is an invalid name, we don't cause an error here; instead
10265 // let it pass, and the error will be "invalid builtin function" later.
10266 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe;
10267 return builtin_info.eval_to_error;
10268 },
10269 }
10270 }
10271}
10272
10273/// Returns `true` if it is known the type expression has more than one possible value;
10274/// `false` otherwise.
10275fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.Index) bool {
10276 const node_tags = tree.nodes.items(.tag);
10277 const node_datas = tree.nodes.items(.data);
10278
10279 var node = start_node;
10280 while (true) {
10281 switch (node_tags[node]) {
10282 .root,
10283 .@"usingnamespace",
10284 .test_decl,
10285 .switch_case,
10286 .switch_case_inline,
10287 .switch_case_one,
10288 .switch_case_inline_one,
10289 .container_field_init,
10290 .container_field_align,
10291 .container_field,
10292 .asm_output,
10293 .asm_input,
10294 .global_var_decl,
10295 .local_var_decl,
10296 .simple_var_decl,
10297 .aligned_var_decl,
10298 => unreachable,
10299
10300 .@"return",
10301 .@"break",
10302 .@"continue",
10303 .bit_not,
10304 .bool_not,
10305 .@"defer",
10306 .@"errdefer",
10307 .address_of,
10308 .negation,
10309 .negation_wrap,
10310 .@"resume",
10311 .array_type,
10312 .@"suspend",
10313 .fn_decl,
10314 .anyframe_literal,
10315 .number_literal,
10316 .enum_literal,
10317 .string_literal,
10318 .multiline_string_literal,
10319 .char_literal,
10320 .unreachable_literal,
10321 .error_set_decl,
10322 .container_decl,
10323 .container_decl_trailing,
10324 .container_decl_two,
10325 .container_decl_two_trailing,
10326 .container_decl_arg,
10327 .container_decl_arg_trailing,
10328 .tagged_union,
10329 .tagged_union_trailing,
10330 .tagged_union_two,
10331 .tagged_union_two_trailing,
10332 .tagged_union_enum_tag,
10333 .tagged_union_enum_tag_trailing,
10334 .@"asm",
10335 .asm_simple,
10336 .add,
10337 .add_wrap,
10338 .add_sat,
10339 .array_cat,
10340 .array_mult,
10341 .assign,
10342 .assign_destructure,
10343 .assign_bit_and,
10344 .assign_bit_or,
10345 .assign_shl,
10346 .assign_shl_sat,
10347 .assign_shr,
10348 .assign_bit_xor,
10349 .assign_div,
10350 .assign_sub,
10351 .assign_sub_wrap,
10352 .assign_sub_sat,
10353 .assign_mod,
10354 .assign_add,
10355 .assign_add_wrap,
10356 .assign_add_sat,
10357 .assign_mul,
10358 .assign_mul_wrap,
10359 .assign_mul_sat,
10360 .bang_equal,
10361 .bit_and,
10362 .bit_or,
10363 .shl,
10364 .shl_sat,
10365 .shr,
10366 .bit_xor,
10367 .bool_and,
10368 .bool_or,
10369 .div,
10370 .equal_equal,
10371 .error_union,
10372 .greater_or_equal,
10373 .greater_than,
10374 .less_or_equal,
10375 .less_than,
10376 .merge_error_sets,
10377 .mod,
10378 .mul,
10379 .mul_wrap,
10380 .mul_sat,
10381 .switch_range,
10382 .for_range,
10383 .field_access,
10384 .sub,
10385 .sub_wrap,
10386 .sub_sat,
10387 .slice,
10388 .slice_open,
10389 .slice_sentinel,
10390 .deref,
10391 .array_access,
10392 .error_value,
10393 .while_simple,
10394 .while_cont,
10395 .for_simple,
10396 .if_simple,
10397 .@"catch",
10398 .@"orelse",
10399 .array_init_one,
10400 .array_init_one_comma,
10401 .array_init_dot_two,
10402 .array_init_dot_two_comma,
10403 .array_init_dot,
10404 .array_init_dot_comma,
10405 .array_init,
10406 .array_init_comma,
10407 .struct_init_one,
10408 .struct_init_one_comma,
10409 .struct_init_dot_two,
10410 .struct_init_dot_two_comma,
10411 .struct_init_dot,
10412 .struct_init_dot_comma,
10413 .struct_init,
10414 .struct_init_comma,
10415 .@"while",
10416 .@"if",
10417 .@"for",
10418 .@"switch",
10419 .switch_comma,
10420 .call_one,
10421 .call_one_comma,
10422 .async_call_one,
10423 .async_call_one_comma,
10424 .call,
10425 .call_comma,
10426 .async_call,
10427 .async_call_comma,
10428 .block_two,
10429 .block_two_semicolon,
10430 .block,
10431 .block_semicolon,
10432 .builtin_call,
10433 .builtin_call_comma,
10434 .builtin_call_two,
10435 .builtin_call_two_comma,
10436 // these are function bodies, not pointers
10437 .fn_proto_simple,
10438 .fn_proto_multi,
10439 .fn_proto_one,
10440 .fn_proto,
10441 => return false,
10442
10443 // Forward the question to the LHS sub-expression.
10444 .grouped_expression,
10445 .@"try",
10446 .@"await",
10447 .@"comptime",
10448 .@"nosuspend",
10449 .unwrap_optional,
10450 => node = node_datas[node].lhs,
10451
10452 .ptr_type_aligned,
10453 .ptr_type_sentinel,
10454 .ptr_type,
10455 .ptr_type_bit_range,
10456 .optional_type,
10457 .anyframe_type,
10458 .array_type_sentinel,
10459 => return true,
10460
10461 .identifier => {
10462 const main_tokens = tree.nodes.items(.main_token);
10463 const ident_bytes = tree.tokenSlice(main_tokens[node]);
10464 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
10465 .anyerror_type,
10466 .anyframe_type,
10467 .anyopaque_type,
10468 .bool_type,
10469 .c_int_type,
10470 .c_long_type,
10471 .c_longdouble_type,
10472 .c_longlong_type,
10473 .c_char_type,
10474 .c_short_type,
10475 .c_uint_type,
10476 .c_ulong_type,
10477 .c_ulonglong_type,
10478 .c_ushort_type,
10479 .comptime_float_type,
10480 .comptime_int_type,
10481 .f16_type,
10482 .f32_type,
10483 .f64_type,
10484 .f80_type,
10485 .f128_type,
10486 .i16_type,
10487 .i32_type,
10488 .i64_type,
10489 .i128_type,
10490 .i8_type,
10491 .isize_type,
10492 .type_type,
10493 .u16_type,
10494 .u29_type,
10495 .u32_type,
10496 .u64_type,
10497 .u128_type,
10498 .u1_type,
10499 .u8_type,
10500 .usize_type,
10501 => return true,
10502
10503 .void_type,
10504 .bool_false,
10505 .bool_true,
10506 .null_value,
10507 .undef,
10508 .noreturn_type,
10509 => return false,
10510
10511 else => unreachable, // that's all the values from `primitives`.
10512 } else {
10513 return false;
10514 }
10515 },
10516 }
10517 }
10518}
10519
10520/// Returns `true` if it is known the expression is a type that cannot be used at runtime;
10521/// `false` otherwise.
10522fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
10523 const node_tags = tree.nodes.items(.tag);
10524 const node_datas = tree.nodes.items(.data);
10525
10526 var node = start_node;
10527 while (true) {
10528 switch (node_tags[node]) {
10529 .root,
10530 .@"usingnamespace",
10531 .test_decl,
10532 .switch_case,
10533 .switch_case_inline,
10534 .switch_case_one,
10535 .switch_case_inline_one,
10536 .container_field_init,
10537 .container_field_align,
10538 .container_field,
10539 .asm_output,
10540 .asm_input,
10541 .global_var_decl,
10542 .local_var_decl,
10543 .simple_var_decl,
10544 .aligned_var_decl,
10545 => unreachable,
10546
10547 .@"return",
10548 .@"break",
10549 .@"continue",
10550 .bit_not,
10551 .bool_not,
10552 .@"defer",
10553 .@"errdefer",
10554 .address_of,
10555 .negation,
10556 .negation_wrap,
10557 .@"resume",
10558 .array_type,
10559 .@"suspend",
10560 .fn_decl,
10561 .anyframe_literal,
10562 .number_literal,
10563 .enum_literal,
10564 .string_literal,
10565 .multiline_string_literal,
10566 .char_literal,
10567 .unreachable_literal,
10568 .error_set_decl,
10569 .container_decl,
10570 .container_decl_trailing,
10571 .container_decl_two,
10572 .container_decl_two_trailing,
10573 .container_decl_arg,
10574 .container_decl_arg_trailing,
10575 .tagged_union,
10576 .tagged_union_trailing,
10577 .tagged_union_two,
10578 .tagged_union_two_trailing,
10579 .tagged_union_enum_tag,
10580 .tagged_union_enum_tag_trailing,
10581 .@"asm",
10582 .asm_simple,
10583 .add,
10584 .add_wrap,
10585 .add_sat,
10586 .array_cat,
10587 .array_mult,
10588 .assign,
10589 .assign_destructure,
10590 .assign_bit_and,
10591 .assign_bit_or,
10592 .assign_shl,
10593 .assign_shl_sat,
10594 .assign_shr,
10595 .assign_bit_xor,
10596 .assign_div,
10597 .assign_sub,
10598 .assign_sub_wrap,
10599 .assign_sub_sat,
10600 .assign_mod,
10601 .assign_add,
10602 .assign_add_wrap,
10603 .assign_add_sat,
10604 .assign_mul,
10605 .assign_mul_wrap,
10606 .assign_mul_sat,
10607 .bang_equal,
10608 .bit_and,
10609 .bit_or,
10610 .shl,
10611 .shl_sat,
10612 .shr,
10613 .bit_xor,
10614 .bool_and,
10615 .bool_or,
10616 .div,
10617 .equal_equal,
10618 .error_union,
10619 .greater_or_equal,
10620 .greater_than,
10621 .less_or_equal,
10622 .less_than,
10623 .merge_error_sets,
10624 .mod,
10625 .mul,
10626 .mul_wrap,
10627 .mul_sat,
10628 .switch_range,
10629 .for_range,
10630 .field_access,
10631 .sub,
10632 .sub_wrap,
10633 .sub_sat,
10634 .slice,
10635 .slice_open,
10636 .slice_sentinel,
10637 .deref,
10638 .array_access,
10639 .error_value,
10640 .while_simple,
10641 .while_cont,
10642 .for_simple,
10643 .if_simple,
10644 .@"catch",
10645 .@"orelse",
10646 .array_init_one,
10647 .array_init_one_comma,
10648 .array_init_dot_two,
10649 .array_init_dot_two_comma,
10650 .array_init_dot,
10651 .array_init_dot_comma,
10652 .array_init,
10653 .array_init_comma,
10654 .struct_init_one,
10655 .struct_init_one_comma,
10656 .struct_init_dot_two,
10657 .struct_init_dot_two_comma,
10658 .struct_init_dot,
10659 .struct_init_dot_comma,
10660 .struct_init,
10661 .struct_init_comma,
10662 .@"while",
10663 .@"if",
10664 .@"for",
10665 .@"switch",
10666 .switch_comma,
10667 .call_one,
10668 .call_one_comma,
10669 .async_call_one,
10670 .async_call_one_comma,
10671 .call,
10672 .call_comma,
10673 .async_call,
10674 .async_call_comma,
10675 .block_two,
10676 .block_two_semicolon,
10677 .block,
10678 .block_semicolon,
10679 .builtin_call,
10680 .builtin_call_comma,
10681 .builtin_call_two,
10682 .builtin_call_two_comma,
10683 .ptr_type_aligned,
10684 .ptr_type_sentinel,
10685 .ptr_type,
10686 .ptr_type_bit_range,
10687 .optional_type,
10688 .anyframe_type,
10689 .array_type_sentinel,
10690 => return false,
10691
10692 // these are function bodies, not pointers
10693 .fn_proto_simple,
10694 .fn_proto_multi,
10695 .fn_proto_one,
10696 .fn_proto,
10697 => return true,
10698
10699 // Forward the question to the LHS sub-expression.
10700 .grouped_expression,
10701 .@"try",
10702 .@"await",
10703 .@"comptime",
10704 .@"nosuspend",
10705 .unwrap_optional,
10706 => node = node_datas[node].lhs,
10707
10708 .identifier => {
10709 const main_tokens = tree.nodes.items(.main_token);
10710 const ident_bytes = tree.tokenSlice(main_tokens[node]);
10711 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
10712 .anyerror_type,
10713 .anyframe_type,
10714 .anyopaque_type,
10715 .bool_type,
10716 .c_int_type,
10717 .c_long_type,
10718 .c_longdouble_type,
10719 .c_longlong_type,
10720 .c_char_type,
10721 .c_short_type,
10722 .c_uint_type,
10723 .c_ulong_type,
10724 .c_ulonglong_type,
10725 .c_ushort_type,
10726 .f16_type,
10727 .f32_type,
10728 .f64_type,
10729 .f80_type,
10730 .f128_type,
10731 .i16_type,
10732 .i32_type,
10733 .i64_type,
10734 .i128_type,
10735 .i8_type,
10736 .isize_type,
10737 .u16_type,
10738 .u29_type,
10739 .u32_type,
10740 .u64_type,
10741 .u128_type,
10742 .u1_type,
10743 .u8_type,
10744 .usize_type,
10745 .void_type,
10746 .bool_false,
10747 .bool_true,
10748 .null_value,
10749 .undef,
10750 .noreturn_type,
10751 => return false,
10752
10753 .comptime_float_type,
10754 .comptime_int_type,
10755 .type_type,
10756 => return true,
10757
10758 else => unreachable, // that's all the values from `primitives`.
10759 } else {
10760 return false;
10761 }
10762 },
10763 }
10764 }
10765}
10766
10767/// Returns `true` if the node uses `gz.anon_name_strategy`.
10768fn nodeUsesAnonNameStrategy(tree: *const Ast, node: Ast.Node.Index) bool {
10769 const node_tags = tree.nodes.items(.tag);
10770 switch (node_tags[node]) {
10771 .container_decl,
10772 .container_decl_trailing,
10773 .container_decl_two,
10774 .container_decl_two_trailing,
10775 .container_decl_arg,
10776 .container_decl_arg_trailing,
10777 .tagged_union,
10778 .tagged_union_trailing,
10779 .tagged_union_two,
10780 .tagged_union_two_trailing,
10781 .tagged_union_enum_tag,
10782 .tagged_union_enum_tag_trailing,
10783 => return true,
10784 .builtin_call_two, .builtin_call_two_comma, .builtin_call, .builtin_call_comma => {
10785 const builtin_token = tree.nodes.items(.main_token)[node];
10786 const builtin_name = tree.tokenSlice(builtin_token);
10787 return std.mem.eql(u8, builtin_name, "@Type");
10788 },
10789 else => return false,
10790 }
10791}
10792
10793/// Applies `rl` semantics to `result`. Expressions which do not do their own handling of
10794/// result locations must call this function on their result.
10795/// As an example, if `ri.rl` is `.ptr`, it will write the result to the pointer.
10796/// If `ri.rl` is `.ty`, it will coerce the result to the type.
10797/// Assumes nothing stacked on `gz`.
10798fn rvalue(
10799 gz: *GenZir,
10800 ri: ResultInfo,
10801 raw_result: Zir.Inst.Ref,
10802 src_node: Ast.Node.Index,
10803) InnerError!Zir.Inst.Ref {
10804 return rvalueInner(gz, ri, raw_result, src_node, true);
10805}
10806
10807/// Like `rvalue`, but refuses to perform coercions before taking references for
10808/// the `ref_coerced_ty` result type. This is used for local variables which do
10809/// not have `alloc`s, because we want variables to have consistent addresses,
10810/// i.e. we want them to act like lvalues.
10811fn rvalueNoCoercePreRef(
10812 gz: *GenZir,
10813 ri: ResultInfo,
10814 raw_result: Zir.Inst.Ref,
10815 src_node: Ast.Node.Index,
10816) InnerError!Zir.Inst.Ref {
10817 return rvalueInner(gz, ri, raw_result, src_node, false);
10818}
10819
10820fn rvalueInner(
10821 gz: *GenZir,
10822 ri: ResultInfo,
10823 raw_result: Zir.Inst.Ref,
10824 src_node: Ast.Node.Index,
10825 allow_coerce_pre_ref: bool,
10826) InnerError!Zir.Inst.Ref {
10827 const result = r: {
10828 if (raw_result.toIndex()) |result_index| {
10829 const zir_tags = gz.astgen.instructions.items(.tag);
10830 const data = gz.astgen.instructions.items(.data)[@intFromEnum(result_index)];
10831 if (zir_tags[@intFromEnum(result_index)].isAlwaysVoid(data)) {
10832 break :r Zir.Inst.Ref.void_value;
10833 }
10834 }
10835 break :r raw_result;
10836 };
10837 if (gz.endsWithNoReturn()) return result;
10838 switch (ri.rl) {
10839 .none, .coerced_ty => return result,
10840 .discard => {
10841 // Emit a compile error for discarding error values.
10842 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
10843 return .void_value;
10844 },
10845 .ref, .ref_coerced_ty => {
10846 const coerced_result = if (allow_coerce_pre_ref and ri.rl == .ref_coerced_ty) res: {
10847 const ptr_ty = ri.rl.ref_coerced_ty;
10848 break :res try gz.addPlNode(.coerce_ptr_elem_ty, src_node, Zir.Inst.Bin{
10849 .lhs = ptr_ty,
10850 .rhs = result,
10851 });
10852 } else result;
10853 // We need a pointer but we have a value.
10854 // Unfortunately it's not quite as simple as directly emitting a ref
10855 // instruction here because we need subsequent address-of operator on
10856 // const locals to return the same address.
10857 const astgen = gz.astgen;
10858 const tree = astgen.tree;
10859 const src_token = tree.firstToken(src_node);
10860 const result_index = coerced_result.toIndex() orelse
10861 return gz.addUnTok(.ref, coerced_result, src_token);
10862 const zir_tags = gz.astgen.instructions.items(.tag);
10863 if (zir_tags[@intFromEnum(result_index)].isParam() or astgen.isInferred(coerced_result))
10864 return gz.addUnTok(.ref, coerced_result, src_token);
10865 const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index);
10866 if (!gop.found_existing) {
10867 gop.value_ptr.* = try gz.makeUnTok(.ref, coerced_result, src_token);
10868 }
10869 return gop.value_ptr.*.toRef();
10870 },
10871 .ty => |ty_inst| {
10872 // Quickly eliminate some common, unnecessary type coercion.
10873 const as_ty = @as(u64, @intFromEnum(Zir.Inst.Ref.type_type)) << 32;
10874 const as_comptime_int = @as(u64, @intFromEnum(Zir.Inst.Ref.comptime_int_type)) << 32;
10875 const as_bool = @as(u64, @intFromEnum(Zir.Inst.Ref.bool_type)) << 32;
10876 const as_usize = @as(u64, @intFromEnum(Zir.Inst.Ref.usize_type)) << 32;
10877 const as_void = @as(u64, @intFromEnum(Zir.Inst.Ref.void_type)) << 32;
10878 switch ((@as(u64, @intFromEnum(ty_inst)) << 32) | @as(u64, @intFromEnum(result))) {
10879 as_ty | @intFromEnum(Zir.Inst.Ref.u1_type),
10880 as_ty | @intFromEnum(Zir.Inst.Ref.u8_type),
10881 as_ty | @intFromEnum(Zir.Inst.Ref.i8_type),
10882 as_ty | @intFromEnum(Zir.Inst.Ref.u16_type),
10883 as_ty | @intFromEnum(Zir.Inst.Ref.u29_type),
10884 as_ty | @intFromEnum(Zir.Inst.Ref.i16_type),
10885 as_ty | @intFromEnum(Zir.Inst.Ref.u32_type),
10886 as_ty | @intFromEnum(Zir.Inst.Ref.i32_type),
10887 as_ty | @intFromEnum(Zir.Inst.Ref.u64_type),
10888 as_ty | @intFromEnum(Zir.Inst.Ref.i64_type),
10889 as_ty | @intFromEnum(Zir.Inst.Ref.u128_type),
10890 as_ty | @intFromEnum(Zir.Inst.Ref.i128_type),
10891 as_ty | @intFromEnum(Zir.Inst.Ref.usize_type),
10892 as_ty | @intFromEnum(Zir.Inst.Ref.isize_type),
10893 as_ty | @intFromEnum(Zir.Inst.Ref.c_char_type),
10894 as_ty | @intFromEnum(Zir.Inst.Ref.c_short_type),
10895 as_ty | @intFromEnum(Zir.Inst.Ref.c_ushort_type),
10896 as_ty | @intFromEnum(Zir.Inst.Ref.c_int_type),
10897 as_ty | @intFromEnum(Zir.Inst.Ref.c_uint_type),
10898 as_ty | @intFromEnum(Zir.Inst.Ref.c_long_type),
10899 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulong_type),
10900 as_ty | @intFromEnum(Zir.Inst.Ref.c_longlong_type),
10901 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulonglong_type),
10902 as_ty | @intFromEnum(Zir.Inst.Ref.c_longdouble_type),
10903 as_ty | @intFromEnum(Zir.Inst.Ref.f16_type),
10904 as_ty | @intFromEnum(Zir.Inst.Ref.f32_type),
10905 as_ty | @intFromEnum(Zir.Inst.Ref.f64_type),
10906 as_ty | @intFromEnum(Zir.Inst.Ref.f80_type),
10907 as_ty | @intFromEnum(Zir.Inst.Ref.f128_type),
10908 as_ty | @intFromEnum(Zir.Inst.Ref.anyopaque_type),
10909 as_ty | @intFromEnum(Zir.Inst.Ref.bool_type),
10910 as_ty | @intFromEnum(Zir.Inst.Ref.void_type),
10911 as_ty | @intFromEnum(Zir.Inst.Ref.type_type),
10912 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_type),
10913 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_int_type),
10914 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_float_type),
10915 as_ty | @intFromEnum(Zir.Inst.Ref.noreturn_type),
10916 as_ty | @intFromEnum(Zir.Inst.Ref.anyframe_type),
10917 as_ty | @intFromEnum(Zir.Inst.Ref.null_type),
10918 as_ty | @intFromEnum(Zir.Inst.Ref.undefined_type),
10919 as_ty | @intFromEnum(Zir.Inst.Ref.enum_literal_type),
10920 as_ty | @intFromEnum(Zir.Inst.Ref.atomic_order_type),
10921 as_ty | @intFromEnum(Zir.Inst.Ref.atomic_rmw_op_type),
10922 as_ty | @intFromEnum(Zir.Inst.Ref.calling_convention_type),
10923 as_ty | @intFromEnum(Zir.Inst.Ref.address_space_type),
10924 as_ty | @intFromEnum(Zir.Inst.Ref.float_mode_type),
10925 as_ty | @intFromEnum(Zir.Inst.Ref.reduce_op_type),
10926 as_ty | @intFromEnum(Zir.Inst.Ref.call_modifier_type),
10927 as_ty | @intFromEnum(Zir.Inst.Ref.prefetch_options_type),
10928 as_ty | @intFromEnum(Zir.Inst.Ref.export_options_type),
10929 as_ty | @intFromEnum(Zir.Inst.Ref.extern_options_type),
10930 as_ty | @intFromEnum(Zir.Inst.Ref.type_info_type),
10931 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_u8_type),
10932 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_type),
10933 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_sentinel_0_type),
10934 as_ty | @intFromEnum(Zir.Inst.Ref.single_const_pointer_to_comptime_int_type),
10935 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_type),
10936 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_sentinel_0_type),
10937 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_void_error_union_type),
10938 as_ty | @intFromEnum(Zir.Inst.Ref.generic_poison_type),
10939 as_ty | @intFromEnum(Zir.Inst.Ref.empty_struct_type),
10940 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero),
10941 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one),
10942 as_bool | @intFromEnum(Zir.Inst.Ref.bool_true),
10943 as_bool | @intFromEnum(Zir.Inst.Ref.bool_false),
10944 as_usize | @intFromEnum(Zir.Inst.Ref.zero_usize),
10945 as_usize | @intFromEnum(Zir.Inst.Ref.one_usize),
10946 as_void | @intFromEnum(Zir.Inst.Ref.void_value),
10947 => return result, // type of result is already correct
10948
10949 // Need an explicit type coercion instruction.
10950 else => return gz.addPlNode(ri.zirTag(), src_node, Zir.Inst.As{
10951 .dest_type = ty_inst,
10952 .operand = result,
10953 }),
10954 }
10955 },
10956 .ptr => |ptr_res| {
10957 _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{
10958 .lhs = ptr_res.inst,
10959 .rhs = result,
10960 });
10961 return .void_value;
10962 },
10963 .inferred_ptr => |alloc| {
10964 _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{
10965 .lhs = alloc,
10966 .rhs = result,
10967 });
10968 return .void_value;
10969 },
10970 .destructure => |destructure| {
10971 const components = destructure.components;
10972 _ = try gz.addPlNode(.validate_destructure, src_node, Zir.Inst.ValidateDestructure{
10973 .operand = result,
10974 .destructure_node = gz.nodeIndexToRelative(destructure.src_node),
10975 .expect_len = @intCast(components.len),
10976 });
10977 for (components, 0..) |component, i| {
10978 if (component == .discard) continue;
10979 const elem_val = try gz.add(.{
10980 .tag = .elem_val_imm,
10981 .data = .{ .elem_val_imm = .{
10982 .operand = result,
10983 .idx = @intCast(i),
10984 } },
10985 });
10986 switch (component) {
10987 .typed_ptr => |ptr_res| {
10988 _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{
10989 .lhs = ptr_res.inst,
10990 .rhs = elem_val,
10991 });
10992 },
10993 .inferred_ptr => |ptr_inst| {
10994 _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{
10995 .lhs = ptr_inst,
10996 .rhs = elem_val,
10997 });
10998 },
10999 .discard => unreachable,
11000 }
11001 }
11002 return .void_value;
11003 },
11004 }
11005}
11006
11007/// Given an identifier token, obtain the string for it.
11008/// If the token uses @"" syntax, parses as a string, reports errors if applicable,
11009/// and allocates the result within `astgen.arena`.
11010/// Otherwise, returns a reference to the source code bytes directly.
11011/// See also `appendIdentStr` and `parseStrLit`.
11012fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]const u8 {
11013 const tree = astgen.tree;
11014 const token_tags = tree.tokens.items(.tag);
11015 assert(token_tags[token] == .identifier);
11016 const ident_name = tree.tokenSlice(token);
11017 if (!mem.startsWith(u8, ident_name, "@")) {
11018 return ident_name;
11019 }
11020 var buf: ArrayListUnmanaged(u8) = .{};
11021 defer buf.deinit(astgen.gpa);
11022 try astgen.parseStrLit(token, &buf, ident_name, 1);
11023 if (mem.indexOfScalar(u8, buf.items, 0) != null) {
11024 return astgen.failTok(token, "identifier cannot contain null bytes", .{});
11025 } else if (buf.items.len == 0) {
11026 return astgen.failTok(token, "identifier cannot be empty", .{});
11027 }
11028 const duped = try astgen.arena.dupe(u8, buf.items);
11029 return duped;
11030}
11031
11032/// Given an identifier token, obtain the string for it (possibly parsing as a string
11033/// literal if it is @"" syntax), and append the string to `buf`.
11034/// See also `identifierTokenString` and `parseStrLit`.
11035fn appendIdentStr(
11036 astgen: *AstGen,
11037 token: Ast.TokenIndex,
11038 buf: *ArrayListUnmanaged(u8),
11039) InnerError!void {
11040 const tree = astgen.tree;
11041 const token_tags = tree.tokens.items(.tag);
11042 assert(token_tags[token] == .identifier);
11043 const ident_name = tree.tokenSlice(token);
11044 if (!mem.startsWith(u8, ident_name, "@")) {
11045 return buf.appendSlice(astgen.gpa, ident_name);
11046 } else {
11047 const start = buf.items.len;
11048 try astgen.parseStrLit(token, buf, ident_name, 1);
11049 const slice = buf.items[start..];
11050 if (mem.indexOfScalar(u8, slice, 0) != null) {
11051 return astgen.failTok(token, "identifier cannot contain null bytes", .{});
11052 } else if (slice.len == 0) {
11053 return astgen.failTok(token, "identifier cannot be empty", .{});
11054 }
11055 }
11056}
11057
11058/// Appends the result to `buf`.
11059fn parseStrLit(
11060 astgen: *AstGen,
11061 token: Ast.TokenIndex,
11062 buf: *ArrayListUnmanaged(u8),
11063 bytes: []const u8,
11064 offset: u32,
11065) InnerError!void {
11066 const raw_string = bytes[offset..];
11067 var buf_managed = buf.toManaged(astgen.gpa);
11068 const result = std.zig.string_literal.parseWrite(buf_managed.writer(), raw_string);
11069 buf.* = buf_managed.moveToUnmanaged();
11070 switch (try result) {
11071 .success => return,
11072 .failure => |err| return astgen.failWithStrLitError(err, token, bytes, offset),
11073 }
11074}
11075
11076fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError {
11077 const raw_string = bytes[offset..];
11078 switch (err) {
11079 .invalid_escape_character => |bad_index| {
11080 return astgen.failOff(
11081 token,
11082 offset + @as(u32, @intCast(bad_index)),
11083 "invalid escape character: '{c}'",
11084 .{raw_string[bad_index]},
11085 );
11086 },
11087 .expected_hex_digit => |bad_index| {
11088 return astgen.failOff(
11089 token,
11090 offset + @as(u32, @intCast(bad_index)),
11091 "expected hex digit, found '{c}'",
11092 .{raw_string[bad_index]},
11093 );
11094 },
11095 .empty_unicode_escape_sequence => |bad_index| {
11096 return astgen.failOff(
11097 token,
11098 offset + @as(u32, @intCast(bad_index)),
11099 "empty unicode escape sequence",
11100 .{},
11101 );
11102 },
11103 .expected_hex_digit_or_rbrace => |bad_index| {
11104 return astgen.failOff(
11105 token,
11106 offset + @as(u32, @intCast(bad_index)),
11107 "expected hex digit or '}}', found '{c}'",
11108 .{raw_string[bad_index]},
11109 );
11110 },
11111 .invalid_unicode_codepoint => |bad_index| {
11112 return astgen.failOff(
11113 token,
11114 offset + @as(u32, @intCast(bad_index)),
11115 "unicode escape does not correspond to a valid codepoint",
11116 .{},
11117 );
11118 },
11119 .expected_lbrace => |bad_index| {
11120 return astgen.failOff(
11121 token,
11122 offset + @as(u32, @intCast(bad_index)),
11123 "expected '{{', found '{c}",
11124 .{raw_string[bad_index]},
11125 );
11126 },
11127 .expected_rbrace => |bad_index| {
11128 return astgen.failOff(
11129 token,
11130 offset + @as(u32, @intCast(bad_index)),
11131 "expected '}}', found '{c}",
11132 .{raw_string[bad_index]},
11133 );
11134 },
11135 .expected_single_quote => |bad_index| {
11136 return astgen.failOff(
11137 token,
11138 offset + @as(u32, @intCast(bad_index)),
11139 "expected single quote ('), found '{c}",
11140 .{raw_string[bad_index]},
11141 );
11142 },
11143 .invalid_character => |bad_index| {
11144 return astgen.failOff(
11145 token,
11146 offset + @as(u32, @intCast(bad_index)),
11147 "invalid byte in string or character literal: '{c}'",
11148 .{raw_string[bad_index]},
11149 );
11150 },
11151 }
11152}
11153
11154fn failNode(
11155 astgen: *AstGen,
11156 node: Ast.Node.Index,
11157 comptime format: []const u8,
11158 args: anytype,
11159) InnerError {
11160 return astgen.failNodeNotes(node, format, args, &[0]u32{});
11161}
11162
11163fn appendErrorNode(
11164 astgen: *AstGen,
11165 node: Ast.Node.Index,
11166 comptime format: []const u8,
11167 args: anytype,
11168) Allocator.Error!void {
11169 try astgen.appendErrorNodeNotes(node, format, args, &[0]u32{});
11170}
11171
11172fn appendErrorNodeNotes(
11173 astgen: *AstGen,
11174 node: Ast.Node.Index,
11175 comptime format: []const u8,
11176 args: anytype,
11177 notes: []const u32,
11178) Allocator.Error!void {
11179 @setCold(true);
11180 const string_bytes = &astgen.string_bytes;
11181 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
11182 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
11183 const notes_index: u32 = if (notes.len != 0) blk: {
11184 const notes_start = astgen.extra.items.len;
11185 try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len);
11186 astgen.extra.appendAssumeCapacity(@intCast(notes.len));
11187 astgen.extra.appendSliceAssumeCapacity(notes);
11188 break :blk @intCast(notes_start);
11189 } else 0;
11190 try astgen.compile_errors.append(astgen.gpa, .{
11191 .msg = msg,
11192 .node = node,
11193 .token = 0,
11194 .byte_offset = 0,
11195 .notes = notes_index,
11196 });
11197}
11198
11199fn failNodeNotes(
11200 astgen: *AstGen,
11201 node: Ast.Node.Index,
11202 comptime format: []const u8,
11203 args: anytype,
11204 notes: []const u32,
11205) InnerError {
11206 try appendErrorNodeNotes(astgen, node, format, args, notes);
11207 return error.AnalysisFail;
11208}
11209
11210fn failTok(
11211 astgen: *AstGen,
11212 token: Ast.TokenIndex,
11213 comptime format: []const u8,
11214 args: anytype,
11215) InnerError {
11216 return astgen.failTokNotes(token, format, args, &[0]u32{});
11217}
11218
11219fn appendErrorTok(
11220 astgen: *AstGen,
11221 token: Ast.TokenIndex,
11222 comptime format: []const u8,
11223 args: anytype,
11224) !void {
11225 try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{});
11226}
11227
11228fn failTokNotes(
11229 astgen: *AstGen,
11230 token: Ast.TokenIndex,
11231 comptime format: []const u8,
11232 args: anytype,
11233 notes: []const u32,
11234) InnerError {
11235 try appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
11236 return error.AnalysisFail;
11237}
11238
11239fn appendErrorTokNotes(
11240 astgen: *AstGen,
11241 token: Ast.TokenIndex,
11242 comptime format: []const u8,
11243 args: anytype,
11244 notes: []const u32,
11245) !void {
11246 return appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
11247}
11248
11249/// Same as `fail`, except given a token plus an offset from its starting byte
11250/// offset.
11251fn failOff(
11252 astgen: *AstGen,
11253 token: Ast.TokenIndex,
11254 byte_offset: u32,
11255 comptime format: []const u8,
11256 args: anytype,
11257) InnerError {
11258 try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{});
11259 return error.AnalysisFail;
11260}
11261
11262fn appendErrorTokNotesOff(
11263 astgen: *AstGen,
11264 token: Ast.TokenIndex,
11265 byte_offset: u32,
11266 comptime format: []const u8,
11267 args: anytype,
11268 notes: []const u32,
11269) !void {
11270 @setCold(true);
11271 const gpa = astgen.gpa;
11272 const string_bytes = &astgen.string_bytes;
11273 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
11274 try string_bytes.writer(gpa).print(format ++ "\x00", args);
11275 const notes_index: u32 = if (notes.len != 0) blk: {
11276 const notes_start = astgen.extra.items.len;
11277 try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len);
11278 astgen.extra.appendAssumeCapacity(@intCast(notes.len));
11279 astgen.extra.appendSliceAssumeCapacity(notes);
11280 break :blk @intCast(notes_start);
11281 } else 0;
11282 try astgen.compile_errors.append(gpa, .{
11283 .msg = msg,
11284 .node = 0,
11285 .token = token,
11286 .byte_offset = byte_offset,
11287 .notes = notes_index,
11288 });
11289}
11290
11291fn errNoteTok(
11292 astgen: *AstGen,
11293 token: Ast.TokenIndex,
11294 comptime format: []const u8,
11295 args: anytype,
11296) Allocator.Error!u32 {
11297 return errNoteTokOff(astgen, token, 0, format, args);
11298}
11299
11300fn errNoteTokOff(
11301 astgen: *AstGen,
11302 token: Ast.TokenIndex,
11303 byte_offset: u32,
11304 comptime format: []const u8,
11305 args: anytype,
11306) Allocator.Error!u32 {
11307 @setCold(true);
11308 const string_bytes = &astgen.string_bytes;
11309 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
11310 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
11311 return astgen.addExtra(Zir.Inst.CompileErrors.Item{
11312 .msg = msg,
11313 .node = 0,
11314 .token = token,
11315 .byte_offset = byte_offset,
11316 .notes = 0,
11317 });
11318}
11319
11320fn errNoteNode(
11321 astgen: *AstGen,
11322 node: Ast.Node.Index,
11323 comptime format: []const u8,
11324 args: anytype,
11325) Allocator.Error!u32 {
11326 @setCold(true);
11327 const string_bytes = &astgen.string_bytes;
11328 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
11329 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
11330 return astgen.addExtra(Zir.Inst.CompileErrors.Item{
11331 .msg = msg,
11332 .node = node,
11333 .token = 0,
11334 .byte_offset = 0,
11335 .notes = 0,
11336 });
11337}
11338
11339fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !Zir.NullTerminatedString {
11340 const gpa = astgen.gpa;
11341 const string_bytes = &astgen.string_bytes;
11342 const str_index: u32 = @intCast(string_bytes.items.len);
11343 try astgen.appendIdentStr(ident_token, string_bytes);
11344 const key: []const u8 = string_bytes.items[str_index..];
11345 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
11346 .bytes = string_bytes,
11347 }, StringIndexContext{
11348 .bytes = string_bytes,
11349 });
11350 if (gop.found_existing) {
11351 string_bytes.shrinkRetainingCapacity(str_index);
11352 return @enumFromInt(gop.key_ptr.*);
11353 } else {
11354 gop.key_ptr.* = str_index;
11355 try string_bytes.append(gpa, 0);
11356 return @enumFromInt(str_index);
11357 }
11358}
11359
11360/// Adds a doc comment block to `string_bytes` by walking backwards from `end_token`.
11361/// `end_token` must point at the first token after the last doc coment line.
11362/// Returns 0 if no doc comment is present.
11363fn docCommentAsString(astgen: *AstGen, end_token: Ast.TokenIndex) !Zir.NullTerminatedString {
11364 if (end_token == 0) return .empty;
11365
11366 const token_tags = astgen.tree.tokens.items(.tag);
11367
11368 var tok = end_token - 1;
11369 while (token_tags[tok] == .doc_comment) {
11370 if (tok == 0) break;
11371 tok -= 1;
11372 } else {
11373 tok += 1;
11374 }
11375
11376 return docCommentAsStringFromFirst(astgen, end_token, tok);
11377}
11378
11379/// end_token must be > the index of the last doc comment.
11380fn docCommentAsStringFromFirst(
11381 astgen: *AstGen,
11382 end_token: Ast.TokenIndex,
11383 start_token: Ast.TokenIndex,
11384) !Zir.NullTerminatedString {
11385 if (start_token == end_token) return .empty;
11386
11387 const gpa = astgen.gpa;
11388 const string_bytes = &astgen.string_bytes;
11389 const str_index: u32 = @intCast(string_bytes.items.len);
11390 const token_starts = astgen.tree.tokens.items(.start);
11391 const token_tags = astgen.tree.tokens.items(.tag);
11392
11393 const total_bytes = token_starts[end_token] - token_starts[start_token];
11394 try string_bytes.ensureUnusedCapacity(gpa, total_bytes);
11395
11396 var current_token = start_token;
11397 while (current_token < end_token) : (current_token += 1) {
11398 switch (token_tags[current_token]) {
11399 .doc_comment => {
11400 const tok_bytes = astgen.tree.tokenSlice(current_token)[3..];
11401 string_bytes.appendSliceAssumeCapacity(tok_bytes);
11402 if (current_token != end_token - 1) {
11403 string_bytes.appendAssumeCapacity('\n');
11404 }
11405 },
11406 else => break,
11407 }
11408 }
11409
11410 const key: []const u8 = string_bytes.items[str_index..];
11411 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
11412 .bytes = string_bytes,
11413 }, StringIndexContext{
11414 .bytes = string_bytes,
11415 });
11416
11417 if (gop.found_existing) {
11418 string_bytes.shrinkRetainingCapacity(str_index);
11419 return @enumFromInt(gop.key_ptr.*);
11420 } else {
11421 gop.key_ptr.* = str_index;
11422 try string_bytes.append(gpa, 0);
11423 return @enumFromInt(str_index);
11424 }
11425}
11426
11427const IndexSlice = struct { index: Zir.NullTerminatedString, len: u32 };
11428
11429fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice {
11430 const gpa = astgen.gpa;
11431 const string_bytes = &astgen.string_bytes;
11432 const str_index: u32 = @intCast(string_bytes.items.len);
11433 const token_bytes = astgen.tree.tokenSlice(str_lit_token);
11434 try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
11435 const key: []const u8 = string_bytes.items[str_index..];
11436 if (std.mem.indexOfScalar(u8, key, 0)) |_| return .{
11437 .index = @enumFromInt(str_index),
11438 .len = @intCast(key.len),
11439 };
11440 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
11441 .bytes = string_bytes,
11442 }, StringIndexContext{
11443 .bytes = string_bytes,
11444 });
11445 if (gop.found_existing) {
11446 string_bytes.shrinkRetainingCapacity(str_index);
11447 return .{
11448 .index = @enumFromInt(gop.key_ptr.*),
11449 .len = @intCast(key.len),
11450 };
11451 } else {
11452 gop.key_ptr.* = str_index;
11453 // Still need a null byte because we are using the same table
11454 // to lookup null terminated strings, so if we get a match, it has to
11455 // be null terminated for that to work.
11456 try string_bytes.append(gpa, 0);
11457 return .{
11458 .index = @enumFromInt(str_index),
11459 .len = @intCast(key.len),
11460 };
11461 }
11462}
11463
11464fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice {
11465 const tree = astgen.tree;
11466 const node_datas = tree.nodes.items(.data);
11467
11468 const start = node_datas[node].lhs;
11469 const end = node_datas[node].rhs;
11470
11471 const gpa = astgen.gpa;
11472 const string_bytes = &astgen.string_bytes;
11473 const str_index = string_bytes.items.len;
11474
11475 // First line: do not append a newline.
11476 var tok_i = start;
11477 {
11478 const slice = tree.tokenSlice(tok_i);
11479 const carriage_return_ending: usize = if (slice[slice.len - 2] == '\r') 2 else 1;
11480 const line_bytes = slice[2 .. slice.len - carriage_return_ending];
11481 try string_bytes.appendSlice(gpa, line_bytes);
11482 tok_i += 1;
11483 }
11484 // Following lines: each line prepends a newline.
11485 while (tok_i <= end) : (tok_i += 1) {
11486 const slice = tree.tokenSlice(tok_i);
11487 const carriage_return_ending: usize = if (slice[slice.len - 2] == '\r') 2 else 1;
11488 const line_bytes = slice[2 .. slice.len - carriage_return_ending];
11489 try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1);
11490 string_bytes.appendAssumeCapacity('\n');
11491 string_bytes.appendSliceAssumeCapacity(line_bytes);
11492 }
11493 const len = string_bytes.items.len - str_index;
11494 try string_bytes.append(gpa, 0);
11495 return IndexSlice{
11496 .index = @enumFromInt(str_index),
11497 .len = @intCast(len),
11498 };
11499}
11500
11501fn testNameString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !Zir.NullTerminatedString {
11502 const gpa = astgen.gpa;
11503 const string_bytes = &astgen.string_bytes;
11504 const str_index: u32 = @intCast(string_bytes.items.len);
11505 const token_bytes = astgen.tree.tokenSlice(str_lit_token);
11506 try string_bytes.append(gpa, 0); // Indicates this is a test.
11507 try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
11508 const slice = string_bytes.items[str_index + 1 ..];
11509 if (mem.indexOfScalar(u8, slice, 0) != null) {
11510 return astgen.failTok(str_lit_token, "test name cannot contain null bytes", .{});
11511 } else if (slice.len == 0) {
11512 return astgen.failTok(str_lit_token, "empty test name must be omitted", .{});
11513 }
11514 try string_bytes.append(gpa, 0);
11515 return @enumFromInt(str_index);
11516}
11517
11518const Scope = struct {
11519 tag: Tag,
11520
11521 fn cast(base: *Scope, comptime T: type) ?*T {
11522 if (T == Defer) {
11523 switch (base.tag) {
11524 .defer_normal, .defer_error => return @fieldParentPtr(T, "base", base),
11525 else => return null,
11526 }
11527 }
11528 if (T == Namespace) {
11529 switch (base.tag) {
11530 .namespace, .enum_namespace => return @fieldParentPtr(T, "base", base),
11531 else => return null,
11532 }
11533 }
11534 if (base.tag != T.base_tag)
11535 return null;
11536
11537 return @fieldParentPtr(T, "base", base);
11538 }
11539
11540 fn parent(base: *Scope) ?*Scope {
11541 return switch (base.tag) {
11542 .gen_zir => base.cast(GenZir).?.parent,
11543 .local_val => base.cast(LocalVal).?.parent,
11544 .local_ptr => base.cast(LocalPtr).?.parent,
11545 .defer_normal, .defer_error => base.cast(Defer).?.parent,
11546 .namespace, .enum_namespace => base.cast(Namespace).?.parent,
11547 .top => null,
11548 };
11549 }
11550
11551 const Tag = enum {
11552 gen_zir,
11553 local_val,
11554 local_ptr,
11555 defer_normal,
11556 defer_error,
11557 namespace,
11558 enum_namespace,
11559 top,
11560 };
11561
11562 /// The category of identifier. These tag names are user-visible in compile errors.
11563 const IdCat = enum {
11564 @"function parameter",
11565 @"local constant",
11566 @"local variable",
11567 @"switch tag capture",
11568 capture,
11569 };
11570
11571 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
11572 /// This structure lives as long as the AST generation of the Block
11573 /// node that contains the variable.
11574 const LocalVal = struct {
11575 const base_tag: Tag = .local_val;
11576 base: Scope = Scope{ .tag = base_tag },
11577 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
11578 parent: *Scope,
11579 gen_zir: *GenZir,
11580 inst: Zir.Inst.Ref,
11581 /// Source location of the corresponding variable declaration.
11582 token_src: Ast.TokenIndex,
11583 /// Track the first identifer where it is referenced.
11584 /// 0 means never referenced.
11585 used: Ast.TokenIndex = 0,
11586 /// Track the identifier where it is discarded, like this `_ = foo;`.
11587 /// 0 means never discarded.
11588 discarded: Ast.TokenIndex = 0,
11589 /// String table index.
11590 name: Zir.NullTerminatedString,
11591 id_cat: IdCat,
11592 };
11593
11594 /// This could be a `const` or `var` local. It has a pointer instead of a value.
11595 /// This structure lives as long as the AST generation of the Block
11596 /// node that contains the variable.
11597 const LocalPtr = struct {
11598 const base_tag: Tag = .local_ptr;
11599 base: Scope = Scope{ .tag = base_tag },
11600 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
11601 parent: *Scope,
11602 gen_zir: *GenZir,
11603 ptr: Zir.Inst.Ref,
11604 /// Source location of the corresponding variable declaration.
11605 token_src: Ast.TokenIndex,
11606 /// Track the first identifer where it is referenced.
11607 /// 0 means never referenced.
11608 used: Ast.TokenIndex = 0,
11609 /// Track the identifier where it is discarded, like this `_ = foo;`.
11610 /// 0 means never discarded.
11611 discarded: Ast.TokenIndex = 0,
11612 /// Whether this value is used as an lvalue after inititialization.
11613 /// If not, we know it can be `const`, so will emit a compile error if it is `var`.
11614 used_as_lvalue: bool = false,
11615 /// String table index.
11616 name: Zir.NullTerminatedString,
11617 id_cat: IdCat,
11618 /// true means we find out during Sema whether the value is comptime.
11619 /// false means it is already known at AstGen the value is runtime-known.
11620 maybe_comptime: bool,
11621 };
11622
11623 const Defer = struct {
11624 base: Scope,
11625 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
11626 parent: *Scope,
11627 index: u32,
11628 len: u32,
11629 remapped_err_code: Zir.Inst.OptionalIndex = .none,
11630 };
11631
11632 /// Represents a global scope that has any number of declarations in it.
11633 /// Each declaration has this as the parent scope.
11634 const Namespace = struct {
11635 const base_tag: Tag = .namespace;
11636 base: Scope = Scope{ .tag = base_tag },
11637
11638 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
11639 parent: *Scope,
11640 /// Maps string table index to the source location of declaration,
11641 /// for the purposes of reporting name shadowing compile errors.
11642 decls: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .{},
11643 node: Ast.Node.Index,
11644 inst: Zir.Inst.Index,
11645
11646 /// The astgen scope containing this namespace.
11647 /// Only valid during astgen.
11648 declaring_gz: ?*GenZir,
11649
11650 /// Map from the raw captured value to the instruction
11651 /// ref of the capture for decls in this namespace
11652 captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
11653
11654 fn deinit(self: *Namespace, gpa: Allocator) void {
11655 self.decls.deinit(gpa);
11656 self.captures.deinit(gpa);
11657 self.* = undefined;
11658 }
11659 };
11660
11661 const Top = struct {
11662 const base_tag: Scope.Tag = .top;
11663 base: Scope = Scope{ .tag = base_tag },
11664 };
11665};
11666
11667/// This is a temporary structure; references to it are valid only
11668/// while constructing a `Zir`.
11669const GenZir = struct {
11670 const base_tag: Scope.Tag = .gen_zir;
11671 base: Scope = Scope{ .tag = base_tag },
11672 /// Whether we're already in a scope known to be comptime. This is set
11673 /// whenever we know Sema will analyze the current block with `is_comptime`,
11674 /// for instance when we're within a `struct_decl` or a `block_comptime`.
11675 is_comptime: bool,
11676 /// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime
11677 /// variables is permitted where it is usually not.
11678 is_typeof: bool = false,
11679 /// This is set to true for inline loops; false otherwise.
11680 is_inline: bool = false,
11681 c_import: bool = false,
11682 /// How decls created in this scope should be named.
11683 anon_name_strategy: Zir.Inst.NameStrategy = .anon,
11684 /// The containing decl AST node.
11685 decl_node_index: Ast.Node.Index,
11686 /// The containing decl line index, absolute.
11687 decl_line: u32,
11688 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
11689 parent: *Scope,
11690 /// All `GenZir` scopes for the same ZIR share this.
11691 astgen: *AstGen,
11692 /// Keeps track of the list of instructions in this scope. Possibly shared.
11693 /// Indexes to instructions in `astgen`.
11694 instructions: *ArrayListUnmanaged(Zir.Inst.Index),
11695 /// A sub-block may share its instructions ArrayList with containing GenZir,
11696 /// if use is strictly nested. This saves prior size of list for unstacking.
11697 instructions_top: usize,
11698 label: ?Label = null,
11699 break_block: Zir.Inst.OptionalIndex = .none,
11700 continue_block: Zir.Inst.OptionalIndex = .none,
11701 /// Only valid when setBreakResultInfo is called.
11702 break_result_info: AstGen.ResultInfo = undefined,
11703
11704 suspend_node: Ast.Node.Index = 0,
11705 nosuspend_node: Ast.Node.Index = 0,
11706 /// Set if this GenZir is a defer.
11707 cur_defer_node: Ast.Node.Index = 0,
11708 // Set if this GenZir is a defer or it is inside a defer.
11709 any_defer_node: Ast.Node.Index = 0,
11710
11711 /// Namespace members are lazy. When executing a decl within a namespace,
11712 /// any references to external instructions need to be treated specially.
11713 /// This list tracks those references. See also .closure_capture and .closure_get.
11714 /// Keys are the raw instruction index, values are the closure_capture instruction.
11715 captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
11716
11717 const unstacked_top = std.math.maxInt(usize);
11718 /// Call unstack before adding any new instructions to containing GenZir.
11719 fn unstack(self: *GenZir) void {
11720 if (self.instructions_top != unstacked_top) {
11721 self.instructions.items.len = self.instructions_top;
11722 self.instructions_top = unstacked_top;
11723 }
11724 }
11725
11726 fn isEmpty(self: *const GenZir) bool {
11727 return (self.instructions_top == unstacked_top) or
11728 (self.instructions.items.len == self.instructions_top);
11729 }
11730
11731 fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index {
11732 return if (self.instructions_top == unstacked_top)
11733 &[0]Zir.Inst.Index{}
11734 else
11735 self.instructions.items[self.instructions_top..];
11736 }
11737
11738 fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index {
11739 return if (self.instructions_top == unstacked_top)
11740 &[0]Zir.Inst.Index{}
11741 else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top)
11742 self.instructions.items[self.instructions_top..stacked_gz.instructions_top]
11743 else
11744 self.instructions.items[self.instructions_top..];
11745 }
11746
11747 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
11748 return .{
11749 .is_comptime = gz.is_comptime,
11750 .is_typeof = gz.is_typeof,
11751 .c_import = gz.c_import,
11752 .decl_node_index = gz.decl_node_index,
11753 .decl_line = gz.decl_line,
11754 .parent = scope,
11755 .astgen = gz.astgen,
11756 .suspend_node = gz.suspend_node,
11757 .nosuspend_node = gz.nosuspend_node,
11758 .any_defer_node = gz.any_defer_node,
11759 .instructions = gz.instructions,
11760 .instructions_top = gz.instructions.items.len,
11761 };
11762 }
11763
11764 const Label = struct {
11765 token: Ast.TokenIndex,
11766 block_inst: Zir.Inst.Index,
11767 used: bool = false,
11768 };
11769
11770 /// Assumes nothing stacked on `gz`.
11771 fn endsWithNoReturn(gz: GenZir) bool {
11772 if (gz.isEmpty()) return false;
11773 const tags = gz.astgen.instructions.items(.tag);
11774 const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
11775 return tags[@intFromEnum(last_inst)].isNoReturn();
11776 }
11777
11778 /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`.
11779 fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
11780 if (inst_ref == .unreachable_value) return true;
11781 if (inst_ref.toIndex()) |inst_index| {
11782 return gz.astgen.instructions.items(.tag)[@intFromEnum(inst_index)].isNoReturn();
11783 }
11784 return false;
11785 }
11786
11787 fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) i32 {
11788 return @as(i32, @bitCast(node_index)) - @as(i32, @bitCast(gz.decl_node_index));
11789 }
11790
11791 fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) u32 {
11792 return token - gz.srcToken();
11793 }
11794
11795 fn srcToken(gz: GenZir) Ast.TokenIndex {
11796 return gz.astgen.tree.firstToken(gz.decl_node_index);
11797 }
11798
11799 fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void {
11800 // Depending on whether the result location is a pointer or value, different
11801 // ZIR needs to be generated. In the former case we rely on storing to the
11802 // pointer to communicate the result, and use breakvoid; in the latter case
11803 // the block break instructions will have the result values.
11804 switch (parent_ri.rl) {
11805 .coerced_ty => |ty_inst| {
11806 // Type coercion needs to happen before breaks.
11807 gz.break_result_info = .{ .rl = .{ .ty = ty_inst }, .ctx = parent_ri.ctx };
11808 },
11809 .discard => {
11810 // We don't forward the result context here. This prevents
11811 // "unnecessary discard" errors from being caused by expressions
11812 // far from the actual discard, such as a `break` from a
11813 // discarded block.
11814 gz.break_result_info = .{ .rl = .discard };
11815 },
11816 else => {
11817 gz.break_result_info = parent_ri;
11818 },
11819 }
11820 }
11821
11822 /// Assumes nothing stacked on `gz`. Unstacks `gz`.
11823 fn setBoolBrBody(gz: *GenZir, bool_br: Zir.Inst.Index, bool_br_lhs: Zir.Inst.Ref) !void {
11824 const astgen = gz.astgen;
11825 const gpa = astgen.gpa;
11826 const body = gz.instructionsSlice();
11827 const body_len = astgen.countBodyLenAfterFixups(body);
11828 try astgen.extra.ensureUnusedCapacity(
11829 gpa,
11830 @typeInfo(Zir.Inst.BoolBr).Struct.fields.len + body_len,
11831 );
11832 const zir_datas = astgen.instructions.items(.data);
11833 zir_datas[@intFromEnum(bool_br)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.BoolBr{
11834 .lhs = bool_br_lhs,
11835 .body_len = body_len,
11836 });
11837 astgen.appendBodyWithFixups(body);
11838 gz.unstack();
11839 }
11840
11841 /// Assumes nothing stacked on `gz`. Unstacks `gz`.
11842 fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
11843 const astgen = gz.astgen;
11844 const gpa = astgen.gpa;
11845 const body = gz.instructionsSlice();
11846 const body_len = astgen.countBodyLenAfterFixups(body);
11847 try astgen.extra.ensureUnusedCapacity(
11848 gpa,
11849 @typeInfo(Zir.Inst.Block).Struct.fields.len + body_len,
11850 );
11851 const zir_datas = astgen.instructions.items(.data);
11852 zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
11853 Zir.Inst.Block{ .body_len = body_len },
11854 );
11855 astgen.appendBodyWithFixups(body);
11856 gz.unstack();
11857 }
11858
11859 /// Assumes nothing stacked on `gz`. Unstacks `gz`.
11860 fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
11861 const astgen = gz.astgen;
11862 const gpa = astgen.gpa;
11863 const body = gz.instructionsSlice();
11864 const body_len = astgen.countBodyLenAfterFixups(body);
11865 try astgen.extra.ensureUnusedCapacity(
11866 gpa,
11867 @typeInfo(Zir.Inst.Try).Struct.fields.len + body_len,
11868 );
11869 const zir_datas = astgen.instructions.items(.data);
11870 zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
11871 Zir.Inst.Try{
11872 .operand = operand,
11873 .body_len = body_len,
11874 },
11875 );
11876 astgen.appendBodyWithFixups(body);
11877 gz.unstack();
11878 }
11879
11880 /// Must be called with the following stack set up:
11881 /// * gz (bottom)
11882 /// * align_gz
11883 /// * addrspace_gz
11884 /// * section_gz
11885 /// * cc_gz
11886 /// * ret_gz
11887 /// * body_gz (top)
11888 /// Unstacks all of those except for `gz`.
11889 fn addFunc(gz: *GenZir, args: struct {
11890 src_node: Ast.Node.Index,
11891 lbrace_line: u32 = 0,
11892 lbrace_column: u32 = 0,
11893 param_block: Zir.Inst.Index,
11894
11895 align_gz: ?*GenZir,
11896 addrspace_gz: ?*GenZir,
11897 section_gz: ?*GenZir,
11898 cc_gz: ?*GenZir,
11899 ret_gz: ?*GenZir,
11900 body_gz: ?*GenZir,
11901
11902 align_ref: Zir.Inst.Ref,
11903 addrspace_ref: Zir.Inst.Ref,
11904 section_ref: Zir.Inst.Ref,
11905 cc_ref: Zir.Inst.Ref,
11906 ret_ref: Zir.Inst.Ref,
11907
11908 lib_name: Zir.NullTerminatedString,
11909 noalias_bits: u32,
11910 is_var_args: bool,
11911 is_inferred_error: bool,
11912 is_test: bool,
11913 is_extern: bool,
11914 is_noinline: bool,
11915 }) !Zir.Inst.Ref {
11916 assert(args.src_node != 0);
11917 const astgen = gz.astgen;
11918 const gpa = astgen.gpa;
11919 const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref;
11920 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
11921
11922 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
11923
11924 var body: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
11925 var ret_body: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
11926 var src_locs_and_hash_buffer: [7]u32 = undefined;
11927 var src_locs_and_hash: []u32 = src_locs_and_hash_buffer[0..0];
11928 if (args.body_gz) |body_gz| {
11929 const tree = astgen.tree;
11930 const node_tags = tree.nodes.items(.tag);
11931 const node_datas = tree.nodes.items(.data);
11932 const token_starts = tree.tokens.items(.start);
11933 const fn_decl = args.src_node;
11934 assert(node_tags[fn_decl] == .fn_decl or node_tags[fn_decl] == .test_decl);
11935 const block = node_datas[fn_decl].rhs;
11936 const rbrace_start = token_starts[tree.lastToken(block)];
11937 astgen.advanceSourceCursor(rbrace_start);
11938 const rbrace_line: u32 = @intCast(astgen.source_line - gz.decl_line);
11939 const rbrace_column: u32 = @intCast(astgen.source_column);
11940
11941 const columns = args.lbrace_column | (rbrace_column << 16);
11942
11943 const proto_hash: std.zig.SrcHash = switch (node_tags[fn_decl]) {
11944 .fn_decl => sig_hash: {
11945 const proto_node = node_datas[fn_decl].lhs;
11946 break :sig_hash std.zig.hashSrc(tree.getNodeSource(proto_node));
11947 },
11948 .test_decl => std.zig.hashSrc(""), // tests don't have a prototype
11949 else => unreachable,
11950 };
11951 const proto_hash_arr: [4]u32 = @bitCast(proto_hash);
11952
11953 src_locs_and_hash_buffer = .{
11954 args.lbrace_line,
11955 rbrace_line,
11956 columns,
11957 proto_hash_arr[0],
11958 proto_hash_arr[1],
11959 proto_hash_arr[2],
11960 proto_hash_arr[3],
11961 };
11962 src_locs_and_hash = &src_locs_and_hash_buffer;
11963
11964 body = body_gz.instructionsSlice();
11965 if (args.ret_gz) |ret_gz|
11966 ret_body = ret_gz.instructionsSliceUpto(body_gz);
11967 } else {
11968 if (args.ret_gz) |ret_gz|
11969 ret_body = ret_gz.instructionsSlice();
11970 }
11971 const body_len = astgen.countBodyLenAfterFixups(body);
11972
11973 if (args.cc_ref != .none or args.lib_name != .empty or args.is_var_args or args.is_test or
11974 args.is_extern or args.align_ref != .none or args.section_ref != .none or
11975 args.addrspace_ref != .none or args.noalias_bits != 0 or args.is_noinline)
11976 {
11977 var align_body: []Zir.Inst.Index = &.{};
11978 var addrspace_body: []Zir.Inst.Index = &.{};
11979 var section_body: []Zir.Inst.Index = &.{};
11980 var cc_body: []Zir.Inst.Index = &.{};
11981 if (args.ret_gz != null) {
11982 align_body = args.align_gz.?.instructionsSliceUpto(args.addrspace_gz.?);
11983 addrspace_body = args.addrspace_gz.?.instructionsSliceUpto(args.section_gz.?);
11984 section_body = args.section_gz.?.instructionsSliceUpto(args.cc_gz.?);
11985 cc_body = args.cc_gz.?.instructionsSliceUpto(args.ret_gz.?);
11986 }
11987
11988 try astgen.extra.ensureUnusedCapacity(
11989 gpa,
11990 @typeInfo(Zir.Inst.FuncFancy).Struct.fields.len +
11991 fancyFnExprExtraLen(astgen, align_body, args.align_ref) +
11992 fancyFnExprExtraLen(astgen, addrspace_body, args.addrspace_ref) +
11993 fancyFnExprExtraLen(astgen, section_body, args.section_ref) +
11994 fancyFnExprExtraLen(astgen, cc_body, args.cc_ref) +
11995 fancyFnExprExtraLen(astgen, ret_body, ret_ref) +
11996 body_len + src_locs_and_hash.len +
11997 @intFromBool(args.lib_name != .empty) +
11998 @intFromBool(args.noalias_bits != 0),
11999 );
12000 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
12001 .param_block = args.param_block,
12002 .body_len = body_len,
12003 .bits = .{
12004 .is_var_args = args.is_var_args,
12005 .is_inferred_error = args.is_inferred_error,
12006 .is_test = args.is_test,
12007 .is_extern = args.is_extern,
12008 .is_noinline = args.is_noinline,
12009 .has_lib_name = args.lib_name != .empty,
12010 .has_any_noalias = args.noalias_bits != 0,
12011
12012 .has_align_ref = args.align_ref != .none,
12013 .has_addrspace_ref = args.addrspace_ref != .none,
12014 .has_section_ref = args.section_ref != .none,
12015 .has_cc_ref = args.cc_ref != .none,
12016 .has_ret_ty_ref = ret_ref != .none,
12017
12018 .has_align_body = align_body.len != 0,
12019 .has_addrspace_body = addrspace_body.len != 0,
12020 .has_section_body = section_body.len != 0,
12021 .has_cc_body = cc_body.len != 0,
12022 .has_ret_ty_body = ret_body.len != 0,
12023 },
12024 });
12025 if (args.lib_name != .empty) {
12026 astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name));
12027 }
12028
12029 const zir_datas = astgen.instructions.items(.data);
12030 if (align_body.len != 0) {
12031 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, align_body));
12032 astgen.appendBodyWithFixups(align_body);
12033 const break_extra = zir_datas[@intFromEnum(align_body[align_body.len - 1])].@"break".payload_index;
12034 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
12035 @intFromEnum(new_index);
12036 } else if (args.align_ref != .none) {
12037 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_ref));
12038 }
12039 if (addrspace_body.len != 0) {
12040 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, addrspace_body));
12041 astgen.appendBodyWithFixups(addrspace_body);
12042 const break_extra =
12043 zir_datas[@intFromEnum(addrspace_body[addrspace_body.len - 1])].@"break".payload_index;
12044 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
12045 @intFromEnum(new_index);
12046 } else if (args.addrspace_ref != .none) {
12047 astgen.extra.appendAssumeCapacity(@intFromEnum(args.addrspace_ref));
12048 }
12049 if (section_body.len != 0) {
12050 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, section_body));
12051 astgen.appendBodyWithFixups(section_body);
12052 const break_extra =
12053 zir_datas[@intFromEnum(section_body[section_body.len - 1])].@"break".payload_index;
12054 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
12055 @intFromEnum(new_index);
12056 } else if (args.section_ref != .none) {
12057 astgen.extra.appendAssumeCapacity(@intFromEnum(args.section_ref));
12058 }
12059 if (cc_body.len != 0) {
12060 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, cc_body));
12061 astgen.appendBodyWithFixups(cc_body);
12062 const break_extra = zir_datas[@intFromEnum(cc_body[cc_body.len - 1])].@"break".payload_index;
12063 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
12064 @intFromEnum(new_index);
12065 } else if (args.cc_ref != .none) {
12066 astgen.extra.appendAssumeCapacity(@intFromEnum(args.cc_ref));
12067 }
12068 if (ret_body.len != 0) {
12069 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, ret_body));
12070 astgen.appendBodyWithFixups(ret_body);
12071 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index;
12072 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
12073 @intFromEnum(new_index);
12074 } else if (ret_ref != .none) {
12075 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref));
12076 }
12077
12078 if (args.noalias_bits != 0) {
12079 astgen.extra.appendAssumeCapacity(args.noalias_bits);
12080 }
12081
12082 astgen.appendBodyWithFixups(body);
12083 astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
12084
12085 // Order is important when unstacking.
12086 if (args.body_gz) |body_gz| body_gz.unstack();
12087 if (args.ret_gz != null) {
12088 args.ret_gz.?.unstack();
12089 args.cc_gz.?.unstack();
12090 args.section_gz.?.unstack();
12091 args.addrspace_gz.?.unstack();
12092 args.align_gz.?.unstack();
12093 }
12094
12095 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12096
12097 astgen.instructions.appendAssumeCapacity(.{
12098 .tag = .func_fancy,
12099 .data = .{ .pl_node = .{
12100 .src_node = gz.nodeIndexToRelative(args.src_node),
12101 .payload_index = payload_index,
12102 } },
12103 });
12104 gz.instructions.appendAssumeCapacity(new_index);
12105 return new_index.toRef();
12106 } else {
12107 try astgen.extra.ensureUnusedCapacity(
12108 gpa,
12109 @typeInfo(Zir.Inst.Func).Struct.fields.len + 1 +
12110 fancyFnExprExtraLen(astgen, ret_body, ret_ref) +
12111 body_len + src_locs_and_hash.len,
12112 );
12113
12114 const ret_body_len = if (ret_body.len != 0)
12115 countBodyLenAfterFixups(astgen, ret_body)
12116 else
12117 @intFromBool(ret_ref != .none);
12118
12119 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{
12120 .param_block = args.param_block,
12121 .ret_body_len = ret_body_len,
12122 .body_len = body_len,
12123 });
12124 const zir_datas = astgen.instructions.items(.data);
12125 if (ret_body.len != 0) {
12126 astgen.appendBodyWithFixups(ret_body);
12127
12128 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index;
12129 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
12130 @intFromEnum(new_index);
12131 } else if (ret_ref != .none) {
12132 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref));
12133 }
12134 astgen.appendBodyWithFixups(body);
12135 astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
12136
12137 // Order is important when unstacking.
12138 if (args.body_gz) |body_gz| body_gz.unstack();
12139 if (args.ret_gz) |ret_gz| ret_gz.unstack();
12140 if (args.cc_gz) |cc_gz| cc_gz.unstack();
12141 if (args.section_gz) |section_gz| section_gz.unstack();
12142 if (args.addrspace_gz) |addrspace_gz| addrspace_gz.unstack();
12143 if (args.align_gz) |align_gz| align_gz.unstack();
12144
12145 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12146
12147 const tag: Zir.Inst.Tag = if (args.is_inferred_error) .func_inferred else .func;
12148 astgen.instructions.appendAssumeCapacity(.{
12149 .tag = tag,
12150 .data = .{ .pl_node = .{
12151 .src_node = gz.nodeIndexToRelative(args.src_node),
12152 .payload_index = payload_index,
12153 } },
12154 });
12155 gz.instructions.appendAssumeCapacity(new_index);
12156 return new_index.toRef();
12157 }
12158 }
12159
12160 fn fancyFnExprExtraLen(astgen: *AstGen, body: []Zir.Inst.Index, ref: Zir.Inst.Ref) u32 {
12161 // In the case of non-empty body, there is one for the body length,
12162 // and then one for each instruction.
12163 return countBodyLenAfterFixups(astgen, body) + @intFromBool(ref != .none);
12164 }
12165
12166 fn addVar(gz: *GenZir, args: struct {
12167 align_inst: Zir.Inst.Ref,
12168 lib_name: Zir.NullTerminatedString,
12169 var_type: Zir.Inst.Ref,
12170 init: Zir.Inst.Ref,
12171 is_extern: bool,
12172 is_const: bool,
12173 is_threadlocal: bool,
12174 }) !Zir.Inst.Ref {
12175 const astgen = gz.astgen;
12176 const gpa = astgen.gpa;
12177
12178 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12179 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
12180
12181 try astgen.extra.ensureUnusedCapacity(
12182 gpa,
12183 @typeInfo(Zir.Inst.ExtendedVar).Struct.fields.len +
12184 @intFromBool(args.lib_name != .empty) +
12185 @intFromBool(args.align_inst != .none) +
12186 @intFromBool(args.init != .none),
12187 );
12188 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedVar{
12189 .var_type = args.var_type,
12190 });
12191 if (args.lib_name != .empty) {
12192 astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name));
12193 }
12194 if (args.align_inst != .none) {
12195 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst));
12196 }
12197 if (args.init != .none) {
12198 astgen.extra.appendAssumeCapacity(@intFromEnum(args.init));
12199 }
12200
12201 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12202 astgen.instructions.appendAssumeCapacity(.{
12203 .tag = .extended,
12204 .data = .{ .extended = .{
12205 .opcode = .variable,
12206 .small = @bitCast(Zir.Inst.ExtendedVar.Small{
12207 .has_lib_name = args.lib_name != .empty,
12208 .has_align = args.align_inst != .none,
12209 .has_init = args.init != .none,
12210 .is_extern = args.is_extern,
12211 .is_const = args.is_const,
12212 .is_threadlocal = args.is_threadlocal,
12213 }),
12214 .operand = payload_index,
12215 } },
12216 });
12217 gz.instructions.appendAssumeCapacity(new_index);
12218 return new_index.toRef();
12219 }
12220
12221 fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref {
12222 return gz.add(.{
12223 .tag = .int,
12224 .data = .{ .int = integer },
12225 });
12226 }
12227
12228 fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !Zir.Inst.Ref {
12229 const astgen = gz.astgen;
12230 const gpa = astgen.gpa;
12231 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12232 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
12233 try astgen.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len);
12234
12235 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12236 astgen.instructions.appendAssumeCapacity(.{
12237 .tag = .int_big,
12238 .data = .{ .str = .{
12239 .start = @enumFromInt(astgen.string_bytes.items.len),
12240 .len = @intCast(limbs.len),
12241 } },
12242 });
12243 gz.instructions.appendAssumeCapacity(new_index);
12244 astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
12245 return new_index.toRef();
12246 }
12247
12248 fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
12249 return gz.add(.{
12250 .tag = .float,
12251 .data = .{ .float = number },
12252 });
12253 }
12254
12255 fn addUnNode(
12256 gz: *GenZir,
12257 tag: Zir.Inst.Tag,
12258 operand: Zir.Inst.Ref,
12259 /// Absolute node index. This function does the conversion to offset from Decl.
12260 src_node: Ast.Node.Index,
12261 ) !Zir.Inst.Ref {
12262 assert(operand != .none);
12263 return gz.add(.{
12264 .tag = tag,
12265 .data = .{ .un_node = .{
12266 .operand = operand,
12267 .src_node = gz.nodeIndexToRelative(src_node),
12268 } },
12269 });
12270 }
12271
12272 fn makeUnNode(
12273 gz: *GenZir,
12274 tag: Zir.Inst.Tag,
12275 operand: Zir.Inst.Ref,
12276 /// Absolute node index. This function does the conversion to offset from Decl.
12277 src_node: Ast.Node.Index,
12278 ) !Zir.Inst.Index {
12279 assert(operand != .none);
12280 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12281 try gz.astgen.instructions.append(gz.astgen.gpa, .{
12282 .tag = tag,
12283 .data = .{ .un_node = .{
12284 .operand = operand,
12285 .src_node = gz.nodeIndexToRelative(src_node),
12286 } },
12287 });
12288 return new_index;
12289 }
12290
12291 fn addPlNode(
12292 gz: *GenZir,
12293 tag: Zir.Inst.Tag,
12294 /// Absolute node index. This function does the conversion to offset from Decl.
12295 src_node: Ast.Node.Index,
12296 extra: anytype,
12297 ) !Zir.Inst.Ref {
12298 const gpa = gz.astgen.gpa;
12299 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12300 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
12301
12302 const payload_index = try gz.astgen.addExtra(extra);
12303 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12304 gz.astgen.instructions.appendAssumeCapacity(.{
12305 .tag = tag,
12306 .data = .{ .pl_node = .{
12307 .src_node = gz.nodeIndexToRelative(src_node),
12308 .payload_index = payload_index,
12309 } },
12310 });
12311 gz.instructions.appendAssumeCapacity(new_index);
12312 return new_index.toRef();
12313 }
12314
12315 fn addPlNodePayloadIndex(
12316 gz: *GenZir,
12317 tag: Zir.Inst.Tag,
12318 /// Absolute node index. This function does the conversion to offset from Decl.
12319 src_node: Ast.Node.Index,
12320 payload_index: u32,
12321 ) !Zir.Inst.Ref {
12322 return try gz.add(.{
12323 .tag = tag,
12324 .data = .{ .pl_node = .{
12325 .src_node = gz.nodeIndexToRelative(src_node),
12326 .payload_index = payload_index,
12327 } },
12328 });
12329 }
12330
12331 /// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`.
12332 fn addParam(
12333 gz: *GenZir,
12334 param_gz: *GenZir,
12335 tag: Zir.Inst.Tag,
12336 /// Absolute token index. This function does the conversion to Decl offset.
12337 abs_tok_index: Ast.TokenIndex,
12338 name: Zir.NullTerminatedString,
12339 first_doc_comment: ?Ast.TokenIndex,
12340 ) !Zir.Inst.Index {
12341 const gpa = gz.astgen.gpa;
12342 const param_body = param_gz.instructionsSlice();
12343 const body_len = gz.astgen.countBodyLenAfterFixups(param_body);
12344 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
12345 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).Struct.fields.len + body_len);
12346
12347 const doc_comment_index = if (first_doc_comment) |first|
12348 try gz.astgen.docCommentAsStringFromFirst(abs_tok_index, first)
12349 else
12350 .empty;
12351
12352 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{
12353 .name = name,
12354 .doc_comment = doc_comment_index,
12355 .body_len = @intCast(body_len),
12356 });
12357 gz.astgen.appendBodyWithFixups(param_body);
12358 param_gz.unstack();
12359
12360 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12361 gz.astgen.instructions.appendAssumeCapacity(.{
12362 .tag = tag,
12363 .data = .{ .pl_tok = .{
12364 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
12365 .payload_index = payload_index,
12366 } },
12367 });
12368 gz.instructions.appendAssumeCapacity(new_index);
12369 return new_index;
12370 }
12371
12372 fn addExtendedPayload(gz: *GenZir, opcode: Zir.Inst.Extended, extra: anytype) !Zir.Inst.Ref {
12373 return addExtendedPayloadSmall(gz, opcode, undefined, extra);
12374 }
12375
12376 fn addExtendedPayloadSmall(
12377 gz: *GenZir,
12378 opcode: Zir.Inst.Extended,
12379 small: u16,
12380 extra: anytype,
12381 ) !Zir.Inst.Ref {
12382 const gpa = gz.astgen.gpa;
12383
12384 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12385 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
12386
12387 const payload_index = try gz.astgen.addExtra(extra);
12388 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12389 gz.astgen.instructions.appendAssumeCapacity(.{
12390 .tag = .extended,
12391 .data = .{ .extended = .{
12392 .opcode = opcode,
12393 .small = small,
12394 .operand = payload_index,
12395 } },
12396 });
12397 gz.instructions.appendAssumeCapacity(new_index);
12398 return new_index.toRef();
12399 }
12400
12401 fn addExtendedMultiOp(
12402 gz: *GenZir,
12403 opcode: Zir.Inst.Extended,
12404 node: Ast.Node.Index,
12405 operands: []const Zir.Inst.Ref,
12406 ) !Zir.Inst.Ref {
12407 const astgen = gz.astgen;
12408 const gpa = astgen.gpa;
12409
12410 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12411 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
12412 try astgen.extra.ensureUnusedCapacity(
12413 gpa,
12414 @typeInfo(Zir.Inst.NodeMultiOp).Struct.fields.len + operands.len,
12415 );
12416
12417 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{
12418 .src_node = gz.nodeIndexToRelative(node),
12419 });
12420 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12421 astgen.instructions.appendAssumeCapacity(.{
12422 .tag = .extended,
12423 .data = .{ .extended = .{
12424 .opcode = opcode,
12425 .small = @intCast(operands.len),
12426 .operand = payload_index,
12427 } },
12428 });
12429 gz.instructions.appendAssumeCapacity(new_index);
12430 astgen.appendRefsAssumeCapacity(operands);
12431 return new_index.toRef();
12432 }
12433
12434 fn addExtendedMultiOpPayloadIndex(
12435 gz: *GenZir,
12436 opcode: Zir.Inst.Extended,
12437 payload_index: u32,
12438 trailing_len: usize,
12439 ) !Zir.Inst.Ref {
12440 const astgen = gz.astgen;
12441 const gpa = astgen.gpa;
12442
12443 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12444 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
12445 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12446 astgen.instructions.appendAssumeCapacity(.{
12447 .tag = .extended,
12448 .data = .{ .extended = .{
12449 .opcode = opcode,
12450 .small = @intCast(trailing_len),
12451 .operand = payload_index,
12452 } },
12453 });
12454 gz.instructions.appendAssumeCapacity(new_index);
12455 return new_index.toRef();
12456 }
12457
12458 fn addUnTok(
12459 gz: *GenZir,
12460 tag: Zir.Inst.Tag,
12461 operand: Zir.Inst.Ref,
12462 /// Absolute token index. This function does the conversion to Decl offset.
12463 abs_tok_index: Ast.TokenIndex,
12464 ) !Zir.Inst.Ref {
12465 assert(operand != .none);
12466 return gz.add(.{
12467 .tag = tag,
12468 .data = .{ .un_tok = .{
12469 .operand = operand,
12470 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
12471 } },
12472 });
12473 }
12474
12475 fn makeUnTok(
12476 gz: *GenZir,
12477 tag: Zir.Inst.Tag,
12478 operand: Zir.Inst.Ref,
12479 /// Absolute token index. This function does the conversion to Decl offset.
12480 abs_tok_index: Ast.TokenIndex,
12481 ) !Zir.Inst.Index {
12482 const astgen = gz.astgen;
12483 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12484 assert(operand != .none);
12485 try astgen.instructions.append(astgen.gpa, .{
12486 .tag = tag,
12487 .data = .{ .un_tok = .{
12488 .operand = operand,
12489 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
12490 } },
12491 });
12492 return new_index;
12493 }
12494
12495 fn addStrTok(
12496 gz: *GenZir,
12497 tag: Zir.Inst.Tag,
12498 str_index: Zir.NullTerminatedString,
12499 /// Absolute token index. This function does the conversion to Decl offset.
12500 abs_tok_index: Ast.TokenIndex,
12501 ) !Zir.Inst.Ref {
12502 return gz.add(.{
12503 .tag = tag,
12504 .data = .{ .str_tok = .{
12505 .start = str_index,
12506 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
12507 } },
12508 });
12509 }
12510
12511 fn addSaveErrRetIndex(
12512 gz: *GenZir,
12513 cond: union(enum) {
12514 always: void,
12515 if_of_error_type: Zir.Inst.Ref,
12516 },
12517 ) !Zir.Inst.Index {
12518 return gz.addAsIndex(.{
12519 .tag = .save_err_ret_index,
12520 .data = .{ .save_err_ret_index = .{
12521 .operand = switch (cond) {
12522 .if_of_error_type => |x| x,
12523 else => .none,
12524 },
12525 } },
12526 });
12527 }
12528
12529 const BranchTarget = union(enum) {
12530 ret,
12531 block: Zir.Inst.Index,
12532 };
12533
12534 fn addRestoreErrRetIndex(
12535 gz: *GenZir,
12536 bt: BranchTarget,
12537 cond: union(enum) {
12538 always: void,
12539 if_non_error: Zir.Inst.Ref,
12540 },
12541 src_node: Ast.Node.Index,
12542 ) !Zir.Inst.Index {
12543 switch (cond) {
12544 .always => return gz.addAsIndex(.{
12545 .tag = .restore_err_ret_index_unconditional,
12546 .data = .{ .un_node = .{
12547 .operand = switch (bt) {
12548 .ret => .none,
12549 .block => |b| b.toRef(),
12550 },
12551 .src_node = gz.nodeIndexToRelative(src_node),
12552 } },
12553 }),
12554 .if_non_error => |operand| switch (bt) {
12555 .ret => return gz.addAsIndex(.{
12556 .tag = .restore_err_ret_index_fn_entry,
12557 .data = .{ .un_node = .{
12558 .operand = operand,
12559 .src_node = gz.nodeIndexToRelative(src_node),
12560 } },
12561 }),
12562 .block => |block| return (try gz.addExtendedPayload(
12563 .restore_err_ret_index,
12564 Zir.Inst.RestoreErrRetIndex{
12565 .src_node = gz.nodeIndexToRelative(src_node),
12566 .block = block.toRef(),
12567 .operand = operand,
12568 },
12569 )).toIndex().?,
12570 },
12571 }
12572 }
12573
12574 fn addBreak(
12575 gz: *GenZir,
12576 tag: Zir.Inst.Tag,
12577 block_inst: Zir.Inst.Index,
12578 operand: Zir.Inst.Ref,
12579 ) !Zir.Inst.Index {
12580 const gpa = gz.astgen.gpa;
12581 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12582
12583 const new_index = try gz.makeBreak(tag, block_inst, operand);
12584 gz.instructions.appendAssumeCapacity(new_index);
12585 return new_index;
12586 }
12587
12588 fn makeBreak(
12589 gz: *GenZir,
12590 tag: Zir.Inst.Tag,
12591 block_inst: Zir.Inst.Index,
12592 operand: Zir.Inst.Ref,
12593 ) !Zir.Inst.Index {
12594 return gz.makeBreakCommon(tag, block_inst, operand, null);
12595 }
12596
12597 fn addBreakWithSrcNode(
12598 gz: *GenZir,
12599 tag: Zir.Inst.Tag,
12600 block_inst: Zir.Inst.Index,
12601 operand: Zir.Inst.Ref,
12602 operand_src_node: Ast.Node.Index,
12603 ) !Zir.Inst.Index {
12604 const gpa = gz.astgen.gpa;
12605 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12606
12607 const new_index = try gz.makeBreakWithSrcNode(tag, block_inst, operand, operand_src_node);
12608 gz.instructions.appendAssumeCapacity(new_index);
12609 return new_index;
12610 }
12611
12612 fn makeBreakWithSrcNode(
12613 gz: *GenZir,
12614 tag: Zir.Inst.Tag,
12615 block_inst: Zir.Inst.Index,
12616 operand: Zir.Inst.Ref,
12617 operand_src_node: Ast.Node.Index,
12618 ) !Zir.Inst.Index {
12619 return gz.makeBreakCommon(tag, block_inst, operand, operand_src_node);
12620 }
12621
12622 fn makeBreakCommon(
12623 gz: *GenZir,
12624 tag: Zir.Inst.Tag,
12625 block_inst: Zir.Inst.Index,
12626 operand: Zir.Inst.Ref,
12627 operand_src_node: ?Ast.Node.Index,
12628 ) !Zir.Inst.Index {
12629 const gpa = gz.astgen.gpa;
12630 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
12631 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Break).Struct.fields.len);
12632
12633 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12634 gz.astgen.instructions.appendAssumeCapacity(.{
12635 .tag = tag,
12636 .data = .{ .@"break" = .{
12637 .operand = operand,
12638 .payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Break{
12639 .operand_src_node = if (operand_src_node) |src_node|
12640 gz.nodeIndexToRelative(src_node)
12641 else
12642 Zir.Inst.Break.no_src_node,
12643 .block_inst = block_inst,
12644 }),
12645 } },
12646 });
12647 return new_index;
12648 }
12649
12650 fn addBin(
12651 gz: *GenZir,
12652 tag: Zir.Inst.Tag,
12653 lhs: Zir.Inst.Ref,
12654 rhs: Zir.Inst.Ref,
12655 ) !Zir.Inst.Ref {
12656 assert(lhs != .none);
12657 assert(rhs != .none);
12658 return gz.add(.{
12659 .tag = tag,
12660 .data = .{ .bin = .{
12661 .lhs = lhs,
12662 .rhs = rhs,
12663 } },
12664 });
12665 }
12666
12667 fn addDefer(gz: *GenZir, index: u32, len: u32) !void {
12668 _ = try gz.add(.{
12669 .tag = .@"defer",
12670 .data = .{ .@"defer" = .{
12671 .index = index,
12672 .len = len,
12673 } },
12674 });
12675 }
12676
12677 fn addDecl(
12678 gz: *GenZir,
12679 tag: Zir.Inst.Tag,
12680 decl_index: u32,
12681 src_node: Ast.Node.Index,
12682 ) !Zir.Inst.Ref {
12683 return gz.add(.{
12684 .tag = tag,
12685 .data = .{ .pl_node = .{
12686 .src_node = gz.nodeIndexToRelative(src_node),
12687 .payload_index = decl_index,
12688 } },
12689 });
12690 }
12691
12692 fn addNode(
12693 gz: *GenZir,
12694 tag: Zir.Inst.Tag,
12695 /// Absolute node index. This function does the conversion to offset from Decl.
12696 src_node: Ast.Node.Index,
12697 ) !Zir.Inst.Ref {
12698 return gz.add(.{
12699 .tag = tag,
12700 .data = .{ .node = gz.nodeIndexToRelative(src_node) },
12701 });
12702 }
12703
12704 fn addInstNode(
12705 gz: *GenZir,
12706 tag: Zir.Inst.Tag,
12707 inst: Zir.Inst.Index,
12708 /// Absolute node index. This function does the conversion to offset from Decl.
12709 src_node: Ast.Node.Index,
12710 ) !Zir.Inst.Ref {
12711 return gz.add(.{
12712 .tag = tag,
12713 .data = .{ .inst_node = .{
12714 .inst = inst,
12715 .src_node = gz.nodeIndexToRelative(src_node),
12716 } },
12717 });
12718 }
12719
12720 fn addNodeExtended(
12721 gz: *GenZir,
12722 opcode: Zir.Inst.Extended,
12723 /// Absolute node index. This function does the conversion to offset from Decl.
12724 src_node: Ast.Node.Index,
12725 ) !Zir.Inst.Ref {
12726 return gz.add(.{
12727 .tag = .extended,
12728 .data = .{ .extended = .{
12729 .opcode = opcode,
12730 .small = undefined,
12731 .operand = @bitCast(gz.nodeIndexToRelative(src_node)),
12732 } },
12733 });
12734 }
12735
12736 fn addAllocExtended(
12737 gz: *GenZir,
12738 args: struct {
12739 /// Absolute node index. This function does the conversion to offset from Decl.
12740 node: Ast.Node.Index,
12741 type_inst: Zir.Inst.Ref,
12742 align_inst: Zir.Inst.Ref,
12743 is_const: bool,
12744 is_comptime: bool,
12745 },
12746 ) !Zir.Inst.Ref {
12747 const astgen = gz.astgen;
12748 const gpa = astgen.gpa;
12749
12750 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12751 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
12752 try astgen.extra.ensureUnusedCapacity(
12753 gpa,
12754 @typeInfo(Zir.Inst.AllocExtended).Struct.fields.len +
12755 @intFromBool(args.type_inst != .none) +
12756 @intFromBool(args.align_inst != .none),
12757 );
12758 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{
12759 .src_node = gz.nodeIndexToRelative(args.node),
12760 });
12761 if (args.type_inst != .none) {
12762 astgen.extra.appendAssumeCapacity(@intFromEnum(args.type_inst));
12763 }
12764 if (args.align_inst != .none) {
12765 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst));
12766 }
12767
12768 const has_type: u4 = @intFromBool(args.type_inst != .none);
12769 const has_align: u4 = @intFromBool(args.align_inst != .none);
12770 const is_const: u4 = @intFromBool(args.is_const);
12771 const is_comptime: u4 = @intFromBool(args.is_comptime);
12772 const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3);
12773
12774 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12775 astgen.instructions.appendAssumeCapacity(.{
12776 .tag = .extended,
12777 .data = .{ .extended = .{
12778 .opcode = .alloc,
12779 .small = small,
12780 .operand = payload_index,
12781 } },
12782 });
12783 gz.instructions.appendAssumeCapacity(new_index);
12784 return new_index.toRef();
12785 }
12786
12787 fn addAsm(
12788 gz: *GenZir,
12789 args: struct {
12790 tag: Zir.Inst.Extended,
12791 /// Absolute node index. This function does the conversion to offset from Decl.
12792 node: Ast.Node.Index,
12793 asm_source: Zir.NullTerminatedString,
12794 output_type_bits: u32,
12795 is_volatile: bool,
12796 outputs: []const Zir.Inst.Asm.Output,
12797 inputs: []const Zir.Inst.Asm.Input,
12798 clobbers: []const u32,
12799 },
12800 ) !Zir.Inst.Ref {
12801 const astgen = gz.astgen;
12802 const gpa = astgen.gpa;
12803
12804 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12805 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
12806 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).Struct.fields.len +
12807 args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).Struct.fields.len +
12808 args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).Struct.fields.len +
12809 args.clobbers.len);
12810
12811 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{
12812 .src_node = gz.nodeIndexToRelative(args.node),
12813 .asm_source = args.asm_source,
12814 .output_type_bits = args.output_type_bits,
12815 });
12816 for (args.outputs) |output| {
12817 _ = gz.astgen.addExtraAssumeCapacity(output);
12818 }
12819 for (args.inputs) |input| {
12820 _ = gz.astgen.addExtraAssumeCapacity(input);
12821 }
12822 gz.astgen.extra.appendSliceAssumeCapacity(args.clobbers);
12823
12824 // * 0b00000000_000XXXXX - `outputs_len`.
12825 // * 0b000000XX_XXX00000 - `inputs_len`.
12826 // * 0b0XXXXX00_00000000 - `clobbers_len`.
12827 // * 0bX0000000_00000000 - is volatile
12828 const small: u16 = @as(u16, @intCast(args.outputs.len)) |
12829 @as(u16, @intCast(args.inputs.len << 5)) |
12830 @as(u16, @intCast(args.clobbers.len << 10)) |
12831 (@as(u16, @intFromBool(args.is_volatile)) << 15);
12832
12833 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12834 astgen.instructions.appendAssumeCapacity(.{
12835 .tag = .extended,
12836 .data = .{ .extended = .{
12837 .opcode = args.tag,
12838 .small = small,
12839 .operand = payload_index,
12840 } },
12841 });
12842 gz.instructions.appendAssumeCapacity(new_index);
12843 return new_index.toRef();
12844 }
12845
12846 /// Note that this returns a `Zir.Inst.Index` not a ref.
12847 /// Does *not* append the block instruction to the scope.
12848 /// Leaves the `payload_index` field undefined.
12849 fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
12850 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12851 const gpa = gz.astgen.gpa;
12852 try gz.astgen.instructions.append(gpa, .{
12853 .tag = tag,
12854 .data = .{ .pl_node = .{
12855 .src_node = gz.nodeIndexToRelative(node),
12856 .payload_index = undefined,
12857 } },
12858 });
12859 return new_index;
12860 }
12861
12862 /// Note that this returns a `Zir.Inst.Index` not a ref.
12863 /// Leaves the `payload_index` field undefined.
12864 fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
12865 const gpa = gz.astgen.gpa;
12866 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12867 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12868 try gz.astgen.instructions.append(gpa, .{
12869 .tag = tag,
12870 .data = .{ .pl_node = .{
12871 .src_node = gz.nodeIndexToRelative(node),
12872 .payload_index = undefined,
12873 } },
12874 });
12875 gz.instructions.appendAssumeCapacity(new_index);
12876 return new_index;
12877 }
12878
12879 fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
12880 src_node: Ast.Node.Index,
12881 fields_len: u32,
12882 decls_len: u32,
12883 backing_int_ref: Zir.Inst.Ref,
12884 backing_int_body_len: u32,
12885 layout: std.builtin.Type.ContainerLayout,
12886 known_non_opv: bool,
12887 known_comptime_only: bool,
12888 is_tuple: bool,
12889 any_comptime_fields: bool,
12890 any_default_inits: bool,
12891 any_aligned_fields: bool,
12892 fields_hash: std.zig.SrcHash,
12893 }) !void {
12894 const astgen = gz.astgen;
12895 const gpa = astgen.gpa;
12896
12897 // Node 0 is valid for the root `struct_decl` of a file!
12898 assert(args.src_node != 0 or gz.parent.tag == .top);
12899
12900 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
12901
12902 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).Struct.fields.len + 4);
12903 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{
12904 .fields_hash_0 = fields_hash_arr[0],
12905 .fields_hash_1 = fields_hash_arr[1],
12906 .fields_hash_2 = fields_hash_arr[2],
12907 .fields_hash_3 = fields_hash_arr[3],
12908 .src_node = gz.nodeIndexToRelative(args.src_node),
12909 });
12910
12911 if (args.fields_len != 0) {
12912 astgen.extra.appendAssumeCapacity(args.fields_len);
12913 }
12914 if (args.decls_len != 0) {
12915 astgen.extra.appendAssumeCapacity(args.decls_len);
12916 }
12917 if (args.backing_int_ref != .none) {
12918 astgen.extra.appendAssumeCapacity(args.backing_int_body_len);
12919 if (args.backing_int_body_len == 0) {
12920 astgen.extra.appendAssumeCapacity(@intFromEnum(args.backing_int_ref));
12921 }
12922 }
12923 astgen.instructions.set(@intFromEnum(inst), .{
12924 .tag = .extended,
12925 .data = .{ .extended = .{
12926 .opcode = .struct_decl,
12927 .small = @bitCast(Zir.Inst.StructDecl.Small{
12928 .has_fields_len = args.fields_len != 0,
12929 .has_decls_len = args.decls_len != 0,
12930 .has_backing_int = args.backing_int_ref != .none,
12931 .known_non_opv = args.known_non_opv,
12932 .known_comptime_only = args.known_comptime_only,
12933 .is_tuple = args.is_tuple,
12934 .name_strategy = gz.anon_name_strategy,
12935 .layout = args.layout,
12936 .any_comptime_fields = args.any_comptime_fields,
12937 .any_default_inits = args.any_default_inits,
12938 .any_aligned_fields = args.any_aligned_fields,
12939 }),
12940 .operand = payload_index,
12941 } },
12942 });
12943 }
12944
12945 fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
12946 src_node: Ast.Node.Index,
12947 tag_type: Zir.Inst.Ref,
12948 body_len: u32,
12949 fields_len: u32,
12950 decls_len: u32,
12951 layout: std.builtin.Type.ContainerLayout,
12952 auto_enum_tag: bool,
12953 any_aligned_fields: bool,
12954 fields_hash: std.zig.SrcHash,
12955 }) !void {
12956 const astgen = gz.astgen;
12957 const gpa = astgen.gpa;
12958
12959 assert(args.src_node != 0);
12960
12961 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
12962
12963 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).Struct.fields.len + 4);
12964 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{
12965 .fields_hash_0 = fields_hash_arr[0],
12966 .fields_hash_1 = fields_hash_arr[1],
12967 .fields_hash_2 = fields_hash_arr[2],
12968 .fields_hash_3 = fields_hash_arr[3],
12969 .src_node = gz.nodeIndexToRelative(args.src_node),
12970 });
12971
12972 if (args.tag_type != .none) {
12973 astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type));
12974 }
12975 if (args.body_len != 0) {
12976 astgen.extra.appendAssumeCapacity(args.body_len);
12977 }
12978 if (args.fields_len != 0) {
12979 astgen.extra.appendAssumeCapacity(args.fields_len);
12980 }
12981 if (args.decls_len != 0) {
12982 astgen.extra.appendAssumeCapacity(args.decls_len);
12983 }
12984 astgen.instructions.set(@intFromEnum(inst), .{
12985 .tag = .extended,
12986 .data = .{ .extended = .{
12987 .opcode = .union_decl,
12988 .small = @bitCast(Zir.Inst.UnionDecl.Small{
12989 .has_tag_type = args.tag_type != .none,
12990 .has_body_len = args.body_len != 0,
12991 .has_fields_len = args.fields_len != 0,
12992 .has_decls_len = args.decls_len != 0,
12993 .name_strategy = gz.anon_name_strategy,
12994 .layout = args.layout,
12995 .auto_enum_tag = args.auto_enum_tag,
12996 .any_aligned_fields = args.any_aligned_fields,
12997 }),
12998 .operand = payload_index,
12999 } },
13000 });
13001 }
13002
13003 fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
13004 src_node: Ast.Node.Index,
13005 tag_type: Zir.Inst.Ref,
13006 body_len: u32,
13007 fields_len: u32,
13008 decls_len: u32,
13009 nonexhaustive: bool,
13010 fields_hash: std.zig.SrcHash,
13011 }) !void {
13012 const astgen = gz.astgen;
13013 const gpa = astgen.gpa;
13014
13015 assert(args.src_node != 0);
13016
13017 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
13018
13019 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).Struct.fields.len + 4);
13020 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{
13021 .fields_hash_0 = fields_hash_arr[0],
13022 .fields_hash_1 = fields_hash_arr[1],
13023 .fields_hash_2 = fields_hash_arr[2],
13024 .fields_hash_3 = fields_hash_arr[3],
13025 .src_node = gz.nodeIndexToRelative(args.src_node),
13026 });
13027
13028 if (args.tag_type != .none) {
13029 astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type));
13030 }
13031 if (args.body_len != 0) {
13032 astgen.extra.appendAssumeCapacity(args.body_len);
13033 }
13034 if (args.fields_len != 0) {
13035 astgen.extra.appendAssumeCapacity(args.fields_len);
13036 }
13037 if (args.decls_len != 0) {
13038 astgen.extra.appendAssumeCapacity(args.decls_len);
13039 }
13040 astgen.instructions.set(@intFromEnum(inst), .{
13041 .tag = .extended,
13042 .data = .{ .extended = .{
13043 .opcode = .enum_decl,
13044 .small = @bitCast(Zir.Inst.EnumDecl.Small{
13045 .has_tag_type = args.tag_type != .none,
13046 .has_body_len = args.body_len != 0,
13047 .has_fields_len = args.fields_len != 0,
13048 .has_decls_len = args.decls_len != 0,
13049 .name_strategy = gz.anon_name_strategy,
13050 .nonexhaustive = args.nonexhaustive,
13051 }),
13052 .operand = payload_index,
13053 } },
13054 });
13055 }
13056
13057 fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
13058 src_node: Ast.Node.Index,
13059 decls_len: u32,
13060 }) !void {
13061 const astgen = gz.astgen;
13062 const gpa = astgen.gpa;
13063
13064 assert(args.src_node != 0);
13065
13066 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).Struct.fields.len + 1);
13067 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{
13068 .src_node = gz.nodeIndexToRelative(args.src_node),
13069 });
13070
13071 if (args.decls_len != 0) {
13072 astgen.extra.appendAssumeCapacity(args.decls_len);
13073 }
13074 astgen.instructions.set(@intFromEnum(inst), .{
13075 .tag = .extended,
13076 .data = .{ .extended = .{
13077 .opcode = .opaque_decl,
13078 .small = @bitCast(Zir.Inst.OpaqueDecl.Small{
13079 .has_decls_len = args.decls_len != 0,
13080 .name_strategy = gz.anon_name_strategy,
13081 }),
13082 .operand = payload_index,
13083 } },
13084 });
13085 }
13086
13087 fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
13088 return (try gz.addAsIndex(inst)).toRef();
13089 }
13090
13091 fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
13092 const gpa = gz.astgen.gpa;
13093 try gz.instructions.ensureUnusedCapacity(gpa, 1);
13094 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
13095
13096 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
13097 gz.astgen.instructions.appendAssumeCapacity(inst);
13098 gz.instructions.appendAssumeCapacity(new_index);
13099 return new_index;
13100 }
13101
13102 fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index {
13103 const gpa = gz.astgen.gpa;
13104 try gz.instructions.ensureUnusedCapacity(gpa, 1);
13105 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
13106
13107 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
13108 gz.astgen.instructions.len += 1;
13109 gz.instructions.appendAssumeCapacity(new_index);
13110 return new_index;
13111 }
13112
13113 fn addRet(gz: *GenZir, ri: ResultInfo, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
13114 switch (ri.rl) {
13115 .ptr => |ptr_res| _ = try gz.addUnNode(.ret_load, ptr_res.inst, node),
13116 .coerced_ty => _ = try gz.addUnNode(.ret_node, operand, node),
13117 else => unreachable,
13118 }
13119 }
13120
13121 fn addNamespaceCaptures(gz: *GenZir, namespace: *Scope.Namespace) !void {
13122 if (namespace.captures.count() > 0) {
13123 try gz.instructions.ensureUnusedCapacity(gz.astgen.gpa, namespace.captures.count());
13124 for (namespace.captures.values()) |capture| {
13125 gz.instructions.appendAssumeCapacity(capture);
13126 }
13127 }
13128 }
13129
13130 fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: Zir.NullTerminatedString, inst: Zir.Inst.Ref) !void {
13131 if (gz.is_comptime) return;
13132
13133 _ = try gz.add(.{ .tag = tag, .data = .{
13134 .str_op = .{
13135 .str = name,
13136 .operand = inst,
13137 },
13138 } });
13139 }
13140};
13141
13142/// This can only be for short-lived references; the memory becomes invalidated
13143/// when another string is added.
13144fn nullTerminatedString(astgen: AstGen, index: Zir.NullTerminatedString) [*:0]const u8 {
13145 return @ptrCast(astgen.string_bytes.items[@intFromEnum(index)..]);
13146}
13147
13148/// Local variables shadowing detection, including function parameters.
13149fn detectLocalShadowing(
13150 astgen: *AstGen,
13151 scope: *Scope,
13152 ident_name: Zir.NullTerminatedString,
13153 name_token: Ast.TokenIndex,
13154 token_bytes: []const u8,
13155 id_cat: Scope.IdCat,
13156) !void {
13157 const gpa = astgen.gpa;
13158 if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
13159 return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
13160 token_bytes,
13161 }, &[_]u32{
13162 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
13163 token_bytes,
13164 }),
13165 });
13166 }
13167
13168 var s = scope;
13169 var outer_scope = false;
13170 while (true) switch (s.tag) {
13171 .local_val => {
13172 const local_val = s.cast(Scope.LocalVal).?;
13173 if (local_val.name == ident_name) {
13174 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
13175 const name = try gpa.dupe(u8, name_slice);
13176 defer gpa.free(name);
13177 if (outer_scope) {
13178 return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
13179 @tagName(id_cat), name, @tagName(local_val.id_cat),
13180 }, &[_]u32{
13181 try astgen.errNoteTok(
13182 local_val.token_src,
13183 "previous declaration here",
13184 .{},
13185 ),
13186 });
13187 }
13188 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
13189 @tagName(local_val.id_cat), name,
13190 }, &[_]u32{
13191 try astgen.errNoteTok(
13192 local_val.token_src,
13193 "previous declaration here",
13194 .{},
13195 ),
13196 });
13197 }
13198 s = local_val.parent;
13199 },
13200 .local_ptr => {
13201 const local_ptr = s.cast(Scope.LocalPtr).?;
13202 if (local_ptr.name == ident_name) {
13203 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
13204 const name = try gpa.dupe(u8, name_slice);
13205 defer gpa.free(name);
13206 if (outer_scope) {
13207 return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
13208 @tagName(id_cat), name, @tagName(local_ptr.id_cat),
13209 }, &[_]u32{
13210 try astgen.errNoteTok(
13211 local_ptr.token_src,
13212 "previous declaration here",
13213 .{},
13214 ),
13215 });
13216 }
13217 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
13218 @tagName(local_ptr.id_cat), name,
13219 }, &[_]u32{
13220 try astgen.errNoteTok(
13221 local_ptr.token_src,
13222 "previous declaration here",
13223 .{},
13224 ),
13225 });
13226 }
13227 s = local_ptr.parent;
13228 },
13229 .namespace, .enum_namespace => {
13230 outer_scope = true;
13231 const ns = s.cast(Scope.Namespace).?;
13232 const decl_node = ns.decls.get(ident_name) orelse {
13233 s = ns.parent;
13234 continue;
13235 };
13236 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
13237 const name = try gpa.dupe(u8, name_slice);
13238 defer gpa.free(name);
13239 return astgen.failTokNotes(name_token, "{s} shadows declaration of '{s}'", .{
13240 @tagName(id_cat), name,
13241 }, &[_]u32{
13242 try astgen.errNoteNode(decl_node, "declared here", .{}),
13243 });
13244 },
13245 .gen_zir => {
13246 s = s.cast(GenZir).?.parent;
13247 outer_scope = true;
13248 },
13249 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
13250 .top => break,
13251 };
13252}
13253
13254const LineColumn = struct { u32, u32 };
13255
13256/// Advances the source cursor to the main token of `node` if not in comptime scope.
13257/// Usually paired with `emitDbgStmt`.
13258fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) LineColumn {
13259 if (gz.is_comptime) return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column };
13260
13261 const tree = gz.astgen.tree;
13262 const token_starts = tree.tokens.items(.start);
13263 const main_tokens = tree.nodes.items(.main_token);
13264 const node_start = token_starts[main_tokens[node]];
13265 gz.astgen.advanceSourceCursor(node_start);
13266
13267 return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column };
13268}
13269
13270/// Advances the source cursor to the beginning of `node`.
13271fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void {
13272 const tree = astgen.tree;
13273 const token_starts = tree.tokens.items(.start);
13274 const node_start = token_starts[tree.firstToken(node)];
13275 astgen.advanceSourceCursor(node_start);
13276}
13277
13278/// Advances the source cursor to an absolute byte offset `end` in the file.
13279fn advanceSourceCursor(astgen: *AstGen, end: usize) void {
13280 const source = astgen.tree.source;
13281 var i = astgen.source_offset;
13282 var line = astgen.source_line;
13283 var column = astgen.source_column;
13284 assert(i <= end);
13285 while (i < end) : (i += 1) {
13286 if (source[i] == '\n') {
13287 line += 1;
13288 column = 0;
13289 } else {
13290 column += 1;
13291 }
13292 }
13293 astgen.source_offset = i;
13294 astgen.source_line = line;
13295 astgen.source_column = column;
13296}
13297
13298fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !u32 {
13299 const gpa = astgen.gpa;
13300 const tree = astgen.tree;
13301 const node_tags = tree.nodes.items(.tag);
13302 const main_tokens = tree.nodes.items(.main_token);
13303 const token_tags = tree.tokens.items(.tag);
13304 var decl_count: u32 = 0;
13305 for (members) |member_node| {
13306 const name_token = switch (node_tags[member_node]) {
13307 .global_var_decl,
13308 .local_var_decl,
13309 .simple_var_decl,
13310 .aligned_var_decl,
13311 => blk: {
13312 decl_count += 1;
13313 break :blk main_tokens[member_node] + 1;
13314 },
13315
13316 .fn_proto_simple,
13317 .fn_proto_multi,
13318 .fn_proto_one,
13319 .fn_proto,
13320 .fn_decl,
13321 => blk: {
13322 decl_count += 1;
13323 const ident = main_tokens[member_node] + 1;
13324 if (token_tags[ident] != .identifier) {
13325 switch (astgen.failNode(member_node, "missing function name", .{})) {
13326 error.AnalysisFail => continue,
13327 error.OutOfMemory => return error.OutOfMemory,
13328 }
13329 }
13330 break :blk ident;
13331 },
13332
13333 .@"comptime", .@"usingnamespace", .test_decl => {
13334 decl_count += 1;
13335 continue;
13336 },
13337
13338 else => continue,
13339 };
13340
13341 const token_bytes = astgen.tree.tokenSlice(name_token);
13342 if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
13343 switch (astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
13344 token_bytes,
13345 }, &[_]u32{
13346 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
13347 token_bytes,
13348 }),
13349 })) {
13350 error.AnalysisFail => continue,
13351 error.OutOfMemory => return error.OutOfMemory,
13352 }
13353 }
13354
13355 const name_str_index = try astgen.identAsString(name_token);
13356 const gop = try namespace.decls.getOrPut(gpa, name_str_index);
13357 if (gop.found_existing) {
13358 const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(name_str_index)));
13359 defer gpa.free(name);
13360 switch (astgen.failNodeNotes(member_node, "redeclaration of '{s}'", .{
13361 name,
13362 }, &[_]u32{
13363 try astgen.errNoteNode(gop.value_ptr.*, "other declaration here", .{}),
13364 })) {
13365 error.AnalysisFail => continue,
13366 error.OutOfMemory => return error.OutOfMemory,
13367 }
13368 }
13369
13370 var s = namespace.parent;
13371 while (true) switch (s.tag) {
13372 .local_val => {
13373 const local_val = s.cast(Scope.LocalVal).?;
13374 if (local_val.name == name_str_index) {
13375 return astgen.failTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
13376 token_bytes, @tagName(local_val.id_cat),
13377 }, &[_]u32{
13378 try astgen.errNoteTok(
13379 local_val.token_src,
13380 "previous declaration here",
13381 .{},
13382 ),
13383 });
13384 }
13385 s = local_val.parent;
13386 },
13387 .local_ptr => {
13388 const local_ptr = s.cast(Scope.LocalPtr).?;
13389 if (local_ptr.name == name_str_index) {
13390 return astgen.failTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
13391 token_bytes, @tagName(local_ptr.id_cat),
13392 }, &[_]u32{
13393 try astgen.errNoteTok(
13394 local_ptr.token_src,
13395 "previous declaration here",
13396 .{},
13397 ),
13398 });
13399 }
13400 s = local_ptr.parent;
13401 },
13402 .namespace, .enum_namespace => s = s.cast(Scope.Namespace).?.parent,
13403 .gen_zir => s = s.cast(GenZir).?.parent,
13404 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
13405 .top => break,
13406 };
13407 gop.value_ptr.* = member_node;
13408 }
13409 return decl_count;
13410}
13411
13412fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {
13413 const inst = ref.toIndex() orelse return false;
13414 const zir_tags = astgen.instructions.items(.tag);
13415 return switch (zir_tags[@intFromEnum(inst)]) {
13416 .alloc_inferred,
13417 .alloc_inferred_mut,
13418 .alloc_inferred_comptime,
13419 .alloc_inferred_comptime_mut,
13420 => true,
13421
13422 .extended => {
13423 const zir_data = astgen.instructions.items(.data);
13424 if (zir_data[@intFromEnum(inst)].extended.opcode != .alloc) return false;
13425 const small: Zir.Inst.AllocExtended.Small = @bitCast(zir_data[@intFromEnum(inst)].extended.small);
13426 return !small.has_type;
13427 },
13428
13429 else => false,
13430 };
13431}
13432
13433/// Assumes capacity for body has already been added. Needed capacity taking into
13434/// account fixups can be found with `countBodyLenAfterFixups`.
13435fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void {
13436 return appendBodyWithFixupsArrayList(astgen, &astgen.extra, body);
13437}
13438
13439fn appendBodyWithFixupsArrayList(
13440 astgen: *AstGen,
13441 list: *std.ArrayListUnmanaged(u32),
13442 body: []const Zir.Inst.Index,
13443) void {
13444 for (body) |body_inst| {
13445 appendPossiblyRefdBodyInst(astgen, list, body_inst);
13446 }
13447}
13448
13449fn appendPossiblyRefdBodyInst(
13450 astgen: *AstGen,
13451 list: *std.ArrayListUnmanaged(u32),
13452 body_inst: Zir.Inst.Index,
13453) void {
13454 list.appendAssumeCapacity(@intFromEnum(body_inst));
13455 const kv = astgen.ref_table.fetchRemove(body_inst) orelse return;
13456 const ref_inst = kv.value;
13457 return appendPossiblyRefdBodyInst(astgen, list, ref_inst);
13458}
13459
13460fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
13461 var count = body.len;
13462 for (body) |body_inst| {
13463 var check_inst = body_inst;
13464 while (astgen.ref_table.get(check_inst)) |ref_inst| {
13465 count += 1;
13466 check_inst = ref_inst;
13467 }
13468 }
13469 return @intCast(count);
13470}
13471
13472fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void {
13473 if (gz.is_comptime) return;
13474 if (gz.instructions.items.len > 0) {
13475 const astgen = gz.astgen;
13476 const last = gz.instructions.items[gz.instructions.items.len - 1];
13477 if (astgen.instructions.items(.tag)[@intFromEnum(last)] == .dbg_stmt) {
13478 astgen.instructions.items(.data)[@intFromEnum(last)].dbg_stmt = .{
13479 .line = lc[0],
13480 .column = lc[1],
13481 };
13482 return;
13483 }
13484 }
13485
13486 _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
13487 .dbg_stmt = .{
13488 .line = lc[0],
13489 .column = lc[1],
13490 },
13491 } });
13492}
13493
13494/// In some cases, Sema expects us to generate a `dbg_stmt` at the instruction
13495/// *index* directly preceding the next instruction (e.g. if a call is %10, it
13496/// expects a dbg_stmt at %9). TODO: this logic may allow redundant dbg_stmt
13497/// instructions; fix up Sema so we don't need it!
13498fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void {
13499 const astgen = gz.astgen;
13500 if (gz.instructions.items.len > 0 and
13501 @intFromEnum(gz.instructions.items[gz.instructions.items.len - 1]) == astgen.instructions.len - 1)
13502 {
13503 const last = astgen.instructions.len - 1;
13504 if (astgen.instructions.items(.tag)[last] == .dbg_stmt) {
13505 astgen.instructions.items(.data)[last].dbg_stmt = .{
13506 .line = lc[0],
13507 .column = lc[1],
13508 };
13509 return;
13510 }
13511 }
13512
13513 _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
13514 .dbg_stmt = .{
13515 .line = lc[0],
13516 .column = lc[1],
13517 },
13518 } });
13519}
13520
13521fn lowerAstErrors(astgen: *AstGen) !void {
13522 const tree = astgen.tree;
13523 assert(tree.errors.len > 0);
13524
13525 const gpa = astgen.gpa;
13526 const parse_err = tree.errors[0];
13527
13528 var msg: std.ArrayListUnmanaged(u8) = .{};
13529 defer msg.deinit(gpa);
13530
13531 const token_starts = tree.tokens.items(.start);
13532 const token_tags = tree.tokens.items(.tag);
13533
13534 var notes: std.ArrayListUnmanaged(u32) = .{};
13535 defer notes.deinit(gpa);
13536
13537 if (token_tags[parse_err.token + @intFromBool(parse_err.token_is_prev)] == .invalid) {
13538 const tok = parse_err.token + @intFromBool(parse_err.token_is_prev);
13539 const bad_off: u32 = @intCast(tree.tokenSlice(parse_err.token + @intFromBool(parse_err.token_is_prev)).len);
13540 const byte_abs = token_starts[parse_err.token + @intFromBool(parse_err.token_is_prev)] + bad_off;
13541 try notes.append(gpa, try astgen.errNoteTokOff(tok, bad_off, "invalid byte: '{'}'", .{
13542 std.zig.fmtEscapes(tree.source[byte_abs..][0..1]),
13543 }));
13544 }
13545
13546 for (tree.errors[1..]) |note| {
13547 if (!note.is_note) break;
13548
13549 msg.clearRetainingCapacity();
13550 try tree.renderError(note, msg.writer(gpa));
13551 try notes.append(gpa, try astgen.errNoteTok(note.token, "{s}", .{msg.items}));
13552 }
13553
13554 const extra_offset = tree.errorOffset(parse_err);
13555 msg.clearRetainingCapacity();
13556 try tree.renderError(parse_err, msg.writer(gpa));
13557 try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
13558}
13559
13560const DeclarationName = union(enum) {
13561 named: Ast.TokenIndex,
13562 named_test: Ast.TokenIndex,
13563 unnamed_test,
13564 decltest: Zir.NullTerminatedString,
13565 @"comptime",
13566 @"usingnamespace",
13567};
13568
13569/// Sets all extra data for a `declaration` instruction.
13570/// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`.
13571fn setDeclaration(
13572 decl_inst: Zir.Inst.Index,
13573 src_hash: std.zig.SrcHash,
13574 name: DeclarationName,
13575 line_offset: u32,
13576 is_pub: bool,
13577 is_export: bool,
13578 doc_comment: Zir.NullTerminatedString,
13579 value_gz: *GenZir,
13580 /// May be `null` if all these blocks would be empty.
13581 /// If `null`, then `value_gz` must have nothing stacked on it.
13582 extra_gzs: ?struct {
13583 /// Must be stacked on `value_gz`.
13584 align_gz: *GenZir,
13585 /// Must be stacked on `align_gz`.
13586 linksection_gz: *GenZir,
13587 /// Must be stacked on `linksection_gz`, and have nothing stacked on it.
13588 addrspace_gz: *GenZir,
13589 },
13590) !void {
13591 const astgen = value_gz.astgen;
13592 const gpa = astgen.gpa;
13593
13594 const empty_body: []Zir.Inst.Index = &.{};
13595 const value_body, const align_body, const linksection_body, const addrspace_body = if (extra_gzs) |e| .{
13596 value_gz.instructionsSliceUpto(e.align_gz),
13597 e.align_gz.instructionsSliceUpto(e.linksection_gz),
13598 e.linksection_gz.instructionsSliceUpto(e.addrspace_gz),
13599 e.addrspace_gz.instructionsSlice(),
13600 } else .{ value_gz.instructionsSlice(), empty_body, empty_body, empty_body };
13601
13602 const value_len = astgen.countBodyLenAfterFixups(value_body);
13603 const align_len = astgen.countBodyLenAfterFixups(align_body);
13604 const linksection_len = astgen.countBodyLenAfterFixups(linksection_body);
13605 const addrspace_len = astgen.countBodyLenAfterFixups(addrspace_body);
13606
13607 const true_doc_comment: Zir.NullTerminatedString = switch (name) {
13608 .decltest => |test_name| test_name,
13609 else => doc_comment,
13610 };
13611
13612 const src_hash_arr: [4]u32 = @bitCast(src_hash);
13613
13614 const extra: Zir.Inst.Declaration = .{
13615 .src_hash_0 = src_hash_arr[0],
13616 .src_hash_1 = src_hash_arr[1],
13617 .src_hash_2 = src_hash_arr[2],
13618 .src_hash_3 = src_hash_arr[3],
13619 .name = switch (name) {
13620 .named => |tok| @enumFromInt(@intFromEnum(try astgen.identAsString(tok))),
13621 .named_test => |tok| @enumFromInt(@intFromEnum(try astgen.testNameString(tok))),
13622 .unnamed_test => .unnamed_test,
13623 .decltest => .decltest,
13624 .@"comptime" => .@"comptime",
13625 .@"usingnamespace" => .@"usingnamespace",
13626 },
13627 .line_offset = line_offset,
13628 .flags = .{
13629 .value_body_len = @intCast(value_len),
13630 .is_pub = is_pub,
13631 .is_export = is_export,
13632 .has_doc_comment = true_doc_comment != .empty,
13633 .has_align_linksection_addrspace = align_len != 0 or linksection_len != 0 or addrspace_len != 0,
13634 },
13635 };
13636 astgen.instructions.items(.data)[@intFromEnum(decl_inst)].pl_node.payload_index = try astgen.addExtra(extra);
13637 if (extra.flags.has_doc_comment) {
13638 try astgen.extra.append(gpa, @intFromEnum(true_doc_comment));
13639 }
13640 if (extra.flags.has_align_linksection_addrspace) {
13641 try astgen.extra.appendSlice(gpa, &.{
13642 align_len,
13643 linksection_len,
13644 addrspace_len,
13645 });
13646 }
13647 try astgen.extra.ensureUnusedCapacity(gpa, value_len + align_len + linksection_len + addrspace_len);
13648 astgen.appendBodyWithFixups(value_body);
13649 if (extra.flags.has_align_linksection_addrspace) {
13650 astgen.appendBodyWithFixups(align_body);
13651 astgen.appendBodyWithFixups(linksection_body);
13652 astgen.appendBodyWithFixups(addrspace_body);
13653 }
13654
13655 if (extra_gzs) |e| {
13656 e.addrspace_gz.unstack();
13657 e.linksection_gz.unstack();
13658 e.align_gz.unstack();
13659 }
13660 value_gz.unstack();
13661}
lib/std/zig/ErrorBundle.zig+84
...@@ -459,6 +459,90 @@ pub const Wip = struct {...@@ -459,6 +459,90 @@ pub const Wip = struct {
459 return @intCast(wip.extra.items.len - notes_len);459 return @intCast(wip.extra.items.len - notes_len);
460 }460 }
461461
462 pub fn addZirErrorMessages(
463 eb: *ErrorBundle.Wip,
464 zir: std.zig.Zir,
465 tree: std.zig.Ast,
466 source: [:0]const u8,
467 src_path: []const u8,
468 ) !void {
469 const Zir = std.zig.Zir;
470 const payload_index = zir.extra[@intFromEnum(Zir.ExtraIndex.compile_errors)];
471 assert(payload_index != 0);
472
473 const header = zir.extraData(Zir.Inst.CompileErrors, payload_index);
474 const items_len = header.data.items_len;
475 var extra_index = header.end;
476 for (0..items_len) |_| {
477 const item = zir.extraData(Zir.Inst.CompileErrors.Item, extra_index);
478 extra_index = item.end;
479 const err_span = blk: {
480 if (item.data.node != 0) {
481 break :blk tree.nodeToSpan(item.data.node);
482 }
483 const token_starts = tree.tokens.items(.start);
484 const start = token_starts[item.data.token] + item.data.byte_offset;
485 const end = start + @as(u32, @intCast(tree.tokenSlice(item.data.token).len)) - item.data.byte_offset;
486 break :blk std.zig.Ast.Span{ .start = start, .end = end, .main = start };
487 };
488 const err_loc = std.zig.findLineColumn(source, err_span.main);
489
490 {
491 const msg = zir.nullTerminatedString(item.data.msg);
492 try eb.addRootErrorMessage(.{
493 .msg = try eb.addString(msg),
494 .src_loc = try eb.addSourceLocation(.{
495 .src_path = try eb.addString(src_path),
496 .span_start = err_span.start,
497 .span_main = err_span.main,
498 .span_end = err_span.end,
499 .line = @intCast(err_loc.line),
500 .column = @intCast(err_loc.column),
501 .source_line = try eb.addString(err_loc.source_line),
502 }),
503 .notes_len = item.data.notesLen(zir),
504 });
505 }
506
507 if (item.data.notes != 0) {
508 const notes_start = try eb.reserveNotes(item.data.notes);
509 const block = zir.extraData(Zir.Inst.Block, item.data.notes);
510 const body = zir.extra[block.end..][0..block.data.body_len];
511 for (notes_start.., body) |note_i, body_elem| {
512 const note_item = zir.extraData(Zir.Inst.CompileErrors.Item, body_elem);
513 const msg = zir.nullTerminatedString(note_item.data.msg);
514 const span = blk: {
515 if (note_item.data.node != 0) {
516 break :blk tree.nodeToSpan(note_item.data.node);
517 }
518 const token_starts = tree.tokens.items(.start);
519 const start = token_starts[note_item.data.token] + note_item.data.byte_offset;
520 const end = start + @as(u32, @intCast(tree.tokenSlice(note_item.data.token).len)) - item.data.byte_offset;
521 break :blk std.zig.Ast.Span{ .start = start, .end = end, .main = start };
522 };
523 const loc = std.zig.findLineColumn(source, span.main);
524
525 eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{
526 .msg = try eb.addString(msg),
527 .src_loc = try eb.addSourceLocation(.{
528 .src_path = try eb.addString(src_path),
529 .span_start = span.start,
530 .span_main = span.main,
531 .span_end = span.end,
532 .line = @intCast(loc.line),
533 .column = @intCast(loc.column),
534 .source_line = if (loc.eql(err_loc))
535 0
536 else
537 try eb.addString(loc.source_line),
538 }),
539 .notes_len = 0, // TODO rework this function to be recursive
540 }));
541 }
542 }
543 }
544 }
545
462 fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex {546 fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex {
463 const other_msg = other.getErrorMessage(msg_index);547 const other_msg = other.getErrorMessage(msg_index);
464 const src_loc = try wip.addOtherSourceLocation(other, other_msg.src_loc);548 const src_loc = try wip.addOtherSourceLocation(other, other_msg.src_loc);
lib/std/zig/Zir.zig created+4090
...@@ -0,0 +1,4090 @@
1//! Zig Intermediate Representation. Astgen.zig converts AST nodes to these
2//! untyped IR instructions. Next, Sema.zig processes these into AIR.
3//! The minimum amount of information needed to represent a list of ZIR instructions.
4//! Once this structure is completed, it can be used to generate AIR, followed by
5//! machine code, without any memory access into the AST tree token list, node list,
6//! or source bytes. Exceptions include:
7//! * Compile errors, which may need to reach into these data structures to
8//! create a useful report.
9//! * In the future, possibly inline assembly, which needs to get parsed and
10//! handled by the codegen backend, and errors reported there. However for now,
11//! inline assembly is not an exception.
12
13const std = @import("std");
14const builtin = @import("builtin");
15const mem = std.mem;
16const Allocator = std.mem.Allocator;
17const assert = std.debug.assert;
18const BigIntConst = std.math.big.int.Const;
19const BigIntMutable = std.math.big.int.Mutable;
20const Ast = std.zig.Ast;
21
22const Zir = @This();
23const LazySrcLoc = std.zig.LazySrcLoc;
24
25instructions: std.MultiArrayList(Inst).Slice,
26/// In order to store references to strings in fewer bytes, we copy all
27/// string bytes into here. String bytes can be null. It is up to whomever
28/// is referencing the data here whether they want to store both index and length,
29/// thus allowing null bytes, or store only index, and use null-termination. The
30/// `string_bytes` array is agnostic to either usage.
31/// Index 0 is reserved for special cases.
32string_bytes: []u8,
33/// The meaning of this data is determined by `Inst.Tag` value.
34/// The first few indexes are reserved. See `ExtraIndex` for the values.
35extra: []u32,
36
37/// The data stored at byte offset 0 when ZIR is stored in a file.
38pub const Header = extern struct {
39 instructions_len: u32,
40 string_bytes_len: u32,
41 extra_len: u32,
42 /// We could leave this as padding, however it triggers a Valgrind warning because
43 /// we read and write undefined bytes to the file system. This is harmless, but
44 /// it's essentially free to have a zero field here and makes the warning go away,
45 /// making it more likely that following Valgrind warnings will be taken seriously.
46 unused: u32 = 0,
47 stat_inode: std.fs.File.INode,
48 stat_size: u64,
49 stat_mtime: i128,
50};
51
52pub const ExtraIndex = enum(u32) {
53 /// If this is 0, no compile errors. Otherwise there is a `CompileErrors`
54 /// payload at this index.
55 compile_errors,
56 /// If this is 0, this file contains no imports. Otherwise there is a `Imports`
57 /// payload at this index.
58 imports,
59
60 _,
61};
62
63fn ExtraData(comptime T: type) type {
64 return struct { data: T, end: usize };
65}
66
67/// Returns the requested data, as well as the new index which is at the start of the
68/// trailers for the object.
69pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) {
70 const fields = @typeInfo(T).Struct.fields;
71 var i: usize = index;
72 var result: T = undefined;
73 inline for (fields) |field| {
74 @field(result, field.name) = switch (field.type) {
75 u32 => code.extra[i],
76
77 Inst.Ref,
78 Inst.Index,
79 Inst.Declaration.Name,
80 NullTerminatedString,
81 => @enumFromInt(code.extra[i]),
82
83 i32,
84 Inst.Call.Flags,
85 Inst.BuiltinCall.Flags,
86 Inst.SwitchBlock.Bits,
87 Inst.SwitchBlockErrUnion.Bits,
88 Inst.FuncFancy.Bits,
89 Inst.Declaration.Flags,
90 => @bitCast(code.extra[i]),
91
92 else => @compileError("bad field type"),
93 };
94 i += 1;
95 }
96 return .{
97 .data = result,
98 .end = i,
99 };
100}
101
102pub const NullTerminatedString = enum(u32) {
103 empty = 0,
104 _,
105};
106
107/// Given an index into `string_bytes` returns the null-terminated string found there.
108pub fn nullTerminatedString(code: Zir, index: NullTerminatedString) [:0]const u8 {
109 const start = @intFromEnum(index);
110 var end: u32 = start;
111 while (code.string_bytes[end] != 0) {
112 end += 1;
113 }
114 return code.string_bytes[start..end :0];
115}
116
117pub fn refSlice(code: Zir, start: usize, len: usize) []Inst.Ref {
118 return @ptrCast(code.extra[start..][0..len]);
119}
120
121pub fn bodySlice(zir: Zir, start: usize, len: usize) []Inst.Index {
122 return @ptrCast(zir.extra[start..][0..len]);
123}
124
125pub fn hasCompileErrors(code: Zir) bool {
126 return code.extra[@intFromEnum(ExtraIndex.compile_errors)] != 0;
127}
128
129pub fn deinit(code: *Zir, gpa: Allocator) void {
130 code.instructions.deinit(gpa);
131 gpa.free(code.string_bytes);
132 gpa.free(code.extra);
133 code.* = undefined;
134}
135
136/// These are untyped instructions generated from an Abstract Syntax Tree.
137/// The data here is immutable because it is possible to have multiple
138/// analyses on the same ZIR happening at the same time.
139pub const Inst = struct {
140 tag: Tag,
141 data: Data,
142
143 /// These names are used directly as the instruction names in the text format.
144 /// See `data_field_map` for a list of which `Data` fields are used by each `Tag`.
145 pub const Tag = enum(u8) {
146 /// Arithmetic addition, asserts no integer overflow.
147 /// Uses the `pl_node` union field. Payload is `Bin`.
148 add,
149 /// Twos complement wrapping integer addition.
150 /// Uses the `pl_node` union field. Payload is `Bin`.
151 addwrap,
152 /// Saturating addition.
153 /// Uses the `pl_node` union field. Payload is `Bin`.
154 add_sat,
155 /// The same as `add` except no safety check.
156 add_unsafe,
157 /// Arithmetic subtraction. Asserts no integer overflow.
158 /// Uses the `pl_node` union field. Payload is `Bin`.
159 sub,
160 /// Twos complement wrapping integer subtraction.
161 /// Uses the `pl_node` union field. Payload is `Bin`.
162 subwrap,
163 /// Saturating subtraction.
164 /// Uses the `pl_node` union field. Payload is `Bin`.
165 sub_sat,
166 /// Arithmetic multiplication. Asserts no integer overflow.
167 /// Uses the `pl_node` union field. Payload is `Bin`.
168 mul,
169 /// Twos complement wrapping integer multiplication.
170 /// Uses the `pl_node` union field. Payload is `Bin`.
171 mulwrap,
172 /// Saturating multiplication.
173 /// Uses the `pl_node` union field. Payload is `Bin`.
174 mul_sat,
175 /// Implements the `@divExact` builtin.
176 /// Uses the `pl_node` union field with payload `Bin`.
177 div_exact,
178 /// Implements the `@divFloor` builtin.
179 /// Uses the `pl_node` union field with payload `Bin`.
180 div_floor,
181 /// Implements the `@divTrunc` builtin.
182 /// Uses the `pl_node` union field with payload `Bin`.
183 div_trunc,
184 /// Implements the `@mod` builtin.
185 /// Uses the `pl_node` union field with payload `Bin`.
186 mod,
187 /// Implements the `@rem` builtin.
188 /// Uses the `pl_node` union field with payload `Bin`.
189 rem,
190 /// Ambiguously remainder division or modulus. If the computation would possibly have
191 /// a different value depending on whether the operation is remainder division or modulus,
192 /// a compile error is emitted. Otherwise the computation is performed.
193 /// Uses the `pl_node` union field. Payload is `Bin`.
194 mod_rem,
195 /// Integer shift-left. Zeroes are shifted in from the right hand side.
196 /// Uses the `pl_node` union field. Payload is `Bin`.
197 shl,
198 /// Implements the `@shlExact` builtin.
199 /// Uses the `pl_node` union field with payload `Bin`.
200 shl_exact,
201 /// Saturating shift-left.
202 /// Uses the `pl_node` union field. Payload is `Bin`.
203 shl_sat,
204 /// Integer shift-right. Arithmetic or logical depending on the signedness of
205 /// the integer type.
206 /// Uses the `pl_node` union field. Payload is `Bin`.
207 shr,
208 /// Implements the `@shrExact` builtin.
209 /// Uses the `pl_node` union field with payload `Bin`.
210 shr_exact,
211
212 /// Declares a parameter of the current function. Used for:
213 /// * debug info
214 /// * checking shadowing against declarations in the current namespace
215 /// * parameter type expressions referencing other parameters
216 /// These occur in the block outside a function body (the same block as
217 /// contains the func instruction).
218 /// Uses the `pl_tok` field. Token is the parameter name, payload is a `Param`.
219 param,
220 /// Same as `param` except the parameter is marked comptime.
221 param_comptime,
222 /// Same as `param` except the parameter is marked anytype.
223 /// Uses the `str_tok` field. Token is the parameter name. String is the parameter name.
224 param_anytype,
225 /// Same as `param` except the parameter is marked both comptime and anytype.
226 /// Uses the `str_tok` field. Token is the parameter name. String is the parameter name.
227 param_anytype_comptime,
228 /// Array concatenation. `a ++ b`
229 /// Uses the `pl_node` union field. Payload is `Bin`.
230 array_cat,
231 /// Array multiplication `a ** b`
232 /// Uses the `pl_node` union field. Payload is `ArrayMul`.
233 array_mul,
234 /// `[N]T` syntax. No source location provided.
235 /// Uses the `pl_node` union field. Payload is `Bin`. lhs is length, rhs is element type.
236 array_type,
237 /// `[N:S]T` syntax. Source location is the array type expression node.
238 /// Uses the `pl_node` union field. Payload is `ArrayTypeSentinel`.
239 array_type_sentinel,
240 /// `@Vector` builtin.
241 /// Uses the `pl_node` union field with `Bin` payload.
242 /// lhs is length, rhs is element type.
243 vector_type,
244 /// Given a pointer type, returns its element type. Reaches through any optional or error
245 /// union types wrapping the pointer. Asserts that the underlying type is a pointer type.
246 /// Returns generic poison if the element type is `anyopaque`.
247 /// Uses the `un_node` field.
248 elem_type,
249 /// Given an indexable pointer (slice, many-ptr, single-ptr-to-array), returns its
250 /// element type. Emits a compile error if the type is not an indexable pointer.
251 /// Uses the `un_node` field.
252 indexable_ptr_elem_type,
253 /// Given a vector type, returns its element type.
254 /// Uses the `un_node` field.
255 vector_elem_type,
256 /// Given a pointer to an indexable object, returns the len property. This is
257 /// used by for loops. This instruction also emits a for-loop specific compile
258 /// error if the indexable object is not indexable.
259 /// Uses the `un_node` field. The AST node is the for loop node.
260 indexable_ptr_len,
261 /// Create a `anyframe->T` type.
262 /// Uses the `un_node` field.
263 anyframe_type,
264 /// Type coercion to the function's return type.
265 /// Uses the `pl_node` field. Payload is `As`. AST node could be many things.
266 as_node,
267 /// Same as `as_node` but ignores runtime to comptime int error.
268 as_shift_operand,
269 /// Bitwise AND. `&`
270 bit_and,
271 /// Reinterpret the memory representation of a value as a different type.
272 /// Uses the pl_node field with payload `Bin`.
273 bitcast,
274 /// Bitwise NOT. `~`
275 /// Uses `un_node`.
276 bit_not,
277 /// Bitwise OR. `|`
278 bit_or,
279 /// A labeled block of code, which can return a value.
280 /// Uses the `pl_node` union field. Payload is `Block`.
281 block,
282 /// Like `block`, but forces full evaluation of its contents at compile-time.
283 /// Uses the `pl_node` union field. Payload is `Block`.
284 block_comptime,
285 /// A list of instructions which are analyzed in the parent context, without
286 /// generating a runtime block. Must terminate with an "inline" variant of
287 /// a noreturn instruction.
288 /// Uses the `pl_node` union field. Payload is `Block`.
289 block_inline,
290 /// This instruction may only ever appear in the list of declarations for a
291 /// namespace type, e.g. within a `struct_decl` instruction. It represents a
292 /// single source declaration (`const`/`var`/`fn`), containing the name,
293 /// attributes, type, and value of the declaration.
294 /// Uses the `pl_node` union field. Payload is `Declaration`.
295 declaration,
296 /// Implements `suspend {...}`.
297 /// Uses the `pl_node` union field. Payload is `Block`.
298 suspend_block,
299 /// Boolean NOT. See also `bit_not`.
300 /// Uses the `un_node` field.
301 bool_not,
302 /// Short-circuiting boolean `and`. `lhs` is a boolean `Ref` and the other operand
303 /// is a block, which is evaluated if `lhs` is `true`.
304 /// Uses the `pl_node` union field. Payload is `BoolBr`.
305 bool_br_and,
306 /// Short-circuiting boolean `or`. `lhs` is a boolean `Ref` and the other operand
307 /// is a block, which is evaluated if `lhs` is `false`.
308 /// Uses the `pl_node` union field. Payload is `BoolBr`.
309 bool_br_or,
310 /// Return a value from a block.
311 /// Uses the `break` union field.
312 /// Uses the source information from previous instruction.
313 @"break",
314 /// Return a value from a block. This instruction is used as the terminator
315 /// of a `block_inline`. It allows using the return value from `Sema.analyzeBody`.
316 /// This instruction may also be used when it is known that there is only one
317 /// break instruction in a block, and the target block is the parent.
318 /// Uses the `break` union field.
319 break_inline,
320 /// Checks that comptime control flow does not happen inside a runtime block.
321 /// Uses the `un_node` union field.
322 check_comptime_control_flow,
323 /// Function call.
324 /// Uses the `pl_node` union field with payload `Call`.
325 /// AST node is the function call.
326 call,
327 /// Function call using `a.b()` syntax.
328 /// Uses the named field as the callee. If there is no such field, searches in the type for
329 /// a decl matching the field name. The decl is resolved and we ensure that it's a function
330 /// which can accept the object as the first parameter, with one pointer fixup. This
331 /// function is then used as the callee, with the object as an implicit first parameter.
332 /// Uses the `pl_node` union field with payload `FieldCall`.
333 /// AST node is the function call.
334 field_call,
335 /// Implements the `@call` builtin.
336 /// Uses the `pl_node` union field with payload `BuiltinCall`.
337 /// AST node is the builtin call.
338 builtin_call,
339 /// `<`
340 /// Uses the `pl_node` union field. Payload is `Bin`.
341 cmp_lt,
342 /// `<=`
343 /// Uses the `pl_node` union field. Payload is `Bin`.
344 cmp_lte,
345 /// `==`
346 /// Uses the `pl_node` union field. Payload is `Bin`.
347 cmp_eq,
348 /// `>=`
349 /// Uses the `pl_node` union field. Payload is `Bin`.
350 cmp_gte,
351 /// `>`
352 /// Uses the `pl_node` union field. Payload is `Bin`.
353 cmp_gt,
354 /// `!=`
355 /// Uses the `pl_node` union field. Payload is `Bin`.
356 cmp_neq,
357 /// Conditional branch. Splits control flow based on a boolean condition value.
358 /// Uses the `pl_node` union field. AST node is an if, while, for, etc.
359 /// Payload is `CondBr`.
360 condbr,
361 /// Same as `condbr`, except the condition is coerced to a comptime value, and
362 /// only the taken branch is analyzed. The then block and else block must
363 /// terminate with an "inline" variant of a noreturn instruction.
364 condbr_inline,
365 /// Given an operand which is an error union, splits control flow. In
366 /// case of error, control flow goes into the block that is part of this
367 /// instruction, which is guaranteed to end with a return instruction
368 /// and never breaks out of the block.
369 /// In the case of non-error, control flow proceeds to the next instruction
370 /// after the `try`, with the result of this instruction being the unwrapped
371 /// payload value, as if `err_union_payload_unsafe` was executed on the operand.
372 /// Uses the `pl_node` union field. Payload is `Try`.
373 @"try",
374 /// Same as `try` except the operand is a pointer and the result is a pointer.
375 try_ptr,
376 /// An error set type definition. Contains a list of field names.
377 /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`.
378 error_set_decl,
379 error_set_decl_anon,
380 error_set_decl_func,
381 /// Declares the beginning of a statement. Used for debug info.
382 /// Uses the `dbg_stmt` union field. The line and column are offset
383 /// from the parent declaration.
384 dbg_stmt,
385 /// Marks a variable declaration. Used for debug info.
386 /// Uses the `str_op` union field. The string is the local variable name,
387 /// and the operand is the pointer to the variable's location. The local
388 /// may be a const or a var.
389 dbg_var_ptr,
390 /// Same as `dbg_var_ptr` but the local is always a const and the operand
391 /// is the local's value.
392 dbg_var_val,
393 /// Uses a name to identify a Decl and takes a pointer to it.
394 /// Uses the `str_tok` union field.
395 decl_ref,
396 /// Uses a name to identify a Decl and uses it as a value.
397 /// Uses the `str_tok` union field.
398 decl_val,
399 /// Load the value from a pointer. Assumes `x.*` syntax.
400 /// Uses `un_node` field. AST node is the `x.*` syntax.
401 load,
402 /// Arithmetic division. Asserts no integer overflow.
403 /// Uses the `pl_node` union field. Payload is `Bin`.
404 div,
405 /// Given a pointer to an array, slice, or pointer, returns a pointer to the element at
406 /// the provided index.
407 /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
408 elem_ptr_node,
409 /// Same as `elem_ptr_node` but used only for for loop.
410 /// Uses the `pl_node` union field. AST node is the condition of a for loop.
411 /// Payload is `Bin`.
412 /// No OOB safety check is emitted.
413 elem_ptr,
414 /// Given an array, slice, or pointer, returns the element at the provided index.
415 /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
416 elem_val_node,
417 /// Same as `elem_val_node` but used only for for loop.
418 /// Uses the `pl_node` union field. AST node is the condition of a for loop.
419 /// Payload is `Bin`.
420 /// No OOB safety check is emitted.
421 elem_val,
422 /// Same as `elem_val` but takes the index as an immediate value.
423 /// No OOB safety check is emitted. A prior instruction must validate this operation.
424 /// Uses the `elem_val_imm` union field.
425 elem_val_imm,
426 /// Emits a compile error if the operand is not `void`.
427 /// Uses the `un_node` field.
428 ensure_result_used,
429 /// Emits a compile error if an error is ignored.
430 /// Uses the `un_node` field.
431 ensure_result_non_error,
432 /// Emits a compile error error union payload is not void.
433 ensure_err_union_payload_void,
434 /// Create a `E!T` type.
435 /// Uses the `pl_node` field with `Bin` payload.
436 error_union_type,
437 /// `error.Foo` syntax. Uses the `str_tok` field of the Data union.
438 error_value,
439 /// Implements the `@export` builtin function, based on either an identifier to a Decl,
440 /// or field access of a Decl. The thing being exported is the Decl.
441 /// Uses the `pl_node` union field. Payload is `Export`.
442 @"export",
443 /// Implements the `@export` builtin function, based on a comptime-known value.
444 /// The thing being exported is the comptime-known value which is the operand.
445 /// Uses the `pl_node` union field. Payload is `ExportValue`.
446 export_value,
447 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
448 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
449 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
450 field_ptr,
451 /// Given a struct or object that contains virtual fields, returns the named field.
452 /// The field name is stored in string_bytes. Used by a.b syntax.
453 /// This instruction also accepts a pointer.
454 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
455 field_val,
456 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
457 /// to the named field. The field name is a comptime instruction. Used by @field.
458 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
459 field_ptr_named,
460 /// Given a struct or object that contains virtual fields, returns the named field.
461 /// The field name is a comptime instruction. Used by @field.
462 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
463 field_val_named,
464 /// Returns a function type, or a function instance, depending on whether
465 /// the body_len is 0. Calling convention is auto.
466 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.
467 func,
468 /// Same as `func` but has an inferred error set.
469 func_inferred,
470 /// Represents a function declaration or function prototype, depending on
471 /// whether body_len is 0.
472 /// Uses the `pl_node` union field. `payload_index` points to a `FuncFancy`.
473 func_fancy,
474 /// Implements the `@import` builtin.
475 /// Uses the `str_tok` field.
476 import,
477 /// Integer literal that fits in a u64. Uses the `int` union field.
478 int,
479 /// Arbitrary sized integer literal. Uses the `str` union field.
480 int_big,
481 /// A float literal that fits in a f64. Uses the float union value.
482 float,
483 /// A float literal that fits in a f128. Uses the `pl_node` union value.
484 /// Payload is `Float128`.
485 float128,
486 /// Make an integer type out of signedness and bit count.
487 /// Payload is `int_type`
488 int_type,
489 /// Return a boolean false if an optional is null. `x != null`
490 /// Uses the `un_node` field.
491 is_non_null,
492 /// Return a boolean false if an optional is null. `x.* != null`
493 /// Uses the `un_node` field.
494 is_non_null_ptr,
495 /// Return a boolean false if value is an error
496 /// Uses the `un_node` field.
497 is_non_err,
498 /// Return a boolean false if dereferenced pointer is an error
499 /// Uses the `un_node` field.
500 is_non_err_ptr,
501 /// Same as `is_non_er` but doesn't validate that the type can be an error.
502 /// Uses the `un_node` field.
503 ret_is_non_err,
504 /// A labeled block of code that loops forever. At the end of the body will have either
505 /// a `repeat` instruction or a `repeat_inline` instruction.
506 /// Uses the `pl_node` field. The AST node is either a for loop or while loop.
507 /// This ZIR instruction is needed because AIR does not (yet?) match ZIR, and Sema
508 /// needs to emit more than 1 AIR block for this instruction.
509 /// The payload is `Block`.
510 loop,
511 /// Sends runtime control flow back to the beginning of the current block.
512 /// Uses the `node` field.
513 repeat,
514 /// Sends comptime control flow back to the beginning of the current block.
515 /// Uses the `node` field.
516 repeat_inline,
517 /// Asserts that all the lengths provided match. Used to build a for loop.
518 /// Return value is the length as a usize.
519 /// Uses the `pl_node` field with payload `MultiOp`.
520 /// There is exactly one item corresponding to each AST node inside the for
521 /// loop condition. Any item may be `none`, indicating an unbounded range.
522 /// Illegal behaviors:
523 /// * If all lengths are unbounded ranges (always a compile error).
524 /// * If any two lengths do not match each other.
525 for_len,
526 /// Merge two error sets into one, `E1 || E2`.
527 /// Uses the `pl_node` field with payload `Bin`.
528 merge_error_sets,
529 /// Turns an R-Value into a const L-Value. In other words, it takes a value,
530 /// stores it in a memory location, and returns a const pointer to it. If the value
531 /// is `comptime`, the memory location is global static constant data. Otherwise,
532 /// the memory location is in the stack frame, local to the scope containing the
533 /// instruction.
534 /// Uses the `un_tok` union field.
535 ref,
536 /// Sends control flow back to the function's callee.
537 /// Includes an operand as the return value.
538 /// Includes an AST node source location.
539 /// Uses the `un_node` union field.
540 ret_node,
541 /// Sends control flow back to the function's callee.
542 /// The operand is a `ret_ptr` instruction, where the return value can be found.
543 /// Includes an AST node source location.
544 /// Uses the `un_node` union field.
545 ret_load,
546 /// Sends control flow back to the function's callee.
547 /// Includes an operand as the return value.
548 /// Includes a token source location.
549 /// Uses the `un_tok` union field.
550 ret_implicit,
551 /// Sends control flow back to the function's callee.
552 /// The return operand is `error.foo` where `foo` is given by the string.
553 /// If the current function has an inferred error set, the error given by the
554 /// name is added to it.
555 /// Uses the `str_tok` union field.
556 ret_err_value,
557 /// A string name is provided which is an anonymous error set value.
558 /// If the current function has an inferred error set, the error given by the
559 /// name is added to it.
560 /// Results in the error code. Note that control flow is not diverted with
561 /// this instruction; a following 'ret' instruction will do the diversion.
562 /// Uses the `str_tok` union field.
563 ret_err_value_code,
564 /// Obtains a pointer to the return value.
565 /// Uses the `node` union field.
566 ret_ptr,
567 /// Obtains the return type of the in-scope function.
568 /// Uses the `node` union field.
569 ret_type,
570 /// Create a pointer type which can have a sentinel, alignment, address space, and/or bit range.
571 /// Uses the `ptr_type` union field.
572 ptr_type,
573 /// Slice operation `lhs[rhs..]`. No sentinel and no end offset.
574 /// Returns a pointer to the subslice.
575 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceStart`.
576 slice_start,
577 /// Slice operation `array_ptr[start..end]`. No sentinel.
578 /// Returns a pointer to the subslice.
579 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceEnd`.
580 slice_end,
581 /// Slice operation `array_ptr[start..end:sentinel]`.
582 /// Returns a pointer to the subslice.
583 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.
584 slice_sentinel,
585 /// Slice operation `array_ptr[start..][0..len]`. Optional sentinel.
586 /// Returns a pointer to the subslice.
587 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.
588 slice_length,
589 /// Same as `store` except provides a source location.
590 /// Uses the `pl_node` union field. Payload is `Bin`.
591 store_node,
592 /// Same as `store_node` but the type of the value being stored will be
593 /// used to infer the pointer type of an `alloc_inferred`.
594 /// Uses the `pl_node` union field. Payload is `Bin`.
595 store_to_inferred_ptr,
596 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
597 /// Uses the `str` union field.
598 str,
599 /// Arithmetic negation. Asserts no integer overflow.
600 /// Same as sub with a lhs of 0, split into a separate instruction to save memory.
601 /// Uses `un_node`.
602 negate,
603 /// Twos complement wrapping integer negation.
604 /// Same as subwrap with a lhs of 0, split into a separate instruction to save memory.
605 /// Uses `un_node`.
606 negate_wrap,
607 /// Returns the type of a value.
608 /// Uses the `un_node` field.
609 typeof,
610 /// Implements `@TypeOf` for one operand.
611 /// Uses the `pl_node` field.
612 typeof_builtin,
613 /// Given a value, look at the type of it, which must be an integer type.
614 /// Returns the integer type for the RHS of a shift operation.
615 /// Uses the `un_node` field.
616 typeof_log2_int_type,
617 /// Asserts control-flow will not reach this instruction (`unreachable`).
618 /// Uses the `@"unreachable"` union field.
619 @"unreachable",
620 /// Bitwise XOR. `^`
621 /// Uses the `pl_node` union field. Payload is `Bin`.
622 xor,
623 /// Create an optional type '?T'
624 /// Uses the `un_node` field.
625 optional_type,
626 /// ?T => T with safety.
627 /// Given an optional value, returns the payload value, with a safety check that
628 /// the value is non-null. Used for `orelse`, `if` and `while`.
629 /// Uses the `un_node` field.
630 optional_payload_safe,
631 /// ?T => T without safety.
632 /// Given an optional value, returns the payload value. No safety checks.
633 /// Uses the `un_node` field.
634 optional_payload_unsafe,
635 /// *?T => *T with safety.
636 /// Given a pointer to an optional value, returns a pointer to the payload value,
637 /// with a safety check that the value is non-null. Used for `orelse`, `if` and `while`.
638 /// Uses the `un_node` field.
639 optional_payload_safe_ptr,
640 /// *?T => *T without safety.
641 /// Given a pointer to an optional value, returns a pointer to the payload value.
642 /// No safety checks.
643 /// Uses the `un_node` field.
644 optional_payload_unsafe_ptr,
645 /// E!T => T without safety.
646 /// Given an error union value, returns the payload value. No safety checks.
647 /// Uses the `un_node` field.
648 err_union_payload_unsafe,
649 /// *E!T => *T without safety.
650 /// Given a pointer to a error union value, returns a pointer to the payload value.
651 /// No safety checks.
652 /// Uses the `un_node` field.
653 err_union_payload_unsafe_ptr,
654 /// E!T => E without safety.
655 /// Given an error union value, returns the error code. No safety checks.
656 /// Uses the `un_node` field.
657 err_union_code,
658 /// *E!T => E without safety.
659 /// Given a pointer to an error union value, returns the error code. No safety checks.
660 /// Uses the `un_node` field.
661 err_union_code_ptr,
662 /// An enum literal. Uses the `str_tok` union field.
663 enum_literal,
664 /// A switch expression. Uses the `pl_node` union field.
665 /// AST node is the switch, payload is `SwitchBlock`.
666 switch_block,
667 /// A switch expression. Uses the `pl_node` union field.
668 /// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer.
669 switch_block_ref,
670 /// A switch on an error union `a catch |err| switch (err) {...}`.
671 /// Uses the `pl_node` union field. AST node is the `catch`, payload is `SwitchBlockErrUnion`.
672 switch_block_err_union,
673 /// Check that operand type supports the dereference operand (.*).
674 /// Uses the `un_node` field.
675 validate_deref,
676 /// Check that the operand's type is an array or tuple with the given number of elements.
677 /// Uses the `pl_node` field. Payload is `ValidateDestructure`.
678 validate_destructure,
679 /// Given a struct or union, and a field name as a Ref,
680 /// returns the field type. Uses the `pl_node` field. Payload is `FieldTypeRef`.
681 field_type_ref,
682 /// Given a pointer, initializes all error unions and optionals in the pointee to payloads,
683 /// returning the base payload pointer. For instance, converts *E!?T into a valid *T
684 /// (clobbering any existing error or null value).
685 /// Uses the `un_node` field.
686 opt_eu_base_ptr_init,
687 /// Coerce a given value such that when a reference is taken, the resulting pointer will be
688 /// coercible to the given type. For instance, given a value of type 'u32' and the pointer
689 /// type '*u64', coerces the value to a 'u64'. Asserts that the type is a pointer type.
690 /// Uses the `pl_node` field. Payload is `Bin`.
691 /// LHS is the pointer type, RHS is the value.
692 coerce_ptr_elem_ty,
693 /// Given a type, validate that it is a pointer type suitable for return from the address-of
694 /// operator. Emit a compile error if not.
695 /// Uses the `un_tok` union field. Token is the `&` operator. Operand is the type.
696 validate_ref_ty,
697
698 // The following tags all relate to struct initialization expressions.
699
700 /// A struct literal with a specified explicit type, with no fields.
701 /// Uses the `un_node` field.
702 struct_init_empty,
703 /// An anonymous struct literal with a known result type, with no fields.
704 /// Uses the `un_node` field.
705 struct_init_empty_result,
706 /// An anonymous struct literal with no fields, returned by reference, with a known result
707 /// type for the pointer. Asserts that the type is a pointer.
708 /// Uses the `un_node` field.
709 struct_init_empty_ref_result,
710 /// Struct initialization without a type. Creates a value of an anonymous struct type.
711 /// Uses the `pl_node` field. Payload is `StructInitAnon`.
712 struct_init_anon,
713 /// Finalizes a typed struct or union initialization, performs validation, and returns the
714 /// struct or union value. The given type must be validated prior to this instruction, using
715 /// `validate_struct_init_ty` or `validate_struct_init_result_ty`. If the given type is
716 /// generic poison, this is downgraded to an anonymous initialization.
717 /// Uses the `pl_node` field. Payload is `StructInit`.
718 struct_init,
719 /// Struct initialization syntax, make the result a pointer. Equivalent to `struct_init`
720 /// followed by `ref` - this ZIR tag exists as an optimization for a common pattern.
721 /// Uses the `pl_node` field. Payload is `StructInit`.
722 struct_init_ref,
723 /// Checks that the type supports struct init syntax. Always returns void.
724 /// Uses the `un_node` field.
725 validate_struct_init_ty,
726 /// Like `validate_struct_init_ty`, but additionally accepts types which structs coerce to.
727 /// Used on the known result type of a struct init expression. Always returns void.
728 /// Uses the `un_node` field.
729 validate_struct_init_result_ty,
730 /// Given a set of `struct_init_field_ptr` instructions, assumes they are all part of a
731 /// struct initialization expression, and emits compile errors for duplicate fields as well
732 /// as missing fields, if applicable.
733 /// This instruction asserts that there is at least one struct_init_field_ptr instruction,
734 /// because it must use one of them to find out the struct type.
735 /// Uses the `pl_node` field. Payload is `Block`.
736 validate_ptr_struct_init,
737 /// Given a type being used for a struct initialization expression, returns the type of the
738 /// field with the given name.
739 /// Uses the `pl_node` field. Payload is `FieldType`.
740 struct_init_field_type,
741 /// Given a pointer being used as the result pointer of a struct initialization expression,
742 /// return a pointer to the field of the given name.
743 /// Uses the `pl_node` field. The AST node is the field initializer. Payload is Field.
744 struct_init_field_ptr,
745
746 // The following tags all relate to array initialization expressions.
747
748 /// Array initialization without a type. Creates a value of a tuple type.
749 /// Uses the `pl_node` field. Payload is `MultiOp`.
750 array_init_anon,
751 /// Array initialization syntax with a known type. The given type must be validated prior to
752 /// this instruction, using some `validate_array_init_*_ty` instruction.
753 /// Uses the `pl_node` field. Payload is `MultiOp`, where the first operand is the type.
754 array_init,
755 /// Array initialization syntax, make the result a pointer. Equivalent to `array_init`
756 /// followed by `ref`- this ZIR tag exists as an optimization for a common pattern.
757 /// Uses the `pl_node` field. Payload is `MultiOp`, where the first operand is the type.
758 array_init_ref,
759 /// Checks that the type supports array init syntax. Always returns void.
760 /// Uses the `pl_node` field. Payload is `ArrayInit`.
761 validate_array_init_ty,
762 /// Like `validate_array_init_ty`, but additionally accepts types which arrays coerce to.
763 /// Used on the known result type of an array init expression. Always returns void.
764 /// Uses the `pl_node` field. Payload is `ArrayInit`.
765 validate_array_init_result_ty,
766 /// Given a pointer or slice type and an element count, return the expected type of an array
767 /// initializer such that a pointer to the initializer has the given pointer type, checking
768 /// that this type supports array init syntax and emitting a compile error if not. Preserves
769 /// error union and optional wrappers on the array type, if any.
770 /// Asserts that the given type is a pointer or slice type.
771 /// Uses the `pl_node` field. Payload is `ArrayInitRefTy`.
772 validate_array_init_ref_ty,
773 /// Given a set of `array_init_elem_ptr` instructions, assumes they are all part of an array
774 /// initialization expression, and emits a compile error if the number of elements does not
775 /// match the array type.
776 /// This instruction asserts that there is at least one `array_init_elem_ptr` instruction,
777 /// because it must use one of them to find out the array type.
778 /// Uses the `pl_node` field. Payload is `Block`.
779 validate_ptr_array_init,
780 /// Given a type being used for an array initialization expression, returns the type of the
781 /// element at the given index.
782 /// Uses the `bin` union field. lhs is the indexable type, rhs is the index.
783 array_init_elem_type,
784 /// Given a pointer being used as the result pointer of an array initialization expression,
785 /// return a pointer to the element at the given index.
786 /// Uses the `pl_node` union field. AST node is an element inside array initialization
787 /// syntax. Payload is `ElemPtrImm`.
788 array_init_elem_ptr,
789
790 /// Implements the `@unionInit` builtin.
791 /// Uses the `pl_node` field. Payload is `UnionInit`.
792 union_init,
793 /// Implements the `@typeInfo` builtin. Uses `un_node`.
794 type_info,
795 /// Implements the `@sizeOf` builtin. Uses `un_node`.
796 size_of,
797 /// Implements the `@bitSizeOf` builtin. Uses `un_node`.
798 bit_size_of,
799
800 /// Implement builtin `@intFromPtr`. Uses `un_node`.
801 /// Convert a pointer to a `usize` integer.
802 int_from_ptr,
803 /// Emit an error message and fail compilation.
804 /// Uses the `un_node` field.
805 compile_error,
806 /// Changes the maximum number of backwards branches that compile-time
807 /// code execution can use before giving up and making a compile error.
808 /// Uses the `un_node` union field.
809 set_eval_branch_quota,
810 /// Converts an enum value into an integer. Resulting type will be the tag type
811 /// of the enum. Uses `un_node`.
812 int_from_enum,
813 /// Implement builtin `@alignOf`. Uses `un_node`.
814 align_of,
815 /// Implement builtin `@intFromBool`. Uses `un_node`.
816 int_from_bool,
817 /// Implement builtin `@embedFile`. Uses `un_node`.
818 embed_file,
819 /// Implement builtin `@errorName`. Uses `un_node`.
820 error_name,
821 /// Implement builtin `@panic`. Uses `un_node`.
822 panic,
823 /// Implements `@trap`.
824 /// Uses the `node` field.
825 trap,
826 /// Implement builtin `@setRuntimeSafety`. Uses `un_node`.
827 set_runtime_safety,
828 /// Implement builtin `@sqrt`. Uses `un_node`.
829 sqrt,
830 /// Implement builtin `@sin`. Uses `un_node`.
831 sin,
832 /// Implement builtin `@cos`. Uses `un_node`.
833 cos,
834 /// Implement builtin `@tan`. Uses `un_node`.
835 tan,
836 /// Implement builtin `@exp`. Uses `un_node`.
837 exp,
838 /// Implement builtin `@exp2`. Uses `un_node`.
839 exp2,
840 /// Implement builtin `@log`. Uses `un_node`.
841 log,
842 /// Implement builtin `@log2`. Uses `un_node`.
843 log2,
844 /// Implement builtin `@log10`. Uses `un_node`.
845 log10,
846 /// Implement builtin `@abs`. Uses `un_node`.
847 abs,
848 /// Implement builtin `@floor`. Uses `un_node`.
849 floor,
850 /// Implement builtin `@ceil`. Uses `un_node`.
851 ceil,
852 /// Implement builtin `@trunc`. Uses `un_node`.
853 trunc,
854 /// Implement builtin `@round`. Uses `un_node`.
855 round,
856 /// Implement builtin `@tagName`. Uses `un_node`.
857 tag_name,
858 /// Implement builtin `@typeName`. Uses `un_node`.
859 type_name,
860 /// Implement builtin `@Frame`. Uses `un_node`.
861 frame_type,
862 /// Implement builtin `@frameSize`. Uses `un_node`.
863 frame_size,
864
865 /// Implements the `@intFromFloat` builtin.
866 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
867 int_from_float,
868 /// Implements the `@floatFromInt` builtin.
869 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
870 float_from_int,
871 /// Implements the `@ptrFromInt` builtin.
872 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
873 ptr_from_int,
874 /// Converts an integer into an enum value.
875 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
876 enum_from_int,
877 /// Convert a larger float type to any other float type, possibly causing
878 /// a loss of precision.
879 /// Uses the `pl_node` field. AST is the `@floatCast` syntax.
880 /// Payload is `Bin` with lhs as the dest type, rhs the operand.
881 float_cast,
882 /// Implements the `@intCast` builtin.
883 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
884 /// Convert an integer value to another integer type, asserting that the destination type
885 /// can hold the same mathematical value.
886 int_cast,
887 /// Implements the `@ptrCast` builtin.
888 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
889 /// Not every `@ptrCast` will correspond to this instruction - see also
890 /// `ptr_cast_full` in `Extended`.
891 ptr_cast,
892 /// Implements the `@truncate` builtin.
893 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
894 truncate,
895
896 /// Implements the `@hasDecl` builtin.
897 /// Uses the `pl_node` union field. Payload is `Bin`.
898 has_decl,
899 /// Implements the `@hasField` builtin.
900 /// Uses the `pl_node` union field. Payload is `Bin`.
901 has_field,
902
903 /// Implements the `@clz` builtin. Uses the `un_node` union field.
904 clz,
905 /// Implements the `@ctz` builtin. Uses the `un_node` union field.
906 ctz,
907 /// Implements the `@popCount` builtin. Uses the `un_node` union field.
908 pop_count,
909 /// Implements the `@byteSwap` builtin. Uses the `un_node` union field.
910 byte_swap,
911 /// Implements the `@bitReverse` builtin. Uses the `un_node` union field.
912 bit_reverse,
913
914 /// Implements the `@bitOffsetOf` builtin.
915 /// Uses the `pl_node` union field with payload `Bin`.
916 bit_offset_of,
917 /// Implements the `@offsetOf` builtin.
918 /// Uses the `pl_node` union field with payload `Bin`.
919 offset_of,
920 /// Implements the `@splat` builtin.
921 /// Uses the `pl_node` union field with payload `Bin`.
922 splat,
923 /// Implements the `@reduce` builtin.
924 /// Uses the `pl_node` union field with payload `Bin`.
925 reduce,
926 /// Implements the `@shuffle` builtin.
927 /// Uses the `pl_node` union field with payload `Shuffle`.
928 shuffle,
929 /// Implements the `@atomicLoad` builtin.
930 /// Uses the `pl_node` union field with payload `AtomicLoad`.
931 atomic_load,
932 /// Implements the `@atomicRmw` builtin.
933 /// Uses the `pl_node` union field with payload `AtomicRmw`.
934 atomic_rmw,
935 /// Implements the `@atomicStore` builtin.
936 /// Uses the `pl_node` union field with payload `AtomicStore`.
937 atomic_store,
938 /// Implements the `@mulAdd` builtin.
939 /// Uses the `pl_node` union field with payload `MulAdd`.
940 /// The addend communicates the type of the builtin.
941 /// The mulends need to be coerced to the same type.
942 mul_add,
943 /// Implements the `@fieldParentPtr` builtin.
944 /// Uses the `pl_node` union field with payload `FieldParentPtr`.
945 field_parent_ptr,
946 /// Implements the `@memcpy` builtin.
947 /// Uses the `pl_node` union field with payload `Bin`.
948 memcpy,
949 /// Implements the `@memset` builtin.
950 /// Uses the `pl_node` union field with payload `Bin`.
951 memset,
952 /// Implements the `@min` builtin for 2 args.
953 /// Uses the `pl_node` union field with payload `Bin`
954 min,
955 /// Implements the `@max` builtin for 2 args.
956 /// Uses the `pl_node` union field with payload `Bin`
957 max,
958 /// Implements the `@cImport` builtin.
959 /// Uses the `pl_node` union field with payload `Block`.
960 c_import,
961
962 /// Allocates stack local memory.
963 /// Uses the `un_node` union field. The operand is the type of the allocated object.
964 /// The node source location points to a var decl node.
965 /// A `make_ptr_const` instruction should be used once the value has
966 /// been stored to the allocation. To ensure comptime value detection
967 /// functions, there are some restrictions on how this pointer should be
968 /// used prior to the `make_ptr_const` instruction: no pointer derived
969 /// from this `alloc` may be returned from a block or stored to another
970 /// address. In other words, it must be trivial to determine whether any
971 /// given pointer derives from this one.
972 alloc,
973 /// Same as `alloc` except mutable. As such, `make_ptr_const` need not be used,
974 /// and there are no restrictions on the usage of the pointer.
975 alloc_mut,
976 /// Allocates comptime-mutable memory.
977 /// Uses the `un_node` union field. The operand is the type of the allocated object.
978 /// The node source location points to a var decl node.
979 alloc_comptime_mut,
980 /// Same as `alloc` except the type is inferred.
981 /// Uses the `node` union field.
982 alloc_inferred,
983 /// Same as `alloc_inferred` except mutable.
984 alloc_inferred_mut,
985 /// Allocates comptime const memory.
986 /// Uses the `node` union field. The type of the allocated object is inferred.
987 /// The node source location points to a var decl node.
988 alloc_inferred_comptime,
989 /// Same as `alloc_comptime_mut` except the type is inferred.
990 alloc_inferred_comptime_mut,
991 /// Each `store_to_inferred_ptr` puts the type of the stored value into a set,
992 /// and then `resolve_inferred_alloc` triggers peer type resolution on the set.
993 /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which
994 /// is the allocation that needs to have its type inferred.
995 /// Uses the `un_node` field. The AST node is the var decl.
996 resolve_inferred_alloc,
997 /// Turns a pointer coming from an `alloc` or `Extended.alloc` into a constant
998 /// version of the same pointer. For inferred allocations this is instead implicitly
999 /// handled by the `resolve_inferred_alloc` instruction.
1000 /// Uses the `un_node` union field.
1001 make_ptr_const,
1002
1003 /// Implements `resume` syntax. Uses `un_node` field.
1004 @"resume",
1005 @"await",
1006
1007 /// When a type or function refers to a comptime value from an outer
1008 /// scope, that forms a closure over comptime value. The outer scope
1009 /// will record a capture of that value, which encodes its current state
1010 /// and marks it to persist. Uses `un_tok` field. Operand is the
1011 /// instruction value to capture.
1012 closure_capture,
1013 /// The inner scope of a closure uses closure_get to retrieve the value
1014 /// stored by the outer scope. Uses `inst_node` field. Operand is the
1015 /// closure_capture instruction ref.
1016 closure_get,
1017
1018 /// A defer statement.
1019 /// Uses the `defer` union field.
1020 @"defer",
1021 /// An errdefer statement with a code.
1022 /// Uses the `err_defer_code` union field.
1023 defer_err_code,
1024
1025 /// Requests that Sema update the saved error return trace index for the enclosing
1026 /// block, if the operand is .none or of an error/error-union type.
1027 /// Uses the `save_err_ret_index` field.
1028 save_err_ret_index,
1029 /// Specialized form of `Extended.restore_err_ret_index`.
1030 /// Unconditionally restores the error return index to its last saved state
1031 /// in the block referred to by `operand`. If `operand` is `none`, restores
1032 /// to the point of function entry.
1033 /// Uses the `un_node` field.
1034 restore_err_ret_index_unconditional,
1035 /// Specialized form of `Extended.restore_err_ret_index`.
1036 /// Restores the error return index to its state at the entry of
1037 /// the current function conditional on `operand` being a non-error.
1038 /// If `operand` is `none`, restores unconditionally.
1039 /// Uses the `un_node` field.
1040 restore_err_ret_index_fn_entry,
1041
1042 /// The ZIR instruction tag is one of the `Extended` ones.
1043 /// Uses the `extended` union field.
1044 extended,
1045
1046 /// Returns whether the instruction is one of the control flow "noreturn" types.
1047 /// Function calls do not count.
1048 pub fn isNoReturn(tag: Tag) bool {
1049 return switch (tag) {
1050 .param,
1051 .param_comptime,
1052 .param_anytype,
1053 .param_anytype_comptime,
1054 .add,
1055 .addwrap,
1056 .add_sat,
1057 .add_unsafe,
1058 .alloc,
1059 .alloc_mut,
1060 .alloc_comptime_mut,
1061 .alloc_inferred,
1062 .alloc_inferred_mut,
1063 .alloc_inferred_comptime,
1064 .alloc_inferred_comptime_mut,
1065 .make_ptr_const,
1066 .array_cat,
1067 .array_mul,
1068 .array_type,
1069 .array_type_sentinel,
1070 .vector_type,
1071 .elem_type,
1072 .indexable_ptr_elem_type,
1073 .vector_elem_type,
1074 .indexable_ptr_len,
1075 .anyframe_type,
1076 .as_node,
1077 .as_shift_operand,
1078 .bit_and,
1079 .bitcast,
1080 .bit_or,
1081 .block,
1082 .block_comptime,
1083 .block_inline,
1084 .declaration,
1085 .suspend_block,
1086 .loop,
1087 .bool_br_and,
1088 .bool_br_or,
1089 .bool_not,
1090 .call,
1091 .field_call,
1092 .cmp_lt,
1093 .cmp_lte,
1094 .cmp_eq,
1095 .cmp_gte,
1096 .cmp_gt,
1097 .cmp_neq,
1098 .error_set_decl,
1099 .error_set_decl_anon,
1100 .error_set_decl_func,
1101 .dbg_stmt,
1102 .dbg_var_ptr,
1103 .dbg_var_val,
1104 .decl_ref,
1105 .decl_val,
1106 .load,
1107 .div,
1108 .elem_ptr,
1109 .elem_val,
1110 .elem_ptr_node,
1111 .elem_val_node,
1112 .elem_val_imm,
1113 .ensure_result_used,
1114 .ensure_result_non_error,
1115 .ensure_err_union_payload_void,
1116 .@"export",
1117 .export_value,
1118 .field_ptr,
1119 .field_val,
1120 .field_ptr_named,
1121 .field_val_named,
1122 .func,
1123 .func_inferred,
1124 .func_fancy,
1125 .has_decl,
1126 .int,
1127 .int_big,
1128 .float,
1129 .float128,
1130 .int_type,
1131 .is_non_null,
1132 .is_non_null_ptr,
1133 .is_non_err,
1134 .is_non_err_ptr,
1135 .ret_is_non_err,
1136 .mod_rem,
1137 .mul,
1138 .mulwrap,
1139 .mul_sat,
1140 .ref,
1141 .shl,
1142 .shl_sat,
1143 .shr,
1144 .store_node,
1145 .store_to_inferred_ptr,
1146 .str,
1147 .sub,
1148 .subwrap,
1149 .sub_sat,
1150 .negate,
1151 .negate_wrap,
1152 .typeof,
1153 .typeof_builtin,
1154 .xor,
1155 .optional_type,
1156 .optional_payload_safe,
1157 .optional_payload_unsafe,
1158 .optional_payload_safe_ptr,
1159 .optional_payload_unsafe_ptr,
1160 .err_union_payload_unsafe,
1161 .err_union_payload_unsafe_ptr,
1162 .err_union_code,
1163 .err_union_code_ptr,
1164 .ptr_type,
1165 .enum_literal,
1166 .merge_error_sets,
1167 .error_union_type,
1168 .bit_not,
1169 .error_value,
1170 .slice_start,
1171 .slice_end,
1172 .slice_sentinel,
1173 .slice_length,
1174 .import,
1175 .typeof_log2_int_type,
1176 .resolve_inferred_alloc,
1177 .set_eval_branch_quota,
1178 .switch_block,
1179 .switch_block_ref,
1180 .switch_block_err_union,
1181 .validate_deref,
1182 .validate_destructure,
1183 .union_init,
1184 .field_type_ref,
1185 .enum_from_int,
1186 .int_from_enum,
1187 .type_info,
1188 .size_of,
1189 .bit_size_of,
1190 .int_from_ptr,
1191 .align_of,
1192 .int_from_bool,
1193 .embed_file,
1194 .error_name,
1195 .set_runtime_safety,
1196 .sqrt,
1197 .sin,
1198 .cos,
1199 .tan,
1200 .exp,
1201 .exp2,
1202 .log,
1203 .log2,
1204 .log10,
1205 .abs,
1206 .floor,
1207 .ceil,
1208 .trunc,
1209 .round,
1210 .tag_name,
1211 .type_name,
1212 .frame_type,
1213 .frame_size,
1214 .int_from_float,
1215 .float_from_int,
1216 .ptr_from_int,
1217 .float_cast,
1218 .int_cast,
1219 .ptr_cast,
1220 .truncate,
1221 .has_field,
1222 .clz,
1223 .ctz,
1224 .pop_count,
1225 .byte_swap,
1226 .bit_reverse,
1227 .div_exact,
1228 .div_floor,
1229 .div_trunc,
1230 .mod,
1231 .rem,
1232 .shl_exact,
1233 .shr_exact,
1234 .bit_offset_of,
1235 .offset_of,
1236 .splat,
1237 .reduce,
1238 .shuffle,
1239 .atomic_load,
1240 .atomic_rmw,
1241 .atomic_store,
1242 .mul_add,
1243 .builtin_call,
1244 .field_parent_ptr,
1245 .max,
1246 .memcpy,
1247 .memset,
1248 .min,
1249 .c_import,
1250 .@"resume",
1251 .@"await",
1252 .ret_err_value_code,
1253 .extended,
1254 .closure_get,
1255 .closure_capture,
1256 .ret_ptr,
1257 .ret_type,
1258 .@"try",
1259 .try_ptr,
1260 .@"defer",
1261 .defer_err_code,
1262 .save_err_ret_index,
1263 .for_len,
1264 .opt_eu_base_ptr_init,
1265 .coerce_ptr_elem_ty,
1266 .struct_init_empty,
1267 .struct_init_empty_result,
1268 .struct_init_empty_ref_result,
1269 .struct_init_anon,
1270 .struct_init,
1271 .struct_init_ref,
1272 .validate_struct_init_ty,
1273 .validate_struct_init_result_ty,
1274 .validate_ptr_struct_init,
1275 .struct_init_field_type,
1276 .struct_init_field_ptr,
1277 .array_init_anon,
1278 .array_init,
1279 .array_init_ref,
1280 .validate_array_init_ty,
1281 .validate_array_init_result_ty,
1282 .validate_array_init_ref_ty,
1283 .validate_ptr_array_init,
1284 .array_init_elem_type,
1285 .array_init_elem_ptr,
1286 .validate_ref_ty,
1287 .restore_err_ret_index_unconditional,
1288 .restore_err_ret_index_fn_entry,
1289 => false,
1290
1291 .@"break",
1292 .break_inline,
1293 .condbr,
1294 .condbr_inline,
1295 .compile_error,
1296 .ret_node,
1297 .ret_load,
1298 .ret_implicit,
1299 .ret_err_value,
1300 .@"unreachable",
1301 .repeat,
1302 .repeat_inline,
1303 .panic,
1304 .trap,
1305 .check_comptime_control_flow,
1306 => true,
1307 };
1308 }
1309
1310 pub fn isParam(tag: Tag) bool {
1311 return switch (tag) {
1312 .param,
1313 .param_comptime,
1314 .param_anytype,
1315 .param_anytype_comptime,
1316 => true,
1317
1318 else => false,
1319 };
1320 }
1321
1322 /// AstGen uses this to find out if `Ref.void_value` should be used in place
1323 /// of the result of a given instruction. This allows Sema to forego adding
1324 /// the instruction to the map after analysis.
1325 pub fn isAlwaysVoid(tag: Tag, data: Data) bool {
1326 return switch (tag) {
1327 .dbg_stmt,
1328 .dbg_var_ptr,
1329 .dbg_var_val,
1330 .ensure_result_used,
1331 .ensure_result_non_error,
1332 .ensure_err_union_payload_void,
1333 .set_eval_branch_quota,
1334 .atomic_store,
1335 .store_node,
1336 .store_to_inferred_ptr,
1337 .resolve_inferred_alloc,
1338 .validate_deref,
1339 .validate_destructure,
1340 .@"export",
1341 .export_value,
1342 .set_runtime_safety,
1343 .memcpy,
1344 .memset,
1345 .check_comptime_control_flow,
1346 .@"defer",
1347 .defer_err_code,
1348 .save_err_ret_index,
1349 .restore_err_ret_index_unconditional,
1350 .restore_err_ret_index_fn_entry,
1351 .validate_struct_init_ty,
1352 .validate_struct_init_result_ty,
1353 .validate_ptr_struct_init,
1354 .validate_array_init_ty,
1355 .validate_array_init_result_ty,
1356 .validate_ptr_array_init,
1357 .validate_ref_ty,
1358 => true,
1359
1360 .param,
1361 .param_comptime,
1362 .param_anytype,
1363 .param_anytype_comptime,
1364 .add,
1365 .addwrap,
1366 .add_sat,
1367 .add_unsafe,
1368 .alloc,
1369 .alloc_mut,
1370 .alloc_comptime_mut,
1371 .alloc_inferred,
1372 .alloc_inferred_mut,
1373 .alloc_inferred_comptime,
1374 .alloc_inferred_comptime_mut,
1375 .make_ptr_const,
1376 .array_cat,
1377 .array_mul,
1378 .array_type,
1379 .array_type_sentinel,
1380 .vector_type,
1381 .elem_type,
1382 .indexable_ptr_elem_type,
1383 .vector_elem_type,
1384 .indexable_ptr_len,
1385 .anyframe_type,
1386 .as_node,
1387 .as_shift_operand,
1388 .bit_and,
1389 .bitcast,
1390 .bit_or,
1391 .block,
1392 .block_comptime,
1393 .block_inline,
1394 .declaration,
1395 .suspend_block,
1396 .loop,
1397 .bool_br_and,
1398 .bool_br_or,
1399 .bool_not,
1400 .call,
1401 .field_call,
1402 .cmp_lt,
1403 .cmp_lte,
1404 .cmp_eq,
1405 .cmp_gte,
1406 .cmp_gt,
1407 .cmp_neq,
1408 .error_set_decl,
1409 .error_set_decl_anon,
1410 .error_set_decl_func,
1411 .decl_ref,
1412 .decl_val,
1413 .load,
1414 .div,
1415 .elem_ptr,
1416 .elem_val,
1417 .elem_ptr_node,
1418 .elem_val_node,
1419 .elem_val_imm,
1420 .field_ptr,
1421 .field_val,
1422 .field_ptr_named,
1423 .field_val_named,
1424 .func,
1425 .func_inferred,
1426 .func_fancy,
1427 .has_decl,
1428 .int,
1429 .int_big,
1430 .float,
1431 .float128,
1432 .int_type,
1433 .is_non_null,
1434 .is_non_null_ptr,
1435 .is_non_err,
1436 .is_non_err_ptr,
1437 .ret_is_non_err,
1438 .mod_rem,
1439 .mul,
1440 .mulwrap,
1441 .mul_sat,
1442 .ref,
1443 .shl,
1444 .shl_sat,
1445 .shr,
1446 .str,
1447 .sub,
1448 .subwrap,
1449 .sub_sat,
1450 .negate,
1451 .negate_wrap,
1452 .typeof,
1453 .typeof_builtin,
1454 .xor,
1455 .optional_type,
1456 .optional_payload_safe,
1457 .optional_payload_unsafe,
1458 .optional_payload_safe_ptr,
1459 .optional_payload_unsafe_ptr,
1460 .err_union_payload_unsafe,
1461 .err_union_payload_unsafe_ptr,
1462 .err_union_code,
1463 .err_union_code_ptr,
1464 .ptr_type,
1465 .enum_literal,
1466 .merge_error_sets,
1467 .error_union_type,
1468 .bit_not,
1469 .error_value,
1470 .slice_start,
1471 .slice_end,
1472 .slice_sentinel,
1473 .slice_length,
1474 .import,
1475 .typeof_log2_int_type,
1476 .switch_block,
1477 .switch_block_ref,
1478 .switch_block_err_union,
1479 .union_init,
1480 .field_type_ref,
1481 .enum_from_int,
1482 .int_from_enum,
1483 .type_info,
1484 .size_of,
1485 .bit_size_of,
1486 .int_from_ptr,
1487 .align_of,
1488 .int_from_bool,
1489 .embed_file,
1490 .error_name,
1491 .sqrt,
1492 .sin,
1493 .cos,
1494 .tan,
1495 .exp,
1496 .exp2,
1497 .log,
1498 .log2,
1499 .log10,
1500 .abs,
1501 .floor,
1502 .ceil,
1503 .trunc,
1504 .round,
1505 .tag_name,
1506 .type_name,
1507 .frame_type,
1508 .frame_size,
1509 .int_from_float,
1510 .float_from_int,
1511 .ptr_from_int,
1512 .float_cast,
1513 .int_cast,
1514 .ptr_cast,
1515 .truncate,
1516 .has_field,
1517 .clz,
1518 .ctz,
1519 .pop_count,
1520 .byte_swap,
1521 .bit_reverse,
1522 .div_exact,
1523 .div_floor,
1524 .div_trunc,
1525 .mod,
1526 .rem,
1527 .shl_exact,
1528 .shr_exact,
1529 .bit_offset_of,
1530 .offset_of,
1531 .splat,
1532 .reduce,
1533 .shuffle,
1534 .atomic_load,
1535 .atomic_rmw,
1536 .mul_add,
1537 .builtin_call,
1538 .field_parent_ptr,
1539 .max,
1540 .min,
1541 .c_import,
1542 .@"resume",
1543 .@"await",
1544 .ret_err_value_code,
1545 .closure_get,
1546 .closure_capture,
1547 .@"break",
1548 .break_inline,
1549 .condbr,
1550 .condbr_inline,
1551 .compile_error,
1552 .ret_node,
1553 .ret_load,
1554 .ret_implicit,
1555 .ret_err_value,
1556 .ret_ptr,
1557 .ret_type,
1558 .@"unreachable",
1559 .repeat,
1560 .repeat_inline,
1561 .panic,
1562 .trap,
1563 .for_len,
1564 .@"try",
1565 .try_ptr,
1566 .opt_eu_base_ptr_init,
1567 .coerce_ptr_elem_ty,
1568 .struct_init_empty,
1569 .struct_init_empty_result,
1570 .struct_init_empty_ref_result,
1571 .struct_init_anon,
1572 .struct_init,
1573 .struct_init_ref,
1574 .struct_init_field_type,
1575 .struct_init_field_ptr,
1576 .array_init_anon,
1577 .array_init,
1578 .array_init_ref,
1579 .validate_array_init_ref_ty,
1580 .array_init_elem_type,
1581 .array_init_elem_ptr,
1582 => false,
1583
1584 .extended => switch (data.extended.opcode) {
1585 .fence, .set_cold, .breakpoint => true,
1586 else => false,
1587 },
1588 };
1589 }
1590
1591 /// Used by debug safety-checking code.
1592 pub const data_tags = list: {
1593 @setEvalBranchQuota(2000);
1594 break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{
1595 .add = .pl_node,
1596 .addwrap = .pl_node,
1597 .add_sat = .pl_node,
1598 .add_unsafe = .pl_node,
1599 .sub = .pl_node,
1600 .subwrap = .pl_node,
1601 .sub_sat = .pl_node,
1602 .mul = .pl_node,
1603 .mulwrap = .pl_node,
1604 .mul_sat = .pl_node,
1605
1606 .param = .pl_tok,
1607 .param_comptime = .pl_tok,
1608 .param_anytype = .str_tok,
1609 .param_anytype_comptime = .str_tok,
1610 .array_cat = .pl_node,
1611 .array_mul = .pl_node,
1612 .array_type = .pl_node,
1613 .array_type_sentinel = .pl_node,
1614 .vector_type = .pl_node,
1615 .elem_type = .un_node,
1616 .indexable_ptr_elem_type = .un_node,
1617 .vector_elem_type = .un_node,
1618 .indexable_ptr_len = .un_node,
1619 .anyframe_type = .un_node,
1620 .as_node = .pl_node,
1621 .as_shift_operand = .pl_node,
1622 .bit_and = .pl_node,
1623 .bitcast = .pl_node,
1624 .bit_not = .un_node,
1625 .bit_or = .pl_node,
1626 .block = .pl_node,
1627 .block_comptime = .pl_node,
1628 .block_inline = .pl_node,
1629 .declaration = .pl_node,
1630 .suspend_block = .pl_node,
1631 .bool_not = .un_node,
1632 .bool_br_and = .pl_node,
1633 .bool_br_or = .pl_node,
1634 .@"break" = .@"break",
1635 .break_inline = .@"break",
1636 .check_comptime_control_flow = .un_node,
1637 .for_len = .pl_node,
1638 .call = .pl_node,
1639 .field_call = .pl_node,
1640 .cmp_lt = .pl_node,
1641 .cmp_lte = .pl_node,
1642 .cmp_eq = .pl_node,
1643 .cmp_gte = .pl_node,
1644 .cmp_gt = .pl_node,
1645 .cmp_neq = .pl_node,
1646 .condbr = .pl_node,
1647 .condbr_inline = .pl_node,
1648 .@"try" = .pl_node,
1649 .try_ptr = .pl_node,
1650 .error_set_decl = .pl_node,
1651 .error_set_decl_anon = .pl_node,
1652 .error_set_decl_func = .pl_node,
1653 .dbg_stmt = .dbg_stmt,
1654 .dbg_var_ptr = .str_op,
1655 .dbg_var_val = .str_op,
1656 .decl_ref = .str_tok,
1657 .decl_val = .str_tok,
1658 .load = .un_node,
1659 .div = .pl_node,
1660 .elem_ptr = .pl_node,
1661 .elem_ptr_node = .pl_node,
1662 .elem_val = .pl_node,
1663 .elem_val_node = .pl_node,
1664 .elem_val_imm = .elem_val_imm,
1665 .ensure_result_used = .un_node,
1666 .ensure_result_non_error = .un_node,
1667 .ensure_err_union_payload_void = .un_node,
1668 .error_union_type = .pl_node,
1669 .error_value = .str_tok,
1670 .@"export" = .pl_node,
1671 .export_value = .pl_node,
1672 .field_ptr = .pl_node,
1673 .field_val = .pl_node,
1674 .field_ptr_named = .pl_node,
1675 .field_val_named = .pl_node,
1676 .func = .pl_node,
1677 .func_inferred = .pl_node,
1678 .func_fancy = .pl_node,
1679 .import = .str_tok,
1680 .int = .int,
1681 .int_big = .str,
1682 .float = .float,
1683 .float128 = .pl_node,
1684 .int_type = .int_type,
1685 .is_non_null = .un_node,
1686 .is_non_null_ptr = .un_node,
1687 .is_non_err = .un_node,
1688 .is_non_err_ptr = .un_node,
1689 .ret_is_non_err = .un_node,
1690 .loop = .pl_node,
1691 .repeat = .node,
1692 .repeat_inline = .node,
1693 .merge_error_sets = .pl_node,
1694 .mod_rem = .pl_node,
1695 .ref = .un_tok,
1696 .ret_node = .un_node,
1697 .ret_load = .un_node,
1698 .ret_implicit = .un_tok,
1699 .ret_err_value = .str_tok,
1700 .ret_err_value_code = .str_tok,
1701 .ret_ptr = .node,
1702 .ret_type = .node,
1703 .ptr_type = .ptr_type,
1704 .slice_start = .pl_node,
1705 .slice_end = .pl_node,
1706 .slice_sentinel = .pl_node,
1707 .slice_length = .pl_node,
1708 .store_node = .pl_node,
1709 .store_to_inferred_ptr = .pl_node,
1710 .str = .str,
1711 .negate = .un_node,
1712 .negate_wrap = .un_node,
1713 .typeof = .un_node,
1714 .typeof_log2_int_type = .un_node,
1715 .@"unreachable" = .@"unreachable",
1716 .xor = .pl_node,
1717 .optional_type = .un_node,
1718 .optional_payload_safe = .un_node,
1719 .optional_payload_unsafe = .un_node,
1720 .optional_payload_safe_ptr = .un_node,
1721 .optional_payload_unsafe_ptr = .un_node,
1722 .err_union_payload_unsafe = .un_node,
1723 .err_union_payload_unsafe_ptr = .un_node,
1724 .err_union_code = .un_node,
1725 .err_union_code_ptr = .un_node,
1726 .enum_literal = .str_tok,
1727 .switch_block = .pl_node,
1728 .switch_block_ref = .pl_node,
1729 .switch_block_err_union = .pl_node,
1730 .validate_deref = .un_node,
1731 .validate_destructure = .pl_node,
1732 .field_type_ref = .pl_node,
1733 .union_init = .pl_node,
1734 .type_info = .un_node,
1735 .size_of = .un_node,
1736 .bit_size_of = .un_node,
1737 .opt_eu_base_ptr_init = .un_node,
1738 .coerce_ptr_elem_ty = .pl_node,
1739 .validate_ref_ty = .un_tok,
1740
1741 .int_from_ptr = .un_node,
1742 .compile_error = .un_node,
1743 .set_eval_branch_quota = .un_node,
1744 .int_from_enum = .un_node,
1745 .align_of = .un_node,
1746 .int_from_bool = .un_node,
1747 .embed_file = .un_node,
1748 .error_name = .un_node,
1749 .panic = .un_node,
1750 .trap = .node,
1751 .set_runtime_safety = .un_node,
1752 .sqrt = .un_node,
1753 .sin = .un_node,
1754 .cos = .un_node,
1755 .tan = .un_node,
1756 .exp = .un_node,
1757 .exp2 = .un_node,
1758 .log = .un_node,
1759 .log2 = .un_node,
1760 .log10 = .un_node,
1761 .abs = .un_node,
1762 .floor = .un_node,
1763 .ceil = .un_node,
1764 .trunc = .un_node,
1765 .round = .un_node,
1766 .tag_name = .un_node,
1767 .type_name = .un_node,
1768 .frame_type = .un_node,
1769 .frame_size = .un_node,
1770
1771 .int_from_float = .pl_node,
1772 .float_from_int = .pl_node,
1773 .ptr_from_int = .pl_node,
1774 .enum_from_int = .pl_node,
1775 .float_cast = .pl_node,
1776 .int_cast = .pl_node,
1777 .ptr_cast = .pl_node,
1778 .truncate = .pl_node,
1779 .typeof_builtin = .pl_node,
1780
1781 .has_decl = .pl_node,
1782 .has_field = .pl_node,
1783
1784 .clz = .un_node,
1785 .ctz = .un_node,
1786 .pop_count = .un_node,
1787 .byte_swap = .un_node,
1788 .bit_reverse = .un_node,
1789
1790 .div_exact = .pl_node,
1791 .div_floor = .pl_node,
1792 .div_trunc = .pl_node,
1793 .mod = .pl_node,
1794 .rem = .pl_node,
1795
1796 .shl = .pl_node,
1797 .shl_exact = .pl_node,
1798 .shl_sat = .pl_node,
1799 .shr = .pl_node,
1800 .shr_exact = .pl_node,
1801
1802 .bit_offset_of = .pl_node,
1803 .offset_of = .pl_node,
1804 .splat = .pl_node,
1805 .reduce = .pl_node,
1806 .shuffle = .pl_node,
1807 .atomic_load = .pl_node,
1808 .atomic_rmw = .pl_node,
1809 .atomic_store = .pl_node,
1810 .mul_add = .pl_node,
1811 .builtin_call = .pl_node,
1812 .field_parent_ptr = .pl_node,
1813 .max = .pl_node,
1814 .memcpy = .pl_node,
1815 .memset = .pl_node,
1816 .min = .pl_node,
1817 .c_import = .pl_node,
1818
1819 .alloc = .un_node,
1820 .alloc_mut = .un_node,
1821 .alloc_comptime_mut = .un_node,
1822 .alloc_inferred = .node,
1823 .alloc_inferred_mut = .node,
1824 .alloc_inferred_comptime = .node,
1825 .alloc_inferred_comptime_mut = .node,
1826 .resolve_inferred_alloc = .un_node,
1827 .make_ptr_const = .un_node,
1828
1829 .@"resume" = .un_node,
1830 .@"await" = .un_node,
1831
1832 .closure_capture = .un_tok,
1833 .closure_get = .inst_node,
1834
1835 .@"defer" = .@"defer",
1836 .defer_err_code = .defer_err_code,
1837
1838 .save_err_ret_index = .save_err_ret_index,
1839 .restore_err_ret_index_unconditional = .un_node,
1840 .restore_err_ret_index_fn_entry = .un_node,
1841
1842 .struct_init_empty = .un_node,
1843 .struct_init_empty_result = .un_node,
1844 .struct_init_empty_ref_result = .un_node,
1845 .struct_init_anon = .pl_node,
1846 .struct_init = .pl_node,
1847 .struct_init_ref = .pl_node,
1848 .validate_struct_init_ty = .un_node,
1849 .validate_struct_init_result_ty = .un_node,
1850 .validate_ptr_struct_init = .pl_node,
1851 .struct_init_field_type = .pl_node,
1852 .struct_init_field_ptr = .pl_node,
1853 .array_init_anon = .pl_node,
1854 .array_init = .pl_node,
1855 .array_init_ref = .pl_node,
1856 .validate_array_init_ty = .pl_node,
1857 .validate_array_init_result_ty = .pl_node,
1858 .validate_array_init_ref_ty = .pl_node,
1859 .validate_ptr_array_init = .pl_node,
1860 .array_init_elem_type = .bin,
1861 .array_init_elem_ptr = .pl_node,
1862
1863 .extended = .extended,
1864 });
1865 };
1866
1867 // Uncomment to view how many tag slots are available.
1868 //comptime {
1869 // @compileLog("ZIR tags left: ", 256 - @typeInfo(Tag).Enum.fields.len);
1870 //}
1871 };
1872
1873 /// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum.
1874 /// `noreturn` instructions may not go here; they must be part of the main `Tag` enum.
1875 pub const Extended = enum(u16) {
1876 /// Declares a global variable.
1877 /// `operand` is payload index to `ExtendedVar`.
1878 /// `small` is `ExtendedVar.Small`.
1879 variable,
1880 /// A struct type definition. Contains references to ZIR instructions for
1881 /// the field types, defaults, and alignments.
1882 /// `operand` is payload index to `StructDecl`.
1883 /// `small` is `StructDecl.Small`.
1884 struct_decl,
1885 /// An enum type definition. Contains references to ZIR instructions for
1886 /// the field value expressions and optional type tag expression.
1887 /// `operand` is payload index to `EnumDecl`.
1888 /// `small` is `EnumDecl.Small`.
1889 enum_decl,
1890 /// A union type definition. Contains references to ZIR instructions for
1891 /// the field types and optional type tag expression.
1892 /// `operand` is payload index to `UnionDecl`.
1893 /// `small` is `UnionDecl.Small`.
1894 union_decl,
1895 /// An opaque type definition. Contains references to decls and captures.
1896 /// `operand` is payload index to `OpaqueDecl`.
1897 /// `small` is `OpaqueDecl.Small`.
1898 opaque_decl,
1899 /// Implements the `@This` builtin.
1900 /// `operand` is `src_node: i32`.
1901 this,
1902 /// Implements the `@returnAddress` builtin.
1903 /// `operand` is `src_node: i32`.
1904 ret_addr,
1905 /// Implements the `@src` builtin.
1906 /// `operand` is payload index to `LineColumn`.
1907 builtin_src,
1908 /// Implements the `@errorReturnTrace` builtin.
1909 /// `operand` is `src_node: i32`.
1910 error_return_trace,
1911 /// Implements the `@frame` builtin.
1912 /// `operand` is `src_node: i32`.
1913 frame,
1914 /// Implements the `@frameAddress` builtin.
1915 /// `operand` is `src_node: i32`.
1916 frame_address,
1917 /// Same as `alloc` from `Tag` but may contain an alignment instruction.
1918 /// `operand` is payload index to `AllocExtended`.
1919 /// `small`:
1920 /// * 0b000X - has type
1921 /// * 0b00X0 - has alignment
1922 /// * 0b0X00 - 1=const, 0=var
1923 /// * 0bX000 - is comptime
1924 alloc,
1925 /// The `@extern` builtin.
1926 /// `operand` is payload index to `BinNode`.
1927 builtin_extern,
1928 /// Inline assembly.
1929 /// `small`:
1930 /// * 0b00000000_000XXXXX - `outputs_len`.
1931 /// * 0b000000XX_XXX00000 - `inputs_len`.
1932 /// * 0b0XXXXX00_00000000 - `clobbers_len`.
1933 /// * 0bX0000000_00000000 - is volatile
1934 /// `operand` is payload index to `Asm`.
1935 @"asm",
1936 /// Same as `asm` except the assembly template is not a string literal but a comptime
1937 /// expression.
1938 /// The `asm_source` field of the Asm is not a null-terminated string
1939 /// but instead a Ref.
1940 asm_expr,
1941 /// Log compile time variables and emit an error message.
1942 /// `operand` is payload index to `NodeMultiOp`.
1943 /// `small` is `operands_len`.
1944 /// The AST node is the compile log builtin call.
1945 compile_log,
1946 /// The builtin `@TypeOf` which returns the type after Peer Type Resolution
1947 /// of one or more params.
1948 /// `operand` is payload index to `TypeOfPeer`.
1949 /// `small` is `operands_len`.
1950 /// The AST node is the builtin call.
1951 typeof_peer,
1952 /// Implements the `@min` builtin for more than 2 args.
1953 /// `operand` is payload index to `NodeMultiOp`.
1954 /// `small` is `operands_len`.
1955 /// The AST node is the builtin call.
1956 min_multi,
1957 /// Implements the `@max` builtin for more than 2 args.
1958 /// `operand` is payload index to `NodeMultiOp`.
1959 /// `small` is `operands_len`.
1960 /// The AST node is the builtin call.
1961 max_multi,
1962 /// Implements the `@addWithOverflow` builtin.
1963 /// `operand` is payload index to `BinNode`.
1964 /// `small` is unused.
1965 add_with_overflow,
1966 /// Implements the `@subWithOverflow` builtin.
1967 /// `operand` is payload index to `BinNode`.
1968 /// `small` is unused.
1969 sub_with_overflow,
1970 /// Implements the `@mulWithOverflow` builtin.
1971 /// `operand` is payload index to `BinNode`.
1972 /// `small` is unused.
1973 mul_with_overflow,
1974 /// Implements the `@shlWithOverflow` builtin.
1975 /// `operand` is payload index to `BinNode`.
1976 /// `small` is unused.
1977 shl_with_overflow,
1978 /// `operand` is payload index to `UnNode`.
1979 c_undef,
1980 /// `operand` is payload index to `UnNode`.
1981 c_include,
1982 /// `operand` is payload index to `BinNode`.
1983 c_define,
1984 /// `operand` is payload index to `UnNode`.
1985 wasm_memory_size,
1986 /// `operand` is payload index to `BinNode`.
1987 wasm_memory_grow,
1988 /// The `@prefetch` builtin.
1989 /// `operand` is payload index to `BinNode`.
1990 prefetch,
1991 /// Implements the `@fence` builtin.
1992 /// `operand` is payload index to `UnNode`.
1993 fence,
1994 /// Implement builtin `@setFloatMode`.
1995 /// `operand` is payload index to `UnNode`.
1996 set_float_mode,
1997 /// Implement builtin `@setAlignStack`.
1998 /// `operand` is payload index to `UnNode`.
1999 set_align_stack,
2000 /// Implements `@setCold`.
2001 /// `operand` is payload index to `UnNode`.
2002 set_cold,
2003 /// Implements the `@errorCast` builtin.
2004 /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
2005 error_cast,
2006 /// `operand` is payload index to `UnNode`.
2007 await_nosuspend,
2008 /// Implements `@breakpoint`.
2009 /// `operand` is `src_node: i32`.
2010 breakpoint,
2011 /// Implements the `@select` builtin.
2012 /// `operand` is payload index to `Select`.
2013 select,
2014 /// Implement builtin `@errToInt`.
2015 /// `operand` is payload index to `UnNode`.
2016 int_from_error,
2017 /// Implement builtin `@errorFromInt`.
2018 /// `operand` is payload index to `UnNode`.
2019 error_from_int,
2020 /// Implement builtin `@Type`.
2021 /// `operand` is payload index to `UnNode`.
2022 /// `small` contains `NameStrategy`.
2023 reify,
2024 /// Implements the `@asyncCall` builtin.
2025 /// `operand` is payload index to `AsyncCall`.
2026 builtin_async_call,
2027 /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins.
2028 /// `small` 0=>weak 1=>strong
2029 /// `operand` is payload index to `Cmpxchg`.
2030 cmpxchg,
2031 /// Implement builtin `@cVaArg`.
2032 /// `operand` is payload index to `BinNode`.
2033 c_va_arg,
2034 /// Implement builtin `@cVaCopy`.
2035 /// `operand` is payload index to `UnNode`.
2036 c_va_copy,
2037 /// Implement builtin `@cVaEnd`.
2038 /// `operand` is payload index to `UnNode`.
2039 c_va_end,
2040 /// Implement builtin `@cVaStart`.
2041 /// `operand` is `src_node: i32`.
2042 c_va_start,
2043 /// Implements the following builtins:
2044 /// `@ptrCast`, `@alignCast`, `@addrSpaceCast`, `@constCast`, `@volatileCast`.
2045 /// Represents an arbitrary nesting of the above builtins. Such a nesting is treated as a
2046 /// single operation which can modify multiple components of a pointer type.
2047 /// `operand` is payload index to `BinNode`.
2048 /// `small` contains `FullPtrCastFlags`.
2049 /// AST node is the root of the nested casts.
2050 /// `lhs` is dest type, `rhs` is operand.
2051 ptr_cast_full,
2052 /// `operand` is payload index to `UnNode`.
2053 /// `small` contains `FullPtrCastFlags`.
2054 /// Guaranteed to only have flags where no explicit destination type is
2055 /// required (const_cast and volatile_cast).
2056 /// AST node is the root of the nested casts.
2057 ptr_cast_no_dest,
2058 /// Implements the `@workItemId` builtin.
2059 /// `operand` is payload index to `UnNode`.
2060 work_item_id,
2061 /// Implements the `@workGroupSize` builtin.
2062 /// `operand` is payload index to `UnNode`.
2063 work_group_size,
2064 /// Implements the `@workGroupId` builtin.
2065 /// `operand` is payload index to `UnNode`.
2066 work_group_id,
2067 /// Implements the `@inComptime` builtin.
2068 /// `operand` is `src_node: i32`.
2069 in_comptime,
2070 /// Restores the error return index to its last saved state in a given
2071 /// block. If the block is `.none`, restores to the state from the point
2072 /// of function entry. If the operand is not `.none`, the restore is
2073 /// conditional on the operand value not being an error.
2074 /// `operand` is payload index to `RestoreErrRetIndex`.
2075 /// `small` is undefined.
2076 restore_err_ret_index,
2077 /// Used as a placeholder instruction which is just a dummy index for Sema to replace
2078 /// with a specific value. For instance, this is used for the capture of an `errdefer`.
2079 /// This should never appear in a body.
2080 value_placeholder,
2081
2082 pub const InstData = struct {
2083 opcode: Extended,
2084 small: u16,
2085 operand: u32,
2086 };
2087 };
2088
2089 /// The position of a ZIR instruction within the `Zir` instructions array.
2090 pub const Index = enum(u32) {
2091 /// ZIR is structured so that the outermost "main" struct of any file
2092 /// is always at index 0.
2093 main_struct_inst = 0,
2094 ref_start_index = static_len,
2095 _,
2096
2097 pub const static_len = 84;
2098
2099 pub fn toRef(i: Index) Inst.Ref {
2100 return @enumFromInt(@intFromEnum(Index.ref_start_index) + @intFromEnum(i));
2101 }
2102
2103 pub fn toOptional(i: Index) OptionalIndex {
2104 return @enumFromInt(@intFromEnum(i));
2105 }
2106 };
2107
2108 pub const OptionalIndex = enum(u32) {
2109 /// ZIR is structured so that the outermost "main" struct of any file
2110 /// is always at index 0.
2111 main_struct_inst = 0,
2112 ref_start_index = Index.static_len,
2113 none = std.math.maxInt(u32),
2114 _,
2115
2116 pub fn unwrap(oi: OptionalIndex) ?Index {
2117 return if (oi == .none) null else @enumFromInt(@intFromEnum(oi));
2118 }
2119 };
2120
2121 /// A reference to ZIR instruction, or to an InternPool index, or neither.
2122 ///
2123 /// If the integer tag value is < InternPool.static_len, then it
2124 /// corresponds to an InternPool index. Otherwise, this refers to a ZIR
2125 /// instruction.
2126 ///
2127 /// The tag type is specified so that it is safe to bitcast between `[]u32`
2128 /// and `[]Ref`.
2129 pub const Ref = enum(u32) {
2130 u0_type,
2131 i0_type,
2132 u1_type,
2133 u8_type,
2134 i8_type,
2135 u16_type,
2136 i16_type,
2137 u29_type,
2138 u32_type,
2139 i32_type,
2140 u64_type,
2141 i64_type,
2142 u80_type,
2143 u128_type,
2144 i128_type,
2145 usize_type,
2146 isize_type,
2147 c_char_type,
2148 c_short_type,
2149 c_ushort_type,
2150 c_int_type,
2151 c_uint_type,
2152 c_long_type,
2153 c_ulong_type,
2154 c_longlong_type,
2155 c_ulonglong_type,
2156 c_longdouble_type,
2157 f16_type,
2158 f32_type,
2159 f64_type,
2160 f80_type,
2161 f128_type,
2162 anyopaque_type,
2163 bool_type,
2164 void_type,
2165 type_type,
2166 anyerror_type,
2167 comptime_int_type,
2168 comptime_float_type,
2169 noreturn_type,
2170 anyframe_type,
2171 null_type,
2172 undefined_type,
2173 enum_literal_type,
2174 atomic_order_type,
2175 atomic_rmw_op_type,
2176 calling_convention_type,
2177 address_space_type,
2178 float_mode_type,
2179 reduce_op_type,
2180 call_modifier_type,
2181 prefetch_options_type,
2182 export_options_type,
2183 extern_options_type,
2184 type_info_type,
2185 manyptr_u8_type,
2186 manyptr_const_u8_type,
2187 manyptr_const_u8_sentinel_0_type,
2188 single_const_pointer_to_comptime_int_type,
2189 slice_const_u8_type,
2190 slice_const_u8_sentinel_0_type,
2191 optional_noreturn_type,
2192 anyerror_void_error_union_type,
2193 adhoc_inferred_error_set_type,
2194 generic_poison_type,
2195 empty_struct_type,
2196 undef,
2197 zero,
2198 zero_usize,
2199 zero_u8,
2200 one,
2201 one_usize,
2202 one_u8,
2203 four_u8,
2204 negative_one,
2205 calling_convention_c,
2206 calling_convention_inline,
2207 void_value,
2208 unreachable_value,
2209 null_value,
2210 bool_true,
2211 bool_false,
2212 empty_struct,
2213 generic_poison,
2214
2215 /// This tag is here to match Air and InternPool, however it is unused
2216 /// for ZIR purposes.
2217 var_args_param_type = std.math.maxInt(u32) - 1,
2218 /// This Ref does not correspond to any ZIR instruction or constant
2219 /// value and may instead be used as a sentinel to indicate null.
2220 none = std.math.maxInt(u32),
2221
2222 _,
2223
2224 pub fn toIndex(inst: Ref) ?Index {
2225 assert(inst != .none);
2226 const ref_int = @intFromEnum(inst);
2227 if (ref_int >= @intFromEnum(Index.ref_start_index)) {
2228 return @enumFromInt(ref_int - @intFromEnum(Index.ref_start_index));
2229 } else {
2230 return null;
2231 }
2232 }
2233
2234 pub fn toIndexAllowNone(inst: Ref) ?Index {
2235 if (inst == .none) return null;
2236 return toIndex(inst);
2237 }
2238 };
2239
2240 /// All instructions have an 8-byte payload, which is contained within
2241 /// this union. `Tag` determines which union field is active, as well as
2242 /// how to interpret the data within.
2243 pub const Data = union {
2244 /// Used for `Tag.extended`. The extended opcode determines the meaning
2245 /// of the `small` and `operand` fields.
2246 extended: Extended.InstData,
2247 /// Used for unary operators, with an AST node source location.
2248 un_node: struct {
2249 /// Offset from Decl AST node index.
2250 src_node: i32,
2251 /// The meaning of this operand depends on the corresponding `Tag`.
2252 operand: Ref,
2253
2254 pub fn src(self: @This()) LazySrcLoc {
2255 return LazySrcLoc.nodeOffset(self.src_node);
2256 }
2257 },
2258 /// Used for unary operators, with a token source location.
2259 un_tok: struct {
2260 /// Offset from Decl AST token index.
2261 src_tok: Ast.TokenIndex,
2262 /// The meaning of this operand depends on the corresponding `Tag`.
2263 operand: Ref,
2264
2265 pub fn src(self: @This()) LazySrcLoc {
2266 return .{ .token_offset = self.src_tok };
2267 }
2268 },
2269 pl_node: struct {
2270 /// Offset from Decl AST node index.
2271 /// `Tag` determines which kind of AST node this points to.
2272 src_node: i32,
2273 /// index into extra.
2274 /// `Tag` determines what lives there.
2275 payload_index: u32,
2276
2277 pub fn src(self: @This()) LazySrcLoc {
2278 return LazySrcLoc.nodeOffset(self.src_node);
2279 }
2280 },
2281 pl_tok: struct {
2282 /// Offset from Decl AST token index.
2283 src_tok: Ast.TokenIndex,
2284 /// index into extra.
2285 /// `Tag` determines what lives there.
2286 payload_index: u32,
2287
2288 pub fn src(self: @This()) LazySrcLoc {
2289 return .{ .token_offset = self.src_tok };
2290 }
2291 },
2292 bin: Bin,
2293 /// For strings which may contain null bytes.
2294 str: struct {
2295 /// Offset into `string_bytes`.
2296 start: NullTerminatedString,
2297 /// Number of bytes in the string.
2298 len: u32,
2299
2300 pub fn get(self: @This(), code: Zir) []const u8 {
2301 return code.string_bytes[@intFromEnum(self.start)..][0..self.len];
2302 }
2303 },
2304 str_tok: struct {
2305 /// Offset into `string_bytes`. Null-terminated.
2306 start: NullTerminatedString,
2307 /// Offset from Decl AST token index.
2308 src_tok: u32,
2309
2310 pub fn get(self: @This(), code: Zir) [:0]const u8 {
2311 return code.nullTerminatedString(self.start);
2312 }
2313
2314 pub fn src(self: @This()) LazySrcLoc {
2315 return .{ .token_offset = self.src_tok };
2316 }
2317 },
2318 /// Offset from Decl AST token index.
2319 tok: Ast.TokenIndex,
2320 /// Offset from Decl AST node index.
2321 node: i32,
2322 int: u64,
2323 float: f64,
2324 ptr_type: struct {
2325 flags: packed struct {
2326 is_allowzero: bool,
2327 is_mutable: bool,
2328 is_volatile: bool,
2329 has_sentinel: bool,
2330 has_align: bool,
2331 has_addrspace: bool,
2332 has_bit_range: bool,
2333 _: u1 = undefined,
2334 },
2335 size: std.builtin.Type.Pointer.Size,
2336 /// Index into extra. See `PtrType`.
2337 payload_index: u32,
2338 },
2339 int_type: struct {
2340 /// Offset from Decl AST node index.
2341 /// `Tag` determines which kind of AST node this points to.
2342 src_node: i32,
2343 signedness: std.builtin.Signedness,
2344 bit_count: u16,
2345
2346 pub fn src(self: @This()) LazySrcLoc {
2347 return LazySrcLoc.nodeOffset(self.src_node);
2348 }
2349 },
2350 @"unreachable": struct {
2351 /// Offset from Decl AST node index.
2352 /// `Tag` determines which kind of AST node this points to.
2353 src_node: i32,
2354
2355 pub fn src(self: @This()) LazySrcLoc {
2356 return LazySrcLoc.nodeOffset(self.src_node);
2357 }
2358 },
2359 @"break": struct {
2360 operand: Ref,
2361 payload_index: u32,
2362 },
2363 dbg_stmt: LineColumn,
2364 /// Used for unary operators which reference an inst,
2365 /// with an AST node source location.
2366 inst_node: struct {
2367 /// Offset from Decl AST node index.
2368 src_node: i32,
2369 /// The meaning of this operand depends on the corresponding `Tag`.
2370 inst: Index,
2371
2372 pub fn src(self: @This()) LazySrcLoc {
2373 return LazySrcLoc.nodeOffset(self.src_node);
2374 }
2375 },
2376 str_op: struct {
2377 /// Offset into `string_bytes`. Null-terminated.
2378 str: NullTerminatedString,
2379 operand: Ref,
2380
2381 pub fn getStr(self: @This(), zir: Zir) [:0]const u8 {
2382 return zir.nullTerminatedString(self.str);
2383 }
2384 },
2385 @"defer": struct {
2386 index: u32,
2387 len: u32,
2388 },
2389 defer_err_code: struct {
2390 err_code: Ref,
2391 payload_index: u32,
2392 },
2393 save_err_ret_index: struct {
2394 operand: Ref, // If error type (or .none), save new trace index
2395 },
2396 elem_val_imm: struct {
2397 /// The indexable value being accessed.
2398 operand: Ref,
2399 /// The index being accessed.
2400 idx: u32,
2401 },
2402
2403 // Make sure we don't accidentally add a field to make this union
2404 // bigger than expected. Note that in Debug builds, Zig is allowed
2405 // to insert a secret field for safety checks.
2406 comptime {
2407 if (builtin.mode != .Debug and builtin.mode != .ReleaseSafe) {
2408 assert(@sizeOf(Data) == 8);
2409 }
2410 }
2411
2412 /// TODO this has to be kept in sync with `Data` which we want to be an untagged
2413 /// union. There is some kind of language awkwardness here and it has to do with
2414 /// deserializing an untagged union (in this case `Data`) from a file, and trying
2415 /// to preserve the hidden safety field.
2416 pub const FieldEnum = enum {
2417 extended,
2418 un_node,
2419 un_tok,
2420 pl_node,
2421 pl_tok,
2422 bin,
2423 str,
2424 str_tok,
2425 tok,
2426 node,
2427 int,
2428 float,
2429 ptr_type,
2430 int_type,
2431 @"unreachable",
2432 @"break",
2433 dbg_stmt,
2434 inst_node,
2435 str_op,
2436 @"defer",
2437 defer_err_code,
2438 save_err_ret_index,
2439 elem_val_imm,
2440 };
2441 };
2442
2443 pub const Break = struct {
2444 pub const no_src_node = std.math.maxInt(i32);
2445
2446 operand_src_node: i32,
2447 block_inst: Index,
2448 };
2449
2450 /// Trailing:
2451 /// 0. Output for every outputs_len
2452 /// 1. Input for every inputs_len
2453 /// 2. clobber: NullTerminatedString // index into string_bytes (null terminated) for every clobbers_len.
2454 pub const Asm = struct {
2455 src_node: i32,
2456 // null-terminated string index
2457 asm_source: NullTerminatedString,
2458 /// 1 bit for each outputs_len: whether it uses `-> T` or not.
2459 /// 0b0 - operand is a pointer to where to store the output.
2460 /// 0b1 - operand is a type; asm expression has the output as the result.
2461 /// 0b0X is the first output, 0bX0 is the second, etc.
2462 output_type_bits: u32,
2463
2464 pub const Output = struct {
2465 /// index into string_bytes (null terminated)
2466 name: NullTerminatedString,
2467 /// index into string_bytes (null terminated)
2468 constraint: NullTerminatedString,
2469 /// How to interpret this is determined by `output_type_bits`.
2470 operand: Ref,
2471 };
2472
2473 pub const Input = struct {
2474 /// index into string_bytes (null terminated)
2475 name: NullTerminatedString,
2476 /// index into string_bytes (null terminated)
2477 constraint: NullTerminatedString,
2478 operand: Ref,
2479 };
2480 };
2481
2482 /// Trailing:
2483 /// if (ret_body_len == 1) {
2484 /// 0. return_type: Ref
2485 /// }
2486 /// if (ret_body_len > 1) {
2487 /// 1. return_type: Index // for each ret_body_len
2488 /// }
2489 /// 2. body: Index // for each body_len
2490 /// 3. src_locs: SrcLocs // if body_len != 0
2491 /// 4. proto_hash: std.zig.SrcHash // if body_len != 0; hash of function prototype
2492 pub const Func = struct {
2493 /// If this is 0 it means a void return type.
2494 /// If this is 1 it means return_type is a simple Ref
2495 ret_body_len: u32,
2496 /// Points to the block that contains the param instructions for this function.
2497 /// If this is a `declaration`, it refers to the declaration's value body.
2498 param_block: Index,
2499 body_len: u32,
2500
2501 pub const SrcLocs = struct {
2502 /// Line index in the source file relative to the parent decl.
2503 lbrace_line: u32,
2504 /// Line index in the source file relative to the parent decl.
2505 rbrace_line: u32,
2506 /// lbrace_column is least significant bits u16
2507 /// rbrace_column is most significant bits u16
2508 columns: u32,
2509 };
2510 };
2511
2512 /// Trailing:
2513 /// 0. lib_name: NullTerminatedString, // null terminated string index, if has_lib_name is set
2514 /// if (has_align_ref and !has_align_body) {
2515 /// 1. align: Ref,
2516 /// }
2517 /// if (has_align_body) {
2518 /// 2. align_body_len: u32
2519 /// 3. align_body: u32 // for each align_body_len
2520 /// }
2521 /// if (has_addrspace_ref and !has_addrspace_body) {
2522 /// 4. addrspace: Ref,
2523 /// }
2524 /// if (has_addrspace_body) {
2525 /// 5. addrspace_body_len: u32
2526 /// 6. addrspace_body: u32 // for each addrspace_body_len
2527 /// }
2528 /// if (has_section_ref and !has_section_body) {
2529 /// 7. section: Ref,
2530 /// }
2531 /// if (has_section_body) {
2532 /// 8. section_body_len: u32
2533 /// 9. section_body: u32 // for each section_body_len
2534 /// }
2535 /// if (has_cc_ref and !has_cc_body) {
2536 /// 10. cc: Ref,
2537 /// }
2538 /// if (has_cc_body) {
2539 /// 11. cc_body_len: u32
2540 /// 12. cc_body: u32 // for each cc_body_len
2541 /// }
2542 /// if (has_ret_ty_ref and !has_ret_ty_body) {
2543 /// 13. ret_ty: Ref,
2544 /// }
2545 /// if (has_ret_ty_body) {
2546 /// 14. ret_ty_body_len: u32
2547 /// 15. ret_ty_body: u32 // for each ret_ty_body_len
2548 /// }
2549 /// 16. noalias_bits: u32 // if has_any_noalias
2550 /// - each bit starting with LSB corresponds to parameter indexes
2551 /// 17. body: Index // for each body_len
2552 /// 18. src_locs: Func.SrcLocs // if body_len != 0
2553 /// 19. proto_hash: std.zig.SrcHash // if body_len != 0; hash of function prototype
2554 pub const FuncFancy = struct {
2555 /// Points to the block that contains the param instructions for this function.
2556 /// If this is a `declaration`, it refers to the declaration's value body.
2557 param_block: Index,
2558 body_len: u32,
2559 bits: Bits,
2560
2561 /// If both has_cc_ref and has_cc_body are false, it means auto calling convention.
2562 /// If both has_align_ref and has_align_body are false, it means default alignment.
2563 /// If both has_ret_ty_ref and has_ret_ty_body are false, it means void return type.
2564 /// If both has_section_ref and has_section_body are false, it means default section.
2565 /// If both has_addrspace_ref and has_addrspace_body are false, it means default addrspace.
2566 pub const Bits = packed struct {
2567 is_var_args: bool,
2568 is_inferred_error: bool,
2569 is_test: bool,
2570 is_extern: bool,
2571 is_noinline: bool,
2572 has_align_ref: bool,
2573 has_align_body: bool,
2574 has_addrspace_ref: bool,
2575 has_addrspace_body: bool,
2576 has_section_ref: bool,
2577 has_section_body: bool,
2578 has_cc_ref: bool,
2579 has_cc_body: bool,
2580 has_ret_ty_ref: bool,
2581 has_ret_ty_body: bool,
2582 has_lib_name: bool,
2583 has_any_noalias: bool,
2584 _: u15 = undefined,
2585 };
2586 };
2587
2588 /// Trailing:
2589 /// 0. lib_name: NullTerminatedString, // null terminated string index, if has_lib_name is set
2590 /// 1. align: Ref, // if has_align is set
2591 /// 2. init: Ref // if has_init is set
2592 /// The source node is obtained from the containing `block_inline`.
2593 pub const ExtendedVar = struct {
2594 var_type: Ref,
2595
2596 pub const Small = packed struct {
2597 has_lib_name: bool,
2598 has_align: bool,
2599 has_init: bool,
2600 is_extern: bool,
2601 is_const: bool,
2602 is_threadlocal: bool,
2603 _: u10 = undefined,
2604 };
2605 };
2606
2607 /// This data is stored inside extra, with trailing operands according to `operands_len`.
2608 /// Each operand is a `Ref`.
2609 pub const MultiOp = struct {
2610 operands_len: u32,
2611 };
2612
2613 /// Trailing: operand: Ref, // for each `operands_len` (stored in `small`).
2614 pub const NodeMultiOp = struct {
2615 src_node: i32,
2616 };
2617
2618 /// This data is stored inside extra, with trailing operands according to `body_len`.
2619 /// Each operand is an `Index`.
2620 pub const Block = struct {
2621 body_len: u32,
2622 };
2623
2624 /// Trailing:
2625 /// * inst: Index // for each `body_len`
2626 pub const BoolBr = struct {
2627 lhs: Ref,
2628 body_len: u32,
2629 };
2630
2631 /// Trailing:
2632 /// 0. doc_comment: u32 // if `has_doc_comment`; null-terminated string index
2633 /// 1. align_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `align`
2634 /// 2. linksection_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `linksection`
2635 /// 3. addrspace_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `addrspace`
2636 /// 4. value_body_inst: Zir.Inst.Index
2637 /// - for each `value_body_len`
2638 /// - body to be exited via `break_inline` to this `declaration` instruction
2639 /// 5. align_body_inst: Zir.Inst.Index
2640 /// - for each `align_body_len`
2641 /// - body to be exited via `break_inline` to this `declaration` instruction
2642 /// 6. linksection_body_inst: Zir.Inst.Index
2643 /// - for each `linksection_body_len`
2644 /// - body to be exited via `break_inline` to this `declaration` instruction
2645 /// 7. addrspace_body_inst: Zir.Inst.Index
2646 /// - for each `addrspace_body_len`
2647 /// - body to be exited via `break_inline` to this `declaration` instruction
2648 pub const Declaration = struct {
2649 // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`.
2650 src_hash_0: u32,
2651 src_hash_1: u32,
2652 src_hash_2: u32,
2653 src_hash_3: u32,
2654 /// The name of this `Decl`. Also indicates whether it is a test, comptime block, etc.
2655 name: Name,
2656 /// This Decl's line number relative to that of its parent.
2657 /// TODO: column must be encoded similarly to respect non-formatted code!
2658 line_offset: u32,
2659 flags: Flags,
2660
2661 pub const Flags = packed struct(u32) {
2662 value_body_len: u28,
2663 is_pub: bool,
2664 is_export: bool,
2665 has_doc_comment: bool,
2666 has_align_linksection_addrspace: bool,
2667 };
2668
2669 pub const Name = enum(u32) {
2670 @"comptime" = std.math.maxInt(u32),
2671 @"usingnamespace" = std.math.maxInt(u32) - 1,
2672 unnamed_test = std.math.maxInt(u32) - 2,
2673 /// In this case, `has_doc_comment` will be true, and the doc
2674 /// comment body is the identifier name.
2675 decltest = std.math.maxInt(u32) - 3,
2676 /// Other values are `NullTerminatedString` values, i.e. index into
2677 /// `string_bytes`. If the byte referenced is 0, the decl is a named
2678 /// test, and the actual name begins at the following byte.
2679 _,
2680
2681 pub fn isNamedTest(name: Name, zir: Zir) bool {
2682 return switch (name) {
2683 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => false,
2684 _ => zir.string_bytes[@intFromEnum(name)] == 0,
2685 };
2686 }
2687 pub fn toString(name: Name, zir: Zir) ?NullTerminatedString {
2688 switch (name) {
2689 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => return null,
2690 _ => {},
2691 }
2692 const idx: u32 = @intFromEnum(name);
2693 if (zir.string_bytes[idx] == 0) {
2694 // Named test
2695 return @enumFromInt(idx + 1);
2696 }
2697 return @enumFromInt(idx);
2698 }
2699 };
2700
2701 pub const Bodies = struct {
2702 value_body: []const Index,
2703 align_body: ?[]const Index,
2704 linksection_body: ?[]const Index,
2705 addrspace_body: ?[]const Index,
2706 };
2707
2708 pub fn getBodies(declaration: Declaration, extra_end: u32, zir: Zir) Bodies {
2709 var extra_index: u32 = extra_end;
2710 extra_index += @intFromBool(declaration.flags.has_doc_comment);
2711 const value_body_len = declaration.flags.value_body_len;
2712 const align_body_len, const linksection_body_len, const addrspace_body_len = lens: {
2713 if (!declaration.flags.has_align_linksection_addrspace) {
2714 break :lens .{ 0, 0, 0 };
2715 }
2716 const lens = zir.extra[extra_index..][0..3].*;
2717 extra_index += 3;
2718 break :lens lens;
2719 };
2720 return .{
2721 .value_body = b: {
2722 defer extra_index += value_body_len;
2723 break :b zir.bodySlice(extra_index, value_body_len);
2724 },
2725 .align_body = if (align_body_len == 0) null else b: {
2726 defer extra_index += align_body_len;
2727 break :b zir.bodySlice(extra_index, align_body_len);
2728 },
2729 .linksection_body = if (linksection_body_len == 0) null else b: {
2730 defer extra_index += linksection_body_len;
2731 break :b zir.bodySlice(extra_index, linksection_body_len);
2732 },
2733 .addrspace_body = if (addrspace_body_len == 0) null else b: {
2734 defer extra_index += addrspace_body_len;
2735 break :b zir.bodySlice(extra_index, addrspace_body_len);
2736 },
2737 };
2738 }
2739 };
2740
2741 /// Stored inside extra, with trailing arguments according to `args_len`.
2742 /// Implicit 0. arg_0_start: u32, // always same as `args_len`
2743 /// 1. arg_end: u32, // for each `args_len`
2744 /// arg_N_start is the same as arg_N-1_end
2745 pub const Call = struct {
2746 // Note: Flags *must* come first so that unusedResultExpr
2747 // can find it when it goes to modify them.
2748 flags: Flags,
2749 callee: Ref,
2750
2751 pub const Flags = packed struct {
2752 /// std.builtin.CallModifier in packed form
2753 pub const PackedModifier = u3;
2754 pub const PackedArgsLen = u27;
2755
2756 packed_modifier: PackedModifier,
2757 ensure_result_used: bool = false,
2758 pop_error_return_trace: bool,
2759 args_len: PackedArgsLen,
2760
2761 comptime {
2762 if (@sizeOf(Flags) != 4 or @bitSizeOf(Flags) != 32)
2763 @compileError("Layout of Call.Flags needs to be updated!");
2764 if (@bitSizeOf(std.builtin.CallModifier) != @bitSizeOf(PackedModifier))
2765 @compileError("Call.Flags.PackedModifier needs to be updated!");
2766 }
2767 };
2768 };
2769
2770 /// Stored inside extra, with trailing arguments according to `args_len`.
2771 /// Implicit 0. arg_0_start: u32, // always same as `args_len`
2772 /// 1. arg_end: u32, // for each `args_len`
2773 /// arg_N_start is the same as arg_N-1_end
2774 pub const FieldCall = struct {
2775 // Note: Flags *must* come first so that unusedResultExpr
2776 // can find it when it goes to modify them.
2777 flags: Call.Flags,
2778 obj_ptr: Ref,
2779 /// Offset into `string_bytes`.
2780 field_name_start: NullTerminatedString,
2781 };
2782
2783 pub const TypeOfPeer = struct {
2784 src_node: i32,
2785 body_len: u32,
2786 body_index: u32,
2787 };
2788
2789 pub const BuiltinCall = struct {
2790 // Note: Flags *must* come first so that unusedResultExpr
2791 // can find it when it goes to modify them.
2792 flags: Flags,
2793 modifier: Ref,
2794 callee: Ref,
2795 args: Ref,
2796
2797 pub const Flags = packed struct {
2798 is_nosuspend: bool,
2799 ensure_result_used: bool,
2800 _: u30 = undefined,
2801
2802 comptime {
2803 if (@sizeOf(Flags) != 4 or @bitSizeOf(Flags) != 32)
2804 @compileError("Layout of BuiltinCall.Flags needs to be updated!");
2805 }
2806 };
2807 };
2808
2809 /// This data is stored inside extra, with two sets of trailing `Ref`:
2810 /// * 0. the then body, according to `then_body_len`.
2811 /// * 1. the else body, according to `else_body_len`.
2812 pub const CondBr = struct {
2813 condition: Ref,
2814 then_body_len: u32,
2815 else_body_len: u32,
2816 };
2817
2818 /// This data is stored inside extra, trailed by:
2819 /// * 0. body: Index // for each `body_len`.
2820 pub const Try = struct {
2821 /// The error union to unwrap.
2822 operand: Ref,
2823 body_len: u32,
2824 };
2825
2826 /// Stored in extra. Depending on the flags in Data, there will be up to 5
2827 /// trailing Ref fields:
2828 /// 0. sentinel: Ref // if `has_sentinel` flag is set
2829 /// 1. align: Ref // if `has_align` flag is set
2830 /// 2. address_space: Ref // if `has_addrspace` flag is set
2831 /// 3. bit_start: Ref // if `has_bit_range` flag is set
2832 /// 4. host_size: Ref // if `has_bit_range` flag is set
2833 pub const PtrType = struct {
2834 elem_type: Ref,
2835 src_node: i32,
2836 };
2837
2838 pub const ArrayTypeSentinel = struct {
2839 len: Ref,
2840 sentinel: Ref,
2841 elem_type: Ref,
2842 };
2843
2844 pub const SliceStart = struct {
2845 lhs: Ref,
2846 start: Ref,
2847 };
2848
2849 pub const SliceEnd = struct {
2850 lhs: Ref,
2851 start: Ref,
2852 end: Ref,
2853 };
2854
2855 pub const SliceSentinel = struct {
2856 lhs: Ref,
2857 start: Ref,
2858 end: Ref,
2859 sentinel: Ref,
2860 };
2861
2862 pub const SliceLength = struct {
2863 lhs: Ref,
2864 start: Ref,
2865 len: Ref,
2866 sentinel: Ref,
2867 start_src_node_offset: i32,
2868 };
2869
2870 /// The meaning of these operands depends on the corresponding `Tag`.
2871 pub const Bin = struct {
2872 lhs: Ref,
2873 rhs: Ref,
2874 };
2875
2876 pub const BinNode = struct {
2877 node: i32,
2878 lhs: Ref,
2879 rhs: Ref,
2880 };
2881
2882 pub const UnNode = struct {
2883 node: i32,
2884 operand: Ref,
2885 };
2886
2887 pub const ElemPtrImm = struct {
2888 ptr: Ref,
2889 index: u32,
2890 };
2891
2892 pub const SwitchBlockErrUnion = struct {
2893 operand: Ref,
2894 bits: Bits,
2895 main_src_node_offset: i32,
2896
2897 pub const Bits = packed struct(u32) {
2898 /// If true, one or more prongs have multiple items.
2899 has_multi_cases: bool,
2900 /// If true, there is an else prong. This is mutually exclusive with `has_under`.
2901 has_else: bool,
2902 any_uses_err_capture: bool,
2903 payload_is_ref: bool,
2904 scalar_cases_len: ScalarCasesLen,
2905
2906 pub const ScalarCasesLen = u28;
2907 };
2908
2909 pub const MultiProng = struct {
2910 items: []const Ref,
2911 body: []const Index,
2912 };
2913 };
2914
2915 /// 0. multi_cases_len: u32 // If has_multi_cases is set.
2916 /// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture.
2917 /// 2. else_body { // If has_else or has_under is set.
2918 /// info: ProngInfo,
2919 /// body member Index for every info.body_len
2920 /// }
2921 /// 3. scalar_cases: { // for every scalar_cases_len
2922 /// item: Ref,
2923 /// info: ProngInfo,
2924 /// body member Index for every info.body_len
2925 /// }
2926 /// 4. multi_cases: { // for every multi_cases_len
2927 /// items_len: u32,
2928 /// ranges_len: u32,
2929 /// info: ProngInfo,
2930 /// item: Ref // for every items_len
2931 /// ranges: { // for every ranges_len
2932 /// item_first: Ref,
2933 /// item_last: Ref,
2934 /// }
2935 /// body member Index for every info.body_len
2936 /// }
2937 ///
2938 /// When analyzing a case body, the switch instruction itself refers to the
2939 /// captured payload. Whether this is captured by reference or by value
2940 /// depends on whether the `byref` bit is set for the corresponding body.
2941 pub const SwitchBlock = struct {
2942 /// The operand passed to the `switch` expression. If this is a
2943 /// `switch_block`, this is the operand value; if `switch_block_ref` it
2944 /// is a pointer to the operand. `switch_block_ref` is always used if
2945 /// any prong has a byref capture.
2946 operand: Ref,
2947 bits: Bits,
2948
2949 /// These are stored in trailing data in `extra` for each prong.
2950 pub const ProngInfo = packed struct(u32) {
2951 body_len: u28,
2952 capture: Capture,
2953 is_inline: bool,
2954 has_tag_capture: bool,
2955
2956 pub const Capture = enum(u2) {
2957 none,
2958 by_val,
2959 by_ref,
2960 };
2961 };
2962
2963 pub const Bits = packed struct(u32) {
2964 /// If true, one or more prongs have multiple items.
2965 has_multi_cases: bool,
2966 /// If true, there is an else prong. This is mutually exclusive with `has_under`.
2967 has_else: bool,
2968 /// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
2969 has_under: bool,
2970 /// If true, at least one prong has an inline tag capture.
2971 any_has_tag_capture: bool,
2972 scalar_cases_len: ScalarCasesLen,
2973
2974 pub const ScalarCasesLen = u28;
2975
2976 pub fn specialProng(bits: Bits) SpecialProng {
2977 const has_else: u2 = @intFromBool(bits.has_else);
2978 const has_under: u2 = @intFromBool(bits.has_under);
2979 return switch ((has_else << 1) | has_under) {
2980 0b00 => .none,
2981 0b01 => .under,
2982 0b10 => .@"else",
2983 0b11 => unreachable,
2984 };
2985 }
2986 };
2987
2988 pub const MultiProng = struct {
2989 items: []const Ref,
2990 body: []const Index,
2991 };
2992 };
2993
2994 pub const ArrayInitRefTy = struct {
2995 ptr_ty: Ref,
2996 elem_count: u32,
2997 };
2998
2999 pub const Field = struct {
3000 lhs: Ref,
3001 /// Offset into `string_bytes`.
3002 field_name_start: NullTerminatedString,
3003 };
3004
3005 pub const FieldNamed = struct {
3006 lhs: Ref,
3007 field_name: Ref,
3008 };
3009
3010 pub const As = struct {
3011 dest_type: Ref,
3012 operand: Ref,
3013 };
3014
3015 /// Trailing:
3016 /// 0. fields_len: u32, // if has_fields_len
3017 /// 1. decls_len: u32, // if has_decls_len
3018 /// 2. backing_int_body_len: u32, // if has_backing_int
3019 /// 3. backing_int_ref: Ref, // if has_backing_int and backing_int_body_len is 0
3020 /// 4. backing_int_body_inst: Inst, // if has_backing_int and backing_int_body_len is > 0
3021 /// 5. decl: Index, // for every decls_len; points to a `declaration` instruction
3022 /// 6. flags: u32 // for every 8 fields
3023 /// - sets of 4 bits:
3024 /// 0b000X: whether corresponding field has an align expression
3025 /// 0b00X0: whether corresponding field has a default expression
3026 /// 0b0X00: whether corresponding field is comptime
3027 /// 0bX000: whether corresponding field has a type expression
3028 /// 7. fields: { // for every fields_len
3029 /// field_name: u32, // if !is_tuple
3030 /// doc_comment: NullTerminatedString, // .empty if no doc comment
3031 /// field_type: Ref, // if corresponding bit is not set. none means anytype.
3032 /// field_type_body_len: u32, // if corresponding bit is set
3033 /// align_body_len: u32, // if corresponding bit is set
3034 /// init_body_len: u32, // if corresponding bit is set
3035 /// }
3036 /// 8. bodies: { // for every fields_len
3037 /// field_type_body_inst: Inst, // for each field_type_body_len
3038 /// align_body_inst: Inst, // for each align_body_len
3039 /// init_body_inst: Inst, // for each init_body_len
3040 /// }
3041 pub const StructDecl = struct {
3042 // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`.
3043 // This hash contains the source of all fields, and any specified attributes (`extern`, backing type, etc).
3044 fields_hash_0: u32,
3045 fields_hash_1: u32,
3046 fields_hash_2: u32,
3047 fields_hash_3: u32,
3048 src_node: i32,
3049
3050 pub fn src(self: StructDecl) LazySrcLoc {
3051 return LazySrcLoc.nodeOffset(self.src_node);
3052 }
3053
3054 pub const Small = packed struct {
3055 has_fields_len: bool,
3056 has_decls_len: bool,
3057 has_backing_int: bool,
3058 known_non_opv: bool,
3059 known_comptime_only: bool,
3060 is_tuple: bool,
3061 name_strategy: NameStrategy,
3062 layout: std.builtin.Type.ContainerLayout,
3063 any_default_inits: bool,
3064 any_comptime_fields: bool,
3065 any_aligned_fields: bool,
3066 _: u3 = undefined,
3067 };
3068 };
3069
3070 pub const NameStrategy = enum(u2) {
3071 /// Use the same name as the parent declaration name.
3072 /// e.g. `const Foo = struct {...};`.
3073 parent,
3074 /// Use the name of the currently executing comptime function call,
3075 /// with the current parameters. e.g. `ArrayList(i32)`.
3076 func,
3077 /// Create an anonymous name for this declaration.
3078 /// Like this: "ParentDeclName_struct_69"
3079 anon,
3080 /// Use the name specified in the next `dbg_var_{val,ptr}` instruction.
3081 dbg_var,
3082 };
3083
3084 pub const FullPtrCastFlags = packed struct(u5) {
3085 ptr_cast: bool = false,
3086 align_cast: bool = false,
3087 addrspace_cast: bool = false,
3088 const_cast: bool = false,
3089 volatile_cast: bool = false,
3090
3091 pub inline fn needResultTypeBuiltinName(flags: FullPtrCastFlags) []const u8 {
3092 if (flags.ptr_cast) return "@ptrCast";
3093 if (flags.align_cast) return "@alignCast";
3094 if (flags.addrspace_cast) return "@addrSpaceCast";
3095 unreachable;
3096 }
3097 };
3098
3099 /// Trailing:
3100 /// 0. tag_type: Ref, // if has_tag_type
3101 /// 1. body_len: u32, // if has_body_len
3102 /// 2. fields_len: u32, // if has_fields_len
3103 /// 3. decls_len: u32, // if has_decls_len
3104 /// 4. decl: Index, // for every decls_len; points to a `declaration` instruction
3105 /// 5. inst: Index // for every body_len
3106 /// 6. has_bits: u32 // for every 32 fields
3107 /// - the bit is whether corresponding field has an value expression
3108 /// 7. fields: { // for every fields_len
3109 /// field_name: u32,
3110 /// doc_comment: u32, // .empty if no doc_comment
3111 /// value: Ref, // if corresponding bit is set
3112 /// }
3113 pub const EnumDecl = struct {
3114 // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`.
3115 // This hash contains the source of all fields, and the backing type if specified.
3116 fields_hash_0: u32,
3117 fields_hash_1: u32,
3118 fields_hash_2: u32,
3119 fields_hash_3: u32,
3120 src_node: i32,
3121
3122 pub fn src(self: EnumDecl) LazySrcLoc {
3123 return LazySrcLoc.nodeOffset(self.src_node);
3124 }
3125
3126 pub const Small = packed struct {
3127 has_tag_type: bool,
3128 has_body_len: bool,
3129 has_fields_len: bool,
3130 has_decls_len: bool,
3131 name_strategy: NameStrategy,
3132 nonexhaustive: bool,
3133 _: u9 = undefined,
3134 };
3135 };
3136
3137 /// Trailing:
3138 /// 0. tag_type: Ref, // if has_tag_type
3139 /// 1. body_len: u32, // if has_body_len
3140 /// 2. fields_len: u32, // if has_fields_len
3141 /// 3. decls_len: u32, // if has_decls_len
3142 /// 4. decl: Index, // for every decls_len; points to a `declaration` instruction
3143 /// 5. inst: Index // for every body_len
3144 /// 6. has_bits: u32 // for every 8 fields
3145 /// - sets of 4 bits:
3146 /// 0b000X: whether corresponding field has a type expression
3147 /// 0b00X0: whether corresponding field has a align expression
3148 /// 0b0X00: whether corresponding field has a tag value expression
3149 /// 0bX000: unused
3150 /// 7. fields: { // for every fields_len
3151 /// field_name: NullTerminatedString, // null terminated string index
3152 /// doc_comment: NullTerminatedString, // .empty if no doc comment
3153 /// field_type: Ref, // if corresponding bit is set
3154 /// - if none, means `anytype`.
3155 /// align: Ref, // if corresponding bit is set
3156 /// tag_value: Ref, // if corresponding bit is set
3157 /// }
3158 pub const UnionDecl = struct {
3159 // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`.
3160 // This hash contains the source of all fields, and any specified attributes (`extern` etc).
3161 fields_hash_0: u32,
3162 fields_hash_1: u32,
3163 fields_hash_2: u32,
3164 fields_hash_3: u32,
3165 src_node: i32,
3166
3167 pub fn src(self: UnionDecl) LazySrcLoc {
3168 return LazySrcLoc.nodeOffset(self.src_node);
3169 }
3170
3171 pub const Small = packed struct {
3172 has_tag_type: bool,
3173 has_body_len: bool,
3174 has_fields_len: bool,
3175 has_decls_len: bool,
3176 name_strategy: NameStrategy,
3177 layout: std.builtin.Type.ContainerLayout,
3178 /// has_tag_type | auto_enum_tag | result
3179 /// -------------------------------------
3180 /// false | false | union { }
3181 /// false | true | union(enum) { }
3182 /// true | true | union(enum(T)) { }
3183 /// true | false | union(T) { }
3184 auto_enum_tag: bool,
3185 any_aligned_fields: bool,
3186 _: u6 = undefined,
3187 };
3188 };
3189
3190 /// Trailing:
3191 /// 0. decls_len: u32, // if has_decls_len
3192 /// 1. decl: Index, // for every decls_len; points to a `declaration` instruction
3193 pub const OpaqueDecl = struct {
3194 src_node: i32,
3195
3196 pub fn src(self: OpaqueDecl) LazySrcLoc {
3197 return LazySrcLoc.nodeOffset(self.src_node);
3198 }
3199
3200 pub const Small = packed struct {
3201 has_decls_len: bool,
3202 name_strategy: NameStrategy,
3203 _: u13 = undefined,
3204 };
3205 };
3206
3207 /// Trailing:
3208 /// { // for every fields_len
3209 /// field_name: NullTerminatedString // null terminated string index
3210 /// doc_comment: NullTerminatedString // null terminated string index
3211 /// }
3212 pub const ErrorSetDecl = struct {
3213 fields_len: u32,
3214 };
3215
3216 /// A f128 value, broken up into 4 u32 parts.
3217 pub const Float128 = struct {
3218 piece0: u32,
3219 piece1: u32,
3220 piece2: u32,
3221 piece3: u32,
3222
3223 pub fn get(self: Float128) f128 {
3224 const int_bits = @as(u128, self.piece0) |
3225 (@as(u128, self.piece1) << 32) |
3226 (@as(u128, self.piece2) << 64) |
3227 (@as(u128, self.piece3) << 96);
3228 return @as(f128, @bitCast(int_bits));
3229 }
3230 };
3231
3232 /// Trailing is an item per field.
3233 pub const StructInit = struct {
3234 fields_len: u32,
3235
3236 pub const Item = struct {
3237 /// The `struct_init_field_type` ZIR instruction for this field init.
3238 field_type: Index,
3239 /// The field init expression to be used as the field value. This value will be coerced
3240 /// to the field type if not already.
3241 init: Ref,
3242 };
3243 };
3244
3245 /// Trailing is an Item per field.
3246 /// TODO make this instead array of inits followed by array of names because
3247 /// it will be simpler Sema code and better for CPU cache.
3248 pub const StructInitAnon = struct {
3249 fields_len: u32,
3250
3251 pub const Item = struct {
3252 /// Null-terminated string table index.
3253 field_name: NullTerminatedString,
3254 /// The field init expression to be used as the field value.
3255 init: Ref,
3256 };
3257 };
3258
3259 pub const FieldType = struct {
3260 container_type: Ref,
3261 /// Offset into `string_bytes`, null terminated.
3262 name_start: NullTerminatedString,
3263 };
3264
3265 pub const FieldTypeRef = struct {
3266 container_type: Ref,
3267 field_name: Ref,
3268 };
3269
3270 pub const Cmpxchg = struct {
3271 node: i32,
3272 ptr: Ref,
3273 expected_value: Ref,
3274 new_value: Ref,
3275 success_order: Ref,
3276 failure_order: Ref,
3277 };
3278
3279 pub const AtomicRmw = struct {
3280 ptr: Ref,
3281 operation: Ref,
3282 operand: Ref,
3283 ordering: Ref,
3284 };
3285
3286 pub const UnionInit = struct {
3287 union_type: Ref,
3288 field_name: Ref,
3289 init: Ref,
3290 };
3291
3292 pub const AtomicStore = struct {
3293 ptr: Ref,
3294 operand: Ref,
3295 ordering: Ref,
3296 };
3297
3298 pub const AtomicLoad = struct {
3299 elem_type: Ref,
3300 ptr: Ref,
3301 ordering: Ref,
3302 };
3303
3304 pub const MulAdd = struct {
3305 mulend1: Ref,
3306 mulend2: Ref,
3307 addend: Ref,
3308 };
3309
3310 pub const FieldParentPtr = struct {
3311 parent_type: Ref,
3312 field_name: Ref,
3313 field_ptr: Ref,
3314 };
3315
3316 pub const Shuffle = struct {
3317 elem_type: Ref,
3318 a: Ref,
3319 b: Ref,
3320 mask: Ref,
3321 };
3322
3323 pub const Select = struct {
3324 node: i32,
3325 elem_type: Ref,
3326 pred: Ref,
3327 a: Ref,
3328 b: Ref,
3329 };
3330
3331 pub const AsyncCall = struct {
3332 node: i32,
3333 frame_buffer: Ref,
3334 result_ptr: Ref,
3335 fn_ptr: Ref,
3336 args: Ref,
3337 };
3338
3339 /// Trailing: inst: Index // for every body_len
3340 pub const Param = struct {
3341 /// Null-terminated string index.
3342 name: NullTerminatedString,
3343 /// Null-terminated string index.
3344 doc_comment: NullTerminatedString,
3345 /// The body contains the type of the parameter.
3346 body_len: u32,
3347 };
3348
3349 /// Trailing:
3350 /// 0. type_inst: Ref, // if small 0b000X is set
3351 /// 1. align_inst: Ref, // if small 0b00X0 is set
3352 pub const AllocExtended = struct {
3353 src_node: i32,
3354
3355 pub const Small = packed struct {
3356 has_type: bool,
3357 has_align: bool,
3358 is_const: bool,
3359 is_comptime: bool,
3360 _: u12 = undefined,
3361 };
3362 };
3363
3364 pub const Export = struct {
3365 /// If present, this is referring to a Decl via field access, e.g. `a.b`.
3366 /// If omitted, this is referring to a Decl via identifier, e.g. `a`.
3367 namespace: Ref,
3368 /// Null-terminated string index.
3369 decl_name: NullTerminatedString,
3370 options: Ref,
3371 };
3372
3373 pub const ExportValue = struct {
3374 /// The comptime value to export.
3375 operand: Ref,
3376 options: Ref,
3377 };
3378
3379 /// Trailing: `CompileErrors.Item` for each `items_len`.
3380 pub const CompileErrors = struct {
3381 items_len: u32,
3382
3383 /// Trailing: `note_payload_index: u32` for each `notes_len`.
3384 /// It's a payload index of another `Item`.
3385 pub const Item = struct {
3386 /// null terminated string index
3387 msg: NullTerminatedString,
3388 node: Ast.Node.Index,
3389 /// If node is 0 then this will be populated.
3390 token: Ast.TokenIndex,
3391 /// Can be used in combination with `token`.
3392 byte_offset: u32,
3393 /// 0 or a payload index of a `Block`, each is a payload
3394 /// index of another `Item`.
3395 notes: u32,
3396
3397 pub fn notesLen(item: Item, zir: Zir) u32 {
3398 if (item.notes == 0) return 0;
3399 const block = zir.extraData(Block, item.notes);
3400 return block.data.body_len;
3401 }
3402 };
3403 };
3404
3405 /// Trailing: for each `imports_len` there is an Item
3406 pub const Imports = struct {
3407 imports_len: u32,
3408
3409 pub const Item = struct {
3410 /// null terminated string index
3411 name: NullTerminatedString,
3412 /// points to the import name
3413 token: Ast.TokenIndex,
3414 };
3415 };
3416
3417 pub const LineColumn = struct {
3418 line: u32,
3419 column: u32,
3420 };
3421
3422 pub const ArrayInit = struct {
3423 ty: Ref,
3424 init_count: u32,
3425 };
3426
3427 pub const Src = struct {
3428 node: i32,
3429 line: u32,
3430 column: u32,
3431 };
3432
3433 pub const DeferErrCode = struct {
3434 remapped_err_code: Index,
3435 index: u32,
3436 len: u32,
3437 };
3438
3439 pub const ValidateDestructure = struct {
3440 /// The value being destructured.
3441 operand: Ref,
3442 /// The `destructure_assign` node.
3443 destructure_node: i32,
3444 /// The expected field count.
3445 expect_len: u32,
3446 };
3447
3448 pub const ArrayMul = struct {
3449 /// The result type of the array multiplication operation, or `.none` if none was available.
3450 res_ty: Ref,
3451 /// The LHS of the array multiplication.
3452 lhs: Ref,
3453 /// The RHS of the array multiplication.
3454 rhs: Ref,
3455 };
3456
3457 pub const RestoreErrRetIndex = struct {
3458 src_node: i32,
3459 /// If `.none`, restore the trace to its state upon function entry.
3460 block: Ref,
3461 /// If `.none`, restore unconditionally.
3462 operand: Ref,
3463
3464 pub fn src(self: RestoreErrRetIndex) LazySrcLoc {
3465 return LazySrcLoc.nodeOffset(self.src_node);
3466 }
3467 };
3468};
3469
3470pub const SpecialProng = enum { none, @"else", under };
3471
3472pub const DeclIterator = struct {
3473 extra_index: u32,
3474 decls_remaining: u32,
3475 zir: Zir,
3476
3477 pub fn next(it: *DeclIterator) ?Inst.Index {
3478 if (it.decls_remaining == 0) return null;
3479 const decl_inst: Zir.Inst.Index = @enumFromInt(it.zir.extra[it.extra_index]);
3480 it.extra_index += 1;
3481 it.decls_remaining -= 1;
3482 assert(it.zir.instructions.items(.tag)[@intFromEnum(decl_inst)] == .declaration);
3483 return decl_inst;
3484 }
3485};
3486
3487pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
3488 const tags = zir.instructions.items(.tag);
3489 const datas = zir.instructions.items(.data);
3490 switch (tags[@intFromEnum(decl_inst)]) {
3491 // Functions are allowed and yield no iterations.
3492 // There is one case matching this in the extended instruction set below.
3493 .func, .func_inferred, .func_fancy => return .{
3494 .extra_index = undefined,
3495 .decls_remaining = 0,
3496 .zir = zir,
3497 },
3498
3499 .extended => {
3500 const extended = datas[@intFromEnum(decl_inst)].extended;
3501 switch (extended.opcode) {
3502 .struct_decl => {
3503 const small: Inst.StructDecl.Small = @bitCast(extended.small);
3504 var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.StructDecl).Struct.fields.len);
3505 extra_index += @intFromBool(small.has_fields_len);
3506 const decls_len = if (small.has_decls_len) decls_len: {
3507 const decls_len = zir.extra[extra_index];
3508 extra_index += 1;
3509 break :decls_len decls_len;
3510 } else 0;
3511
3512 if (small.has_backing_int) {
3513 const backing_int_body_len = zir.extra[extra_index];
3514 extra_index += 1; // backing_int_body_len
3515 if (backing_int_body_len == 0) {
3516 extra_index += 1; // backing_int_ref
3517 } else {
3518 extra_index += backing_int_body_len; // backing_int_body_inst
3519 }
3520 }
3521
3522 return .{
3523 .extra_index = extra_index,
3524 .decls_remaining = decls_len,
3525 .zir = zir,
3526 };
3527 },
3528 .enum_decl => {
3529 const small: Inst.EnumDecl.Small = @bitCast(extended.small);
3530 var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.EnumDecl).Struct.fields.len);
3531 extra_index += @intFromBool(small.has_tag_type);
3532 extra_index += @intFromBool(small.has_body_len);
3533 extra_index += @intFromBool(small.has_fields_len);
3534 const decls_len = if (small.has_decls_len) decls_len: {
3535 const decls_len = zir.extra[extra_index];
3536 extra_index += 1;
3537 break :decls_len decls_len;
3538 } else 0;
3539
3540 return .{
3541 .extra_index = extra_index,
3542 .decls_remaining = decls_len,
3543 .zir = zir,
3544 };
3545 },
3546 .union_decl => {
3547 const small: Inst.UnionDecl.Small = @bitCast(extended.small);
3548 var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.UnionDecl).Struct.fields.len);
3549 extra_index += @intFromBool(small.has_tag_type);
3550 extra_index += @intFromBool(small.has_body_len);
3551 extra_index += @intFromBool(small.has_fields_len);
3552 const decls_len = if (small.has_decls_len) decls_len: {
3553 const decls_len = zir.extra[extra_index];
3554 extra_index += 1;
3555 break :decls_len decls_len;
3556 } else 0;
3557
3558 return .{
3559 .extra_index = extra_index,
3560 .decls_remaining = decls_len,
3561 .zir = zir,
3562 };
3563 },
3564 .opaque_decl => {
3565 const small: Inst.OpaqueDecl.Small = @bitCast(extended.small);
3566 var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.OpaqueDecl).Struct.fields.len);
3567 const decls_len = if (small.has_decls_len) decls_len: {
3568 const decls_len = zir.extra[extra_index];
3569 extra_index += 1;
3570 break :decls_len decls_len;
3571 } else 0;
3572
3573 return .{
3574 .extra_index = extra_index,
3575 .decls_remaining = decls_len,
3576 .zir = zir,
3577 };
3578 },
3579 else => unreachable,
3580 }
3581 },
3582 else => unreachable,
3583 }
3584}
3585
3586/// The iterator would have to allocate memory anyway to iterate. So here we populate
3587/// an ArrayList as the result.
3588pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_inst: Zir.Inst.Index) !void {
3589 list.clearRetainingCapacity();
3590 const declaration, const extra_end = zir.getDeclaration(decl_inst);
3591 const bodies = declaration.getBodies(extra_end, zir);
3592
3593 try zir.findDeclsBody(list, bodies.value_body);
3594 if (bodies.align_body) |b| try zir.findDeclsBody(list, b);
3595 if (bodies.linksection_body) |b| try zir.findDeclsBody(list, b);
3596 if (bodies.addrspace_body) |b| try zir.findDeclsBody(list, b);
3597}
3598
3599fn findDeclsInner(
3600 zir: Zir,
3601 list: *std.ArrayList(Inst.Index),
3602 inst: Inst.Index,
3603) Allocator.Error!void {
3604 const tags = zir.instructions.items(.tag);
3605 const datas = zir.instructions.items(.data);
3606
3607 switch (tags[@intFromEnum(inst)]) {
3608 // Functions instructions are interesting and have a body.
3609 .func,
3610 .func_inferred,
3611 => {
3612 try list.append(inst);
3613
3614 const inst_data = datas[@intFromEnum(inst)].pl_node;
3615 const extra = zir.extraData(Inst.Func, inst_data.payload_index);
3616 var extra_index: usize = extra.end;
3617 switch (extra.data.ret_body_len) {
3618 0 => {},
3619 1 => extra_index += 1,
3620 else => {
3621 const body = zir.bodySlice(extra_index, extra.data.ret_body_len);
3622 extra_index += body.len;
3623 try zir.findDeclsBody(list, body);
3624 },
3625 }
3626 const body = zir.bodySlice(extra_index, extra.data.body_len);
3627 return zir.findDeclsBody(list, body);
3628 },
3629 .func_fancy => {
3630 try list.append(inst);
3631
3632 const inst_data = datas[@intFromEnum(inst)].pl_node;
3633 const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
3634 var extra_index: usize = extra.end;
3635 extra_index += @intFromBool(extra.data.bits.has_lib_name);
3636
3637 if (extra.data.bits.has_align_body) {
3638 const body_len = zir.extra[extra_index];
3639 extra_index += 1;
3640 const body = zir.bodySlice(extra_index, body_len);
3641 try zir.findDeclsBody(list, body);
3642 extra_index += body.len;
3643 } else if (extra.data.bits.has_align_ref) {
3644 extra_index += 1;
3645 }
3646
3647 if (extra.data.bits.has_addrspace_body) {
3648 const body_len = zir.extra[extra_index];
3649 extra_index += 1;
3650 const body = zir.bodySlice(extra_index, body_len);
3651 try zir.findDeclsBody(list, body);
3652 extra_index += body.len;
3653 } else if (extra.data.bits.has_addrspace_ref) {
3654 extra_index += 1;
3655 }
3656
3657 if (extra.data.bits.has_section_body) {
3658 const body_len = zir.extra[extra_index];
3659 extra_index += 1;
3660 const body = zir.bodySlice(extra_index, body_len);
3661 try zir.findDeclsBody(list, body);
3662 extra_index += body.len;
3663 } else if (extra.data.bits.has_section_ref) {
3664 extra_index += 1;
3665 }
3666
3667 if (extra.data.bits.has_cc_body) {
3668 const body_len = zir.extra[extra_index];
3669 extra_index += 1;
3670 const body = zir.bodySlice(extra_index, body_len);
3671 try zir.findDeclsBody(list, body);
3672 extra_index += body.len;
3673 } else if (extra.data.bits.has_cc_ref) {
3674 extra_index += 1;
3675 }
3676
3677 if (extra.data.bits.has_ret_ty_body) {
3678 const body_len = zir.extra[extra_index];
3679 extra_index += 1;
3680 const body = zir.bodySlice(extra_index, body_len);
3681 try zir.findDeclsBody(list, body);
3682 extra_index += body.len;
3683 } else if (extra.data.bits.has_ret_ty_ref) {
3684 extra_index += 1;
3685 }
3686
3687 extra_index += @intFromBool(extra.data.bits.has_any_noalias);
3688
3689 const body = zir.bodySlice(extra_index, extra.data.body_len);
3690 return zir.findDeclsBody(list, body);
3691 },
3692 .extended => {
3693 const extended = datas[@intFromEnum(inst)].extended;
3694 switch (extended.opcode) {
3695
3696 // Decl instructions are interesting but have no body.
3697 // TODO yes they do have a body actually. recurse over them just like block instructions.
3698 .struct_decl,
3699 .union_decl,
3700 .enum_decl,
3701 .opaque_decl,
3702 => return list.append(inst),
3703
3704 else => return,
3705 }
3706 },
3707
3708 // Block instructions, recurse over the bodies.
3709
3710 .block, .block_comptime, .block_inline => {
3711 const inst_data = datas[@intFromEnum(inst)].pl_node;
3712 const extra = zir.extraData(Inst.Block, inst_data.payload_index);
3713 const body = zir.bodySlice(extra.end, extra.data.body_len);
3714 return zir.findDeclsBody(list, body);
3715 },
3716 .condbr, .condbr_inline => {
3717 const inst_data = datas[@intFromEnum(inst)].pl_node;
3718 const extra = zir.extraData(Inst.CondBr, inst_data.payload_index);
3719 const then_body = zir.bodySlice(extra.end, extra.data.then_body_len);
3720 const else_body = zir.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
3721 try zir.findDeclsBody(list, then_body);
3722 try zir.findDeclsBody(list, else_body);
3723 },
3724 .@"try", .try_ptr => {
3725 const inst_data = datas[@intFromEnum(inst)].pl_node;
3726 const extra = zir.extraData(Inst.Try, inst_data.payload_index);
3727 const body = zir.bodySlice(extra.end, extra.data.body_len);
3728 try zir.findDeclsBody(list, body);
3729 },
3730 .switch_block => return findDeclsSwitch(zir, list, inst),
3731
3732 .suspend_block => @panic("TODO iterate suspend block"),
3733
3734 else => return, // Regular instruction, not interesting.
3735 }
3736}
3737
3738fn findDeclsSwitch(
3739 zir: Zir,
3740 list: *std.ArrayList(Inst.Index),
3741 inst: Inst.Index,
3742) Allocator.Error!void {
3743 const inst_data = zir.instructions.items(.data)[@intFromEnum(inst)].pl_node;
3744 const extra = zir.extraData(Inst.SwitchBlock, inst_data.payload_index);
3745
3746 var extra_index: usize = extra.end;
3747
3748 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
3749 const multi_cases_len = zir.extra[extra_index];
3750 extra_index += 1;
3751 break :blk multi_cases_len;
3752 } else 0;
3753
3754 const special_prong = extra.data.bits.specialProng();
3755 if (special_prong != .none) {
3756 const body_len: u31 = @truncate(zir.extra[extra_index]);
3757 extra_index += 1;
3758 const body = zir.bodySlice(extra_index, body_len);
3759 extra_index += body.len;
3760
3761 try zir.findDeclsBody(list, body);
3762 }
3763
3764 {
3765 const scalar_cases_len = extra.data.bits.scalar_cases_len;
3766 for (0..scalar_cases_len) |_| {
3767 extra_index += 1;
3768 const body_len: u31 = @truncate(zir.extra[extra_index]);
3769 extra_index += 1;
3770 const body = zir.bodySlice(extra_index, body_len);
3771 extra_index += body_len;
3772
3773 try zir.findDeclsBody(list, body);
3774 }
3775 }
3776 {
3777 for (0..multi_cases_len) |_| {
3778 const items_len = zir.extra[extra_index];
3779 extra_index += 1;
3780 const ranges_len = zir.extra[extra_index];
3781 extra_index += 1;
3782 const body_len: u31 = @truncate(zir.extra[extra_index]);
3783 extra_index += 1;
3784 const items = zir.refSlice(extra_index, items_len);
3785 extra_index += items_len;
3786 _ = items;
3787
3788 var range_i: usize = 0;
3789 while (range_i < ranges_len) : (range_i += 1) {
3790 extra_index += 1;
3791 extra_index += 1;
3792 }
3793
3794 const body = zir.bodySlice(extra_index, body_len);
3795 extra_index += body_len;
3796
3797 try zir.findDeclsBody(list, body);
3798 }
3799 }
3800}
3801
3802fn findDeclsBody(
3803 zir: Zir,
3804 list: *std.ArrayList(Inst.Index),
3805 body: []const Inst.Index,
3806) Allocator.Error!void {
3807 for (body) |member| {
3808 try zir.findDeclsInner(list, member);
3809 }
3810}
3811
3812pub const FnInfo = struct {
3813 param_body: []const Inst.Index,
3814 param_body_inst: Inst.Index,
3815 ret_ty_body: []const Inst.Index,
3816 body: []const Inst.Index,
3817 ret_ty_ref: Zir.Inst.Ref,
3818 total_params_len: u32,
3819};
3820
3821pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const Zir.Inst.Index {
3822 const tags = zir.instructions.items(.tag);
3823 const datas = zir.instructions.items(.data);
3824 const inst_data = datas[@intFromEnum(fn_inst)].pl_node;
3825
3826 const param_block_index = switch (tags[@intFromEnum(fn_inst)]) {
3827 .func, .func_inferred => blk: {
3828 const extra = zir.extraData(Inst.Func, inst_data.payload_index);
3829 break :blk extra.data.param_block;
3830 },
3831 .func_fancy => blk: {
3832 const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
3833 break :blk extra.data.param_block;
3834 },
3835 else => unreachable,
3836 };
3837
3838 switch (tags[@intFromEnum(param_block_index)]) {
3839 .block, .block_comptime, .block_inline => {
3840 const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(param_block_index)].pl_node.payload_index);
3841 return zir.bodySlice(param_block.end, param_block.data.body_len);
3842 },
3843 .declaration => {
3844 const decl, const extra_end = zir.getDeclaration(param_block_index);
3845 return decl.getBodies(extra_end, zir).value_body;
3846 },
3847 else => unreachable,
3848 }
3849}
3850
3851pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
3852 const tags = zir.instructions.items(.tag);
3853 const datas = zir.instructions.items(.data);
3854 const info: struct {
3855 param_block: Inst.Index,
3856 body: []const Inst.Index,
3857 ret_ty_ref: Inst.Ref,
3858 ret_ty_body: []const Inst.Index,
3859 } = switch (tags[@intFromEnum(fn_inst)]) {
3860 .func, .func_inferred => blk: {
3861 const inst_data = datas[@intFromEnum(fn_inst)].pl_node;
3862 const extra = zir.extraData(Inst.Func, inst_data.payload_index);
3863
3864 var extra_index: usize = extra.end;
3865 var ret_ty_ref: Inst.Ref = .none;
3866 var ret_ty_body: []const Inst.Index = &.{};
3867
3868 switch (extra.data.ret_body_len) {
3869 0 => {
3870 ret_ty_ref = .void_type;
3871 },
3872 1 => {
3873 ret_ty_ref = @enumFromInt(zir.extra[extra_index]);
3874 extra_index += 1;
3875 },
3876 else => {
3877 ret_ty_body = zir.bodySlice(extra_index, extra.data.ret_body_len);
3878 extra_index += ret_ty_body.len;
3879 },
3880 }
3881
3882 const body = zir.bodySlice(extra_index, extra.data.body_len);
3883 extra_index += body.len;
3884
3885 break :blk .{
3886 .param_block = extra.data.param_block,
3887 .ret_ty_ref = ret_ty_ref,
3888 .ret_ty_body = ret_ty_body,
3889 .body = body,
3890 };
3891 },
3892 .func_fancy => blk: {
3893 const inst_data = datas[@intFromEnum(fn_inst)].pl_node;
3894 const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
3895
3896 var extra_index: usize = extra.end;
3897 var ret_ty_ref: Inst.Ref = .void_type;
3898 var ret_ty_body: []const Inst.Index = &.{};
3899
3900 extra_index += @intFromBool(extra.data.bits.has_lib_name);
3901 if (extra.data.bits.has_align_body) {
3902 extra_index += zir.extra[extra_index] + 1;
3903 } else if (extra.data.bits.has_align_ref) {
3904 extra_index += 1;
3905 }
3906 if (extra.data.bits.has_addrspace_body) {
3907 extra_index += zir.extra[extra_index] + 1;
3908 } else if (extra.data.bits.has_addrspace_ref) {
3909 extra_index += 1;
3910 }
3911 if (extra.data.bits.has_section_body) {
3912 extra_index += zir.extra[extra_index] + 1;
3913 } else if (extra.data.bits.has_section_ref) {
3914 extra_index += 1;
3915 }
3916 if (extra.data.bits.has_cc_body) {
3917 extra_index += zir.extra[extra_index] + 1;
3918 } else if (extra.data.bits.has_cc_ref) {
3919 extra_index += 1;
3920 }
3921 if (extra.data.bits.has_ret_ty_body) {
3922 const body_len = zir.extra[extra_index];
3923 extra_index += 1;
3924 ret_ty_body = zir.bodySlice(extra_index, body_len);
3925 extra_index += ret_ty_body.len;
3926 } else if (extra.data.bits.has_ret_ty_ref) {
3927 ret_ty_ref = @enumFromInt(zir.extra[extra_index]);
3928 extra_index += 1;
3929 }
3930
3931 extra_index += @intFromBool(extra.data.bits.has_any_noalias);
3932
3933 const body = zir.bodySlice(extra_index, extra.data.body_len);
3934 extra_index += body.len;
3935 break :blk .{
3936 .param_block = extra.data.param_block,
3937 .ret_ty_ref = ret_ty_ref,
3938 .ret_ty_body = ret_ty_body,
3939 .body = body,
3940 };
3941 },
3942 else => unreachable,
3943 };
3944 const param_body = switch (tags[@intFromEnum(info.param_block)]) {
3945 .block, .block_comptime, .block_inline => param_body: {
3946 const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(info.param_block)].pl_node.payload_index);
3947 break :param_body zir.bodySlice(param_block.end, param_block.data.body_len);
3948 },
3949 .declaration => param_body: {
3950 const decl, const extra_end = zir.getDeclaration(info.param_block);
3951 break :param_body decl.getBodies(extra_end, zir).value_body;
3952 },
3953 else => unreachable,
3954 };
3955 var total_params_len: u32 = 0;
3956 for (param_body) |inst| {
3957 switch (tags[@intFromEnum(inst)]) {
3958 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
3959 total_params_len += 1;
3960 },
3961 else => continue,
3962 }
3963 }
3964 return .{
3965 .param_body = param_body,
3966 .param_body_inst = info.param_block,
3967 .ret_ty_body = info.ret_ty_body,
3968 .ret_ty_ref = info.ret_ty_ref,
3969 .body = info.body,
3970 .total_params_len = total_params_len,
3971 };
3972}
3973
3974pub fn getDeclaration(zir: Zir, inst: Zir.Inst.Index) struct { Inst.Declaration, u32 } {
3975 assert(zir.instructions.items(.tag)[@intFromEnum(inst)] == .declaration);
3976 const pl_node = zir.instructions.items(.data)[@intFromEnum(inst)].pl_node;
3977 const extra = zir.extraData(Inst.Declaration, pl_node.payload_index);
3978 return .{
3979 extra.data,
3980 @intCast(extra.end),
3981 };
3982}
3983
3984pub fn getAssociatedSrcHash(zir: Zir, inst: Zir.Inst.Index) ?std.zig.SrcHash {
3985 const tag = zir.instructions.items(.tag);
3986 const data = zir.instructions.items(.data);
3987 switch (tag[@intFromEnum(inst)]) {
3988 .declaration => {
3989 const pl_node = data[@intFromEnum(inst)].pl_node;
3990 const extra = zir.extraData(Inst.Declaration, pl_node.payload_index);
3991 return @bitCast([4]u32{
3992 extra.data.src_hash_0,
3993 extra.data.src_hash_1,
3994 extra.data.src_hash_2,
3995 extra.data.src_hash_3,
3996 });
3997 },
3998 .func, .func_inferred => {
3999 const pl_node = data[@intFromEnum(inst)].pl_node;
4000 const extra = zir.extraData(Inst.Func, pl_node.payload_index);
4001 if (extra.data.body_len == 0) {
4002 // Function type or extern fn - no associated hash
4003 return null;
4004 }
4005 const extra_index = extra.end +
4006 1 +
4007 extra.data.body_len +
4008 @typeInfo(Inst.Func.SrcLocs).Struct.fields.len;
4009 return @bitCast([4]u32{
4010 zir.extra[extra_index + 0],
4011 zir.extra[extra_index + 1],
4012 zir.extra[extra_index + 2],
4013 zir.extra[extra_index + 3],
4014 });
4015 },
4016 .func_fancy => {
4017 const pl_node = data[@intFromEnum(inst)].pl_node;
4018 const extra = zir.extraData(Inst.FuncFancy, pl_node.payload_index);
4019 if (extra.data.body_len == 0) {
4020 // Function type or extern fn - no associated hash
4021 return null;
4022 }
4023 const bits = extra.data.bits;
4024 var extra_index = extra.end;
4025 extra_index += @intFromBool(bits.has_lib_name);
4026 if (bits.has_align_body) {
4027 const body_len = zir.extra[extra_index];
4028 extra_index += 1 + body_len;
4029 } else extra_index += @intFromBool(bits.has_align_ref);
4030 if (bits.has_addrspace_body) {
4031 const body_len = zir.extra[extra_index];
4032 extra_index += 1 + body_len;
4033 } else extra_index += @intFromBool(bits.has_addrspace_ref);
4034 if (bits.has_section_body) {
4035 const body_len = zir.extra[extra_index];
4036 extra_index += 1 + body_len;
4037 } else extra_index += @intFromBool(bits.has_section_ref);
4038 if (bits.has_cc_body) {
4039 const body_len = zir.extra[extra_index];
4040 extra_index += 1 + body_len;
4041 } else extra_index += @intFromBool(bits.has_cc_ref);
4042 if (bits.has_ret_ty_body) {
4043 const body_len = zir.extra[extra_index];
4044 extra_index += 1 + body_len;
4045 } else extra_index += @intFromBool(bits.has_ret_ty_ref);
4046 extra_index += @intFromBool(bits.has_any_noalias);
4047 extra_index += extra.data.body_len;
4048 extra_index += @typeInfo(Zir.Inst.Func.SrcLocs).Struct.fields.len;
4049 return @bitCast([4]u32{
4050 zir.extra[extra_index + 0],
4051 zir.extra[extra_index + 1],
4052 zir.extra[extra_index + 2],
4053 zir.extra[extra_index + 3],
4054 });
4055 },
4056 .extended => {},
4057 else => return null,
4058 }
4059 const extended = data[@intFromEnum(inst)].extended;
4060 switch (extended.opcode) {
4061 .struct_decl => {
4062 const extra = zir.extraData(Inst.StructDecl, extended.operand).data;
4063 return @bitCast([4]u32{
4064 extra.fields_hash_0,
4065 extra.fields_hash_1,
4066 extra.fields_hash_2,
4067 extra.fields_hash_3,
4068 });
4069 },
4070 .union_decl => {
4071 const extra = zir.extraData(Inst.UnionDecl, extended.operand).data;
4072 return @bitCast([4]u32{
4073 extra.fields_hash_0,
4074 extra.fields_hash_1,
4075 extra.fields_hash_2,
4076 extra.fields_hash_3,
4077 });
4078 },
4079 .enum_decl => {
4080 const extra = zir.extraData(Inst.EnumDecl, extended.operand).data;
4081 return @bitCast([4]u32{
4082 extra.fields_hash_0,
4083 extra.fields_hash_1,
4084 extra.fields_hash_2,
4085 extra.fields_hash_3,
4086 });
4087 },
4088 else => return null,
4089 }
4090}
lib/std/zig/fmt.zig deleted-110
...@@ -1,110 +0,0 @@
1const std = @import("std");
2const mem = std.mem;
3
4/// Print the string as a Zig identifier escaping it with @"" syntax if needed.
5fn formatId(
6 bytes: []const u8,
7 comptime fmt: []const u8,
8 options: std.fmt.FormatOptions,
9 writer: anytype,
10) !void {
11 _ = fmt;
12 if (isValidId(bytes)) {
13 return writer.writeAll(bytes);
14 }
15 try writer.writeAll("@\"");
16 try stringEscape(bytes, "", options, writer);
17 try writer.writeByte('"');
18}
19
20/// Return a Formatter for a Zig identifier
21pub fn fmtId(bytes: []const u8) std.fmt.Formatter(formatId) {
22 return .{ .data = bytes };
23}
24
25pub fn isValidId(bytes: []const u8) bool {
26 if (bytes.len == 0) return false;
27 if (mem.eql(u8, bytes, "_")) return false;
28 for (bytes, 0..) |c, i| {
29 switch (c) {
30 '_', 'a'...'z', 'A'...'Z' => {},
31 '0'...'9' => if (i == 0) return false,
32 else => return false,
33 }
34 }
35 return std.zig.Token.getKeyword(bytes) == null;
36}
37
38test "isValidId" {
39 try std.testing.expect(!isValidId(""));
40 try std.testing.expect(isValidId("foobar"));
41 try std.testing.expect(!isValidId("a b c"));
42 try std.testing.expect(!isValidId("3d"));
43 try std.testing.expect(!isValidId("enum"));
44 try std.testing.expect(isValidId("i386"));
45}
46
47/// Print the string as escaped contents of a double quoted or single-quoted string.
48/// Format `{}` treats contents as a double-quoted string.
49/// Format `{'}` treats contents as a single-quoted string.
50pub fn stringEscape(
51 bytes: []const u8,
52 comptime fmt: []const u8,
53 options: std.fmt.FormatOptions,
54 writer: anytype,
55) !void {
56 _ = options;
57 for (bytes) |byte| switch (byte) {
58 '\n' => try writer.writeAll("\\n"),
59 '\r' => try writer.writeAll("\\r"),
60 '\t' => try writer.writeAll("\\t"),
61 '\\' => try writer.writeAll("\\\\"),
62 '"' => {
63 if (fmt.len == 1 and fmt[0] == '\'') {
64 try writer.writeByte('"');
65 } else if (fmt.len == 0) {
66 try writer.writeAll("\\\"");
67 } else {
68 @compileError("expected {} or {'}, found {" ++ fmt ++ "}");
69 }
70 },
71 '\'' => {
72 if (fmt.len == 1 and fmt[0] == '\'') {
73 try writer.writeAll("\\'");
74 } else if (fmt.len == 0) {
75 try writer.writeByte('\'');
76 } else {
77 @compileError("expected {} or {'}, found {" ++ fmt ++ "}");
78 }
79 },
80 ' ', '!', '#'...'&', '('...'[', ']'...'~' => try writer.writeByte(byte),
81 // Use hex escapes for rest any unprintable characters.
82 else => {
83 try writer.writeAll("\\x");
84 try std.fmt.formatInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }, writer);
85 },
86 };
87}
88
89/// Return a Formatter for Zig Escapes of a double quoted string.
90/// The format specifier must be one of:
91/// * `{}` treats contents as a double-quoted string.
92/// * `{'}` treats contents as a single-quoted string.
93pub fn fmtEscapes(bytes: []const u8) std.fmt.Formatter(stringEscape) {
94 return .{ .data = bytes };
95}
96
97test "escape invalid identifiers" {
98 const expectFmt = std.testing.expectFmt;
99 try expectFmt("@\"while\"", "{}", .{fmtId("while")});
100 try expectFmt("hello", "{}", .{fmtId("hello")});
101 try expectFmt("@\"11\\\"23\"", "{}", .{fmtId("11\"23")});
102 try expectFmt("@\"11\\x0f23\"", "{}", .{fmtId("11\x0F23")});
103 try expectFmt("\\x0f", "{}", .{fmtEscapes("\x0f")});
104 try expectFmt(
105 \\" \\ hi \x07 \x11 " derp \'"
106 , "\"{'}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
107 try expectFmt(
108 \\" \\ hi \x07 \x11 \" derp '"
109 , "\"{}\"", .{fmtEscapes(" \\ hi \x07 \x11 \" derp '")});
110}
lib/test_runner.zig deleted-249
...@@ -1,249 +0,0 @@
1//! Default test runner for unit tests.
2const std = @import("std");
3const io = std.io;
4const builtin = @import("builtin");
5
6pub const std_options = .{
7 .logFn = log,
8};
9
10var log_err_count: usize = 0;
11var cmdline_buffer: [4096]u8 = undefined;
12var fba = std.heap.FixedBufferAllocator.init(&cmdline_buffer);
13
14pub fn main() void {
15 if (builtin.zig_backend == .stage2_aarch64) {
16 return mainSimple() catch @panic("test failure");
17 }
18
19 const args = std.process.argsAlloc(fba.allocator()) catch
20 @panic("unable to parse command line args");
21
22 var listen = false;
23
24 for (args[1..]) |arg| {
25 if (std.mem.eql(u8, arg, "--listen=-")) {
26 listen = true;
27 } else {
28 @panic("unrecognized command line argument");
29 }
30 }
31
32 if (listen) {
33 return mainServer() catch @panic("internal test runner failure");
34 } else {
35 return mainTerminal();
36 }
37}
38
39fn mainServer() !void {
40 var server = try std.zig.Server.init(.{
41 .gpa = fba.allocator(),
42 .in = std.io.getStdIn(),
43 .out = std.io.getStdOut(),
44 .zig_version = builtin.zig_version_string,
45 });
46 defer server.deinit();
47
48 while (true) {
49 const hdr = try server.receiveMessage();
50 switch (hdr.tag) {
51 .exit => {
52 return std.process.exit(0);
53 },
54 .query_test_metadata => {
55 std.testing.allocator_instance = .{};
56 defer if (std.testing.allocator_instance.deinit() == .leak) {
57 @panic("internal test runner memory leak");
58 };
59
60 var string_bytes: std.ArrayListUnmanaged(u8) = .{};
61 defer string_bytes.deinit(std.testing.allocator);
62 try string_bytes.append(std.testing.allocator, 0); // Reserve 0 for null.
63
64 const test_fns = builtin.test_functions;
65 const names = try std.testing.allocator.alloc(u32, test_fns.len);
66 defer std.testing.allocator.free(names);
67 const expected_panic_msgs = try std.testing.allocator.alloc(u32, test_fns.len);
68 defer std.testing.allocator.free(expected_panic_msgs);
69
70 for (test_fns, names, expected_panic_msgs) |test_fn, *name, *expected_panic_msg| {
71 name.* = @as(u32, @intCast(string_bytes.items.len));
72 try string_bytes.ensureUnusedCapacity(std.testing.allocator, test_fn.name.len + 1);
73 string_bytes.appendSliceAssumeCapacity(test_fn.name);
74 string_bytes.appendAssumeCapacity(0);
75 expected_panic_msg.* = 0;
76 }
77
78 try server.serveTestMetadata(.{
79 .names = names,
80 .expected_panic_msgs = expected_panic_msgs,
81 .string_bytes = string_bytes.items,
82 });
83 },
84
85 .run_test => {
86 std.testing.allocator_instance = .{};
87 log_err_count = 0;
88 const index = try server.receiveBody_u32();
89 const test_fn = builtin.test_functions[index];
90 var fail = false;
91 var skip = false;
92 var leak = false;
93 test_fn.func() catch |err| switch (err) {
94 error.SkipZigTest => skip = true,
95 else => {
96 fail = true;
97 if (@errorReturnTrace()) |trace| {
98 std.debug.dumpStackTrace(trace.*);
99 }
100 },
101 };
102 leak = std.testing.allocator_instance.deinit() == .leak;
103 try server.serveTestResults(.{
104 .index = index,
105 .flags = .{
106 .fail = fail,
107 .skip = skip,
108 .leak = leak,
109 .log_err_count = std.math.lossyCast(std.meta.FieldType(
110 std.zig.Server.Message.TestResults.Flags,
111 .log_err_count,
112 ), log_err_count),
113 },
114 });
115 },
116
117 else => {
118 std.debug.print("unsupported message: {x}", .{@intFromEnum(hdr.tag)});
119 std.process.exit(1);
120 },
121 }
122 }
123}
124
125fn mainTerminal() void {
126 const test_fn_list = builtin.test_functions;
127 var ok_count: usize = 0;
128 var skip_count: usize = 0;
129 var fail_count: usize = 0;
130 var progress = std.Progress{
131 .dont_print_on_dumb = true,
132 };
133 const root_node = progress.start("Test", test_fn_list.len);
134 const have_tty = progress.terminal != null and
135 (progress.supports_ansi_escape_codes or progress.is_windows_terminal);
136
137 var async_frame_buffer: []align(builtin.target.stackAlignment()) u8 = undefined;
138 // TODO this is on the next line (using `undefined` above) because otherwise zig incorrectly
139 // ignores the alignment of the slice.
140 async_frame_buffer = &[_]u8{};
141
142 var leaks: usize = 0;
143 for (test_fn_list, 0..) |test_fn, i| {
144 std.testing.allocator_instance = .{};
145 defer {
146 if (std.testing.allocator_instance.deinit() == .leak) {
147 leaks += 1;
148 }
149 }
150 std.testing.log_level = .warn;
151
152 var test_node = root_node.start(test_fn.name, 0);
153 test_node.activate();
154 progress.refresh();
155 if (!have_tty) {
156 std.debug.print("{d}/{d} {s}... ", .{ i + 1, test_fn_list.len, test_fn.name });
157 }
158 if (test_fn.func()) |_| {
159 ok_count += 1;
160 test_node.end();
161 if (!have_tty) std.debug.print("OK\n", .{});
162 } else |err| switch (err) {
163 error.SkipZigTest => {
164 skip_count += 1;
165 progress.log("SKIP\n", .{});
166 test_node.end();
167 },
168 else => {
169 fail_count += 1;
170 progress.log("FAIL ({s})\n", .{@errorName(err)});
171 if (@errorReturnTrace()) |trace| {
172 std.debug.dumpStackTrace(trace.*);
173 }
174 test_node.end();
175 },
176 }
177 }
178 root_node.end();
179 if (ok_count == test_fn_list.len) {
180 std.debug.print("All {d} tests passed.\n", .{ok_count});
181 } else {
182 std.debug.print("{d} passed; {d} skipped; {d} failed.\n", .{ ok_count, skip_count, fail_count });
183 }
184 if (log_err_count != 0) {
185 std.debug.print("{d} errors were logged.\n", .{log_err_count});
186 }
187 if (leaks != 0) {
188 std.debug.print("{d} tests leaked memory.\n", .{leaks});
189 }
190 if (leaks != 0 or log_err_count != 0 or fail_count != 0) {
191 std.process.exit(1);
192 }
193}
194
195pub fn log(
196 comptime message_level: std.log.Level,
197 comptime scope: @Type(.EnumLiteral),
198 comptime format: []const u8,
199 args: anytype,
200) void {
201 if (@intFromEnum(message_level) <= @intFromEnum(std.log.Level.err)) {
202 log_err_count +|= 1;
203 }
204 if (@intFromEnum(message_level) <= @intFromEnum(std.testing.log_level)) {
205 std.debug.print(
206 "[" ++ @tagName(scope) ++ "] (" ++ @tagName(message_level) ++ "): " ++ format ++ "\n",
207 args,
208 );
209 }
210}
211
212/// Simpler main(), exercising fewer language features, so that
213/// work-in-progress backends can handle it.
214pub fn mainSimple() anyerror!void {
215 const enable_print = false;
216 const print_all = false;
217
218 var passed: u64 = 0;
219 var skipped: u64 = 0;
220 var failed: u64 = 0;
221 const stderr = if (enable_print) std.io.getStdErr() else {};
222 for (builtin.test_functions) |test_fn| {
223 if (enable_print and print_all) {
224 stderr.writeAll(test_fn.name) catch {};
225 stderr.writeAll("... ") catch {};
226 }
227 test_fn.func() catch |err| {
228 if (enable_print and !print_all) {
229 stderr.writeAll(test_fn.name) catch {};
230 stderr.writeAll("... ") catch {};
231 }
232 if (err != error.SkipZigTest) {
233 if (enable_print) stderr.writeAll("FAIL\n") catch {};
234 failed += 1;
235 if (!enable_print) return err;
236 continue;
237 }
238 if (enable_print) stderr.writeAll("SKIP\n") catch {};
239 skipped += 1;
240 continue;
241 };
242 if (enable_print and print_all) stderr.writeAll("PASS\n") catch {};
243 passed += 1;
244 }
245 if (enable_print) {
246 stderr.writer().print("{} passed, {} skipped, {} failed\n", .{ passed, skipped, failed }) catch {};
247 if (failed != 0) std.process.exit(1);
248 }
249}
src/AstGen.zig deleted-13661
...@@ -1,13661 +0,0 @@
1//! Ingests an AST and produces ZIR code.
2const AstGen = @This();
3
4const std = @import("std");
5const Ast = std.zig.Ast;
6const mem = std.mem;
7const Allocator = std.mem.Allocator;
8const assert = std.debug.assert;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const StringIndexAdapter = std.hash_map.StringIndexAdapter;
11const StringIndexContext = std.hash_map.StringIndexContext;
12
13const isPrimitive = std.zig.primitives.isPrimitive;
14
15const Zir = @import("Zir.zig");
16const BuiltinFn = std.zig.BuiltinFn;
17const AstRlAnnotate = std.zig.AstRlAnnotate;
18
19gpa: Allocator,
20tree: *const Ast,
21/// The set of nodes which, given the choice, must expose a result pointer to
22/// sub-expressions. See `AstRlAnnotate` for details.
23nodes_need_rl: *const AstRlAnnotate.RlNeededSet,
24instructions: std.MultiArrayList(Zir.Inst) = .{},
25extra: ArrayListUnmanaged(u32) = .{},
26string_bytes: ArrayListUnmanaged(u8) = .{},
27/// Tracks the current byte offset within the source file.
28/// Used to populate line deltas in the ZIR. AstGen maintains
29/// this "cursor" throughout the entire AST lowering process in order
30/// to avoid starting over the line/column scan for every declaration, which
31/// would be O(N^2).
32source_offset: u32 = 0,
33/// Tracks the corresponding line of `source_offset`.
34/// This value is absolute.
35source_line: u32 = 0,
36/// Tracks the corresponding column of `source_offset`.
37/// This value is absolute.
38source_column: u32 = 0,
39/// Used for temporary allocations; freed after AstGen is complete.
40/// The resulting ZIR code has no references to anything in this arena.
41arena: Allocator,
42string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
43compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .{},
44/// The topmost block of the current function.
45fn_block: ?*GenZir = null,
46fn_var_args: bool = false,
47/// The return type of the current function. This may be a trivial `Ref`, or
48/// otherwise it refers to a `ret_type` instruction.
49fn_ret_ty: Zir.Inst.Ref = .none,
50/// Maps string table indexes to the first `@import` ZIR instruction
51/// that uses this string as the operand.
52imports: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .{},
53/// Used for temporary storage when building payloads.
54scratch: std.ArrayListUnmanaged(u32) = .{},
55/// Whenever a `ref` instruction is needed, it is created and saved in this
56/// table instead of being immediately appended to the current block body.
57/// Then, when the instruction is being added to the parent block (typically from
58/// setBlockBody), if it has a ref_table entry, then the ref instruction is added
59/// there. This makes sure two properties are upheld:
60/// 1. All pointers to the same locals return the same address. This is required
61/// to be compliant with the language specification.
62/// 2. `ref` instructions will dominate their uses. This is a required property
63/// of ZIR.
64/// The key is the ref operand; the value is the ref instruction.
65ref_table: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
66
67const InnerError = error{ OutOfMemory, AnalysisFail };
68
69fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 {
70 const fields = std.meta.fields(@TypeOf(extra));
71 try astgen.extra.ensureUnusedCapacity(astgen.gpa, fields.len);
72 return addExtraAssumeCapacity(astgen, extra);
73}
74
75fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
76 const fields = std.meta.fields(@TypeOf(extra));
77 const extra_index: u32 = @intCast(astgen.extra.items.len);
78 astgen.extra.items.len += fields.len;
79 setExtra(astgen, extra_index, extra);
80 return extra_index;
81}
82
83fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
84 const fields = std.meta.fields(@TypeOf(extra));
85 var i = index;
86 inline for (fields) |field| {
87 astgen.extra.items[i] = switch (field.type) {
88 u32 => @field(extra, field.name),
89
90 Zir.Inst.Ref,
91 Zir.Inst.Index,
92 Zir.Inst.Declaration.Name,
93 Zir.NullTerminatedString,
94 => @intFromEnum(@field(extra, field.name)),
95
96 i32,
97 Zir.Inst.Call.Flags,
98 Zir.Inst.BuiltinCall.Flags,
99 Zir.Inst.SwitchBlock.Bits,
100 Zir.Inst.SwitchBlockErrUnion.Bits,
101 Zir.Inst.FuncFancy.Bits,
102 Zir.Inst.Declaration.Flags,
103 => @bitCast(@field(extra, field.name)),
104
105 else => @compileError("bad field type"),
106 };
107 i += 1;
108 }
109}
110
111fn reserveExtra(astgen: *AstGen, size: usize) Allocator.Error!u32 {
112 const extra_index: u32 = @intCast(astgen.extra.items.len);
113 try astgen.extra.resize(astgen.gpa, extra_index + size);
114 return extra_index;
115}
116
117fn appendRefs(astgen: *AstGen, refs: []const Zir.Inst.Ref) !void {
118 return astgen.extra.appendSlice(astgen.gpa, @ptrCast(refs));
119}
120
121fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void {
122 astgen.extra.appendSliceAssumeCapacity(@ptrCast(refs));
123}
124
125pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
126 var arena = std.heap.ArenaAllocator.init(gpa);
127 defer arena.deinit();
128
129 var nodes_need_rl = try AstRlAnnotate.annotate(gpa, arena.allocator(), tree);
130 defer nodes_need_rl.deinit(gpa);
131
132 var astgen: AstGen = .{
133 .gpa = gpa,
134 .arena = arena.allocator(),
135 .tree = &tree,
136 .nodes_need_rl = &nodes_need_rl,
137 };
138 defer astgen.deinit(gpa);
139
140 // String table index 0 is reserved for `NullTerminatedString.empty`.
141 try astgen.string_bytes.append(gpa, 0);
142
143 // We expect at least as many ZIR instructions and extra data items
144 // as AST nodes.
145 try astgen.instructions.ensureTotalCapacity(gpa, tree.nodes.len);
146
147 // First few indexes of extra are reserved and set at the end.
148 const reserved_count = @typeInfo(Zir.ExtraIndex).Enum.fields.len;
149 try astgen.extra.ensureTotalCapacity(gpa, tree.nodes.len + reserved_count);
150 astgen.extra.items.len += reserved_count;
151
152 var top_scope: Scope.Top = .{};
153
154 var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
155 var gen_scope: GenZir = .{
156 .is_comptime = true,
157 .parent = &top_scope.base,
158 .anon_name_strategy = .parent,
159 .decl_node_index = 0,
160 .decl_line = 0,
161 .astgen = &astgen,
162 .instructions = &gz_instructions,
163 .instructions_top = 0,
164 };
165 defer gz_instructions.deinit(gpa);
166
167 // The AST -> ZIR lowering process assumes an AST that does not have any
168 // parse errors.
169 if (tree.errors.len == 0) {
170 if (AstGen.structDeclInner(
171 &gen_scope,
172 &gen_scope.base,
173 0,
174 tree.containerDeclRoot(),
175 .Auto,
176 0,
177 )) |struct_decl_ref| {
178 assert(struct_decl_ref.toIndex().? == .main_struct_inst);
179 } else |err| switch (err) {
180 error.OutOfMemory => return error.OutOfMemory,
181 error.AnalysisFail => {}, // Handled via compile_errors below.
182 }
183 } else {
184 try lowerAstErrors(&astgen);
185 }
186
187 const err_index = @intFromEnum(Zir.ExtraIndex.compile_errors);
188 if (astgen.compile_errors.items.len == 0) {
189 astgen.extra.items[err_index] = 0;
190 } else {
191 try astgen.extra.ensureUnusedCapacity(gpa, 1 + astgen.compile_errors.items.len *
192 @typeInfo(Zir.Inst.CompileErrors.Item).Struct.fields.len);
193
194 astgen.extra.items[err_index] = astgen.addExtraAssumeCapacity(Zir.Inst.CompileErrors{
195 .items_len = @intCast(astgen.compile_errors.items.len),
196 });
197
198 for (astgen.compile_errors.items) |item| {
199 _ = astgen.addExtraAssumeCapacity(item);
200 }
201 }
202
203 const imports_index = @intFromEnum(Zir.ExtraIndex.imports);
204 if (astgen.imports.count() == 0) {
205 astgen.extra.items[imports_index] = 0;
206 } else {
207 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Imports).Struct.fields.len +
208 astgen.imports.count() * @typeInfo(Zir.Inst.Imports.Item).Struct.fields.len);
209
210 astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{
211 .imports_len = @intCast(astgen.imports.count()),
212 });
213
214 var it = astgen.imports.iterator();
215 while (it.next()) |entry| {
216 _ = astgen.addExtraAssumeCapacity(Zir.Inst.Imports.Item{
217 .name = entry.key_ptr.*,
218 .token = entry.value_ptr.*,
219 });
220 }
221 }
222
223 return Zir{
224 .instructions = astgen.instructions.toOwnedSlice(),
225 .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
226 .extra = try astgen.extra.toOwnedSlice(gpa),
227 };
228}
229
230fn deinit(astgen: *AstGen, gpa: Allocator) void {
231 astgen.instructions.deinit(gpa);
232 astgen.extra.deinit(gpa);
233 astgen.string_table.deinit(gpa);
234 astgen.string_bytes.deinit(gpa);
235 astgen.compile_errors.deinit(gpa);
236 astgen.imports.deinit(gpa);
237 astgen.scratch.deinit(gpa);
238 astgen.ref_table.deinit(gpa);
239}
240
241const ResultInfo = struct {
242 /// The semantics requested for the result location
243 rl: Loc,
244
245 /// The "operator" consuming the result location
246 ctx: Context = .none,
247
248 /// Turns a `coerced_ty` back into a `ty`. Should be called at branch points
249 /// such as if and switch expressions.
250 fn br(ri: ResultInfo) ResultInfo {
251 return switch (ri.rl) {
252 .coerced_ty => |ty| .{
253 .rl = .{ .ty = ty },
254 .ctx = ri.ctx,
255 },
256 else => ri,
257 };
258 }
259
260 fn zirTag(ri: ResultInfo) Zir.Inst.Tag {
261 switch (ri.rl) {
262 .ty => return switch (ri.ctx) {
263 .shift_op => .as_shift_operand,
264 else => .as_node,
265 },
266 else => unreachable,
267 }
268 }
269
270 const Loc = union(enum) {
271 /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
272 /// expression should be generated. The result instruction from the expression must
273 /// be ignored.
274 discard,
275 /// The expression has an inferred type, and it will be evaluated as an rvalue.
276 none,
277 /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
278 ty: Zir.Inst.Ref,
279 /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion,
280 /// so no `as` instruction needs to be emitted.
281 coerced_ty: Zir.Inst.Ref,
282 /// The expression must generate a pointer rather than a value. For example, the left hand side
283 /// of an assignment uses this kind of result location.
284 ref,
285 /// The expression must generate a pointer rather than a value, and the pointer will be coerced
286 /// by other code to this type, which is guaranteed by earlier instructions to be a pointer type.
287 ref_coerced_ty: Zir.Inst.Ref,
288 /// The expression must store its result into this typed pointer. The result instruction
289 /// from the expression must be ignored.
290 ptr: PtrResultLoc,
291 /// The expression must store its result into this allocation, which has an inferred type.
292 /// The result instruction from the expression must be ignored.
293 /// Always an instruction with tag `alloc_inferred`.
294 inferred_ptr: Zir.Inst.Ref,
295 /// The expression has a sequence of pointers to store its results into due to a destructure
296 /// operation. Each of these pointers may or may not have an inferred type.
297 destructure: struct {
298 /// The AST node of the destructure operation itself.
299 src_node: Ast.Node.Index,
300 /// The pointers to store results into.
301 components: []const DestructureComponent,
302 },
303
304 const DestructureComponent = union(enum) {
305 typed_ptr: PtrResultLoc,
306 inferred_ptr: Zir.Inst.Ref,
307 discard,
308 };
309
310 const PtrResultLoc = struct {
311 inst: Zir.Inst.Ref,
312 src_node: ?Ast.Node.Index = null,
313 };
314
315 /// Find the result type for a cast builtin given the result location.
316 /// If the location does not have a known result type, emits an error on
317 /// the given node.
318 fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref {
319 return switch (rl) {
320 .discard, .none, .ref, .inferred_ptr, .destructure => null,
321 .ty, .coerced_ty => |ty_ref| ty_ref,
322 .ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node),
323 .ptr => |ptr| {
324 const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node);
325 return try gz.addUnNode(.elem_type, ptr_ty, node);
326 },
327 };
328 }
329
330 fn resultTypeForCast(rl: Loc, gz: *GenZir, node: Ast.Node.Index, builtin_name: []const u8) !Zir.Inst.Ref {
331 const astgen = gz.astgen;
332 if (try rl.resultType(gz, node)) |ty| return ty;
333 switch (rl) {
334 .destructure => |destructure| return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
335 try astgen.errNoteNode(destructure.src_node, "destructure expressions do not provide a single result type", .{}),
336 try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
337 }),
338 else => return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
339 try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
340 }),
341 }
342 }
343 };
344
345 const Context = enum {
346 /// The expression is the operand to a return expression.
347 @"return",
348 /// The expression is the input to an error-handling operator (if-else, try, or catch).
349 error_handling_expr,
350 /// The expression is the right-hand side of a shift operation.
351 shift_op,
352 /// The expression is an argument in a function call.
353 fn_arg,
354 /// The expression is the right-hand side of an initializer for a `const` variable
355 const_init,
356 /// The expression is the right-hand side of an assignment expression.
357 assignment,
358 /// No specific operator in particular.
359 none,
360 };
361};
362
363const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } };
364const coerced_addrspace_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .address_space_type } };
365const coerced_linksection_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .slice_const_u8_type } };
366const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } };
367const coerced_bool_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .bool_type } };
368
369fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
370 return comptimeExpr(gz, scope, coerced_type_ri, type_node);
371}
372
373fn reachableTypeExpr(
374 gz: *GenZir,
375 scope: *Scope,
376 type_node: Ast.Node.Index,
377 reachable_node: Ast.Node.Index,
378) InnerError!Zir.Inst.Ref {
379 return reachableExprComptime(gz, scope, coerced_type_ri, type_node, reachable_node, true);
380}
381
382/// Same as `expr` but fails with a compile error if the result type is `noreturn`.
383fn reachableExpr(
384 gz: *GenZir,
385 scope: *Scope,
386 ri: ResultInfo,
387 node: Ast.Node.Index,
388 reachable_node: Ast.Node.Index,
389) InnerError!Zir.Inst.Ref {
390 return reachableExprComptime(gz, scope, ri, node, reachable_node, false);
391}
392
393fn reachableExprComptime(
394 gz: *GenZir,
395 scope: *Scope,
396 ri: ResultInfo,
397 node: Ast.Node.Index,
398 reachable_node: Ast.Node.Index,
399 force_comptime: bool,
400) InnerError!Zir.Inst.Ref {
401 const result_inst = if (force_comptime)
402 try comptimeExpr(gz, scope, ri, node)
403 else
404 try expr(gz, scope, ri, node);
405
406 if (gz.refIsNoReturn(result_inst)) {
407 try gz.astgen.appendErrorNodeNotes(reachable_node, "unreachable code", .{}, &[_]u32{
408 try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}),
409 });
410 }
411 return result_inst;
412}
413
414fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
415 const astgen = gz.astgen;
416 const tree = astgen.tree;
417 const node_tags = tree.nodes.items(.tag);
418 const main_tokens = tree.nodes.items(.main_token);
419 switch (node_tags[node]) {
420 .root => unreachable,
421 .@"usingnamespace" => unreachable,
422 .test_decl => unreachable,
423 .global_var_decl => unreachable,
424 .local_var_decl => unreachable,
425 .simple_var_decl => unreachable,
426 .aligned_var_decl => unreachable,
427 .switch_case => unreachable,
428 .switch_case_inline => unreachable,
429 .switch_case_one => unreachable,
430 .switch_case_inline_one => unreachable,
431 .container_field_init => unreachable,
432 .container_field_align => unreachable,
433 .container_field => unreachable,
434 .asm_output => unreachable,
435 .asm_input => unreachable,
436
437 .assign,
438 .assign_destructure,
439 .assign_bit_and,
440 .assign_bit_or,
441 .assign_shl,
442 .assign_shl_sat,
443 .assign_shr,
444 .assign_bit_xor,
445 .assign_div,
446 .assign_sub,
447 .assign_sub_wrap,
448 .assign_sub_sat,
449 .assign_mod,
450 .assign_add,
451 .assign_add_wrap,
452 .assign_add_sat,
453 .assign_mul,
454 .assign_mul_wrap,
455 .assign_mul_sat,
456 .add,
457 .add_wrap,
458 .add_sat,
459 .sub,
460 .sub_wrap,
461 .sub_sat,
462 .mul,
463 .mul_wrap,
464 .mul_sat,
465 .div,
466 .mod,
467 .bit_and,
468 .bit_or,
469 .shl,
470 .shl_sat,
471 .shr,
472 .bit_xor,
473 .bang_equal,
474 .equal_equal,
475 .greater_than,
476 .greater_or_equal,
477 .less_than,
478 .less_or_equal,
479 .array_cat,
480 .array_mult,
481 .bool_and,
482 .bool_or,
483 .@"asm",
484 .asm_simple,
485 .string_literal,
486 .number_literal,
487 .call,
488 .call_comma,
489 .async_call,
490 .async_call_comma,
491 .call_one,
492 .call_one_comma,
493 .async_call_one,
494 .async_call_one_comma,
495 .unreachable_literal,
496 .@"return",
497 .@"if",
498 .if_simple,
499 .@"while",
500 .while_simple,
501 .while_cont,
502 .bool_not,
503 .address_of,
504 .optional_type,
505 .block,
506 .block_semicolon,
507 .block_two,
508 .block_two_semicolon,
509 .@"break",
510 .ptr_type_aligned,
511 .ptr_type_sentinel,
512 .ptr_type,
513 .ptr_type_bit_range,
514 .array_type,
515 .array_type_sentinel,
516 .enum_literal,
517 .multiline_string_literal,
518 .char_literal,
519 .@"defer",
520 .@"errdefer",
521 .@"catch",
522 .error_union,
523 .merge_error_sets,
524 .switch_range,
525 .for_range,
526 .@"await",
527 .bit_not,
528 .negation,
529 .negation_wrap,
530 .@"resume",
531 .@"try",
532 .slice,
533 .slice_open,
534 .slice_sentinel,
535 .array_init_one,
536 .array_init_one_comma,
537 .array_init_dot_two,
538 .array_init_dot_two_comma,
539 .array_init_dot,
540 .array_init_dot_comma,
541 .array_init,
542 .array_init_comma,
543 .struct_init_one,
544 .struct_init_one_comma,
545 .struct_init_dot_two,
546 .struct_init_dot_two_comma,
547 .struct_init_dot,
548 .struct_init_dot_comma,
549 .struct_init,
550 .struct_init_comma,
551 .@"switch",
552 .switch_comma,
553 .@"for",
554 .for_simple,
555 .@"suspend",
556 .@"continue",
557 .fn_proto_simple,
558 .fn_proto_multi,
559 .fn_proto_one,
560 .fn_proto,
561 .fn_decl,
562 .anyframe_type,
563 .anyframe_literal,
564 .error_set_decl,
565 .container_decl,
566 .container_decl_trailing,
567 .container_decl_two,
568 .container_decl_two_trailing,
569 .container_decl_arg,
570 .container_decl_arg_trailing,
571 .tagged_union,
572 .tagged_union_trailing,
573 .tagged_union_two,
574 .tagged_union_two_trailing,
575 .tagged_union_enum_tag,
576 .tagged_union_enum_tag_trailing,
577 .@"comptime",
578 .@"nosuspend",
579 .error_value,
580 => return astgen.failNode(node, "invalid left-hand side to assignment", .{}),
581
582 .builtin_call,
583 .builtin_call_comma,
584 .builtin_call_two,
585 .builtin_call_two_comma,
586 => {
587 const builtin_token = main_tokens[node];
588 const builtin_name = tree.tokenSlice(builtin_token);
589 // If the builtin is an invalid name, we don't cause an error here; instead
590 // let it pass, and the error will be "invalid builtin function" later.
591 if (BuiltinFn.list.get(builtin_name)) |info| {
592 if (!info.allows_lvalue) {
593 return astgen.failNode(node, "invalid left-hand side to assignment", .{});
594 }
595 }
596 },
597
598 // These can be assigned to.
599 .unwrap_optional,
600 .deref,
601 .field_access,
602 .array_access,
603 .identifier,
604 .grouped_expression,
605 .@"orelse",
606 => {},
607 }
608 return expr(gz, scope, .{ .rl = .ref }, node);
609}
610
611/// Turn Zig AST into untyped ZIR instructions.
612/// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the
613/// result instruction can be used to inspect whether it is isNoReturn() but that is it,
614/// it must otherwise not be used.
615fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
616 const astgen = gz.astgen;
617 const tree = astgen.tree;
618 const main_tokens = tree.nodes.items(.main_token);
619 const token_tags = tree.tokens.items(.tag);
620 const node_datas = tree.nodes.items(.data);
621 const node_tags = tree.nodes.items(.tag);
622
623 const prev_anon_name_strategy = gz.anon_name_strategy;
624 defer gz.anon_name_strategy = prev_anon_name_strategy;
625 if (!nodeUsesAnonNameStrategy(tree, node)) {
626 gz.anon_name_strategy = .anon;
627 }
628
629 switch (node_tags[node]) {
630 .root => unreachable, // Top-level declaration.
631 .@"usingnamespace" => unreachable, // Top-level declaration.
632 .test_decl => unreachable, // Top-level declaration.
633 .container_field_init => unreachable, // Top-level declaration.
634 .container_field_align => unreachable, // Top-level declaration.
635 .container_field => unreachable, // Top-level declaration.
636 .fn_decl => unreachable, // Top-level declaration.
637
638 .global_var_decl => unreachable, // Handled in `blockExpr`.
639 .local_var_decl => unreachable, // Handled in `blockExpr`.
640 .simple_var_decl => unreachable, // Handled in `blockExpr`.
641 .aligned_var_decl => unreachable, // Handled in `blockExpr`.
642 .@"defer" => unreachable, // Handled in `blockExpr`.
643 .@"errdefer" => unreachable, // Handled in `blockExpr`.
644
645 .switch_case => unreachable, // Handled in `switchExpr`.
646 .switch_case_inline => unreachable, // Handled in `switchExpr`.
647 .switch_case_one => unreachable, // Handled in `switchExpr`.
648 .switch_case_inline_one => unreachable, // Handled in `switchExpr`.
649 .switch_range => unreachable, // Handled in `switchExpr`.
650
651 .asm_output => unreachable, // Handled in `asmExpr`.
652 .asm_input => unreachable, // Handled in `asmExpr`.
653
654 .for_range => unreachable, // Handled in `forExpr`.
655
656 .assign => {
657 try assign(gz, scope, node);
658 return rvalue(gz, ri, .void_value, node);
659 },
660
661 .assign_destructure => {
662 // Note that this variant does not declare any new var/const: that
663 // variant is handled by `blockExprStmts`.
664 try assignDestructure(gz, scope, node);
665 return rvalue(gz, ri, .void_value, node);
666 },
667
668 .assign_shl => {
669 try assignShift(gz, scope, node, .shl);
670 return rvalue(gz, ri, .void_value, node);
671 },
672 .assign_shl_sat => {
673 try assignShiftSat(gz, scope, node);
674 return rvalue(gz, ri, .void_value, node);
675 },
676 .assign_shr => {
677 try assignShift(gz, scope, node, .shr);
678 return rvalue(gz, ri, .void_value, node);
679 },
680
681 .assign_bit_and => {
682 try assignOp(gz, scope, node, .bit_and);
683 return rvalue(gz, ri, .void_value, node);
684 },
685 .assign_bit_or => {
686 try assignOp(gz, scope, node, .bit_or);
687 return rvalue(gz, ri, .void_value, node);
688 },
689 .assign_bit_xor => {
690 try assignOp(gz, scope, node, .xor);
691 return rvalue(gz, ri, .void_value, node);
692 },
693 .assign_div => {
694 try assignOp(gz, scope, node, .div);
695 return rvalue(gz, ri, .void_value, node);
696 },
697 .assign_sub => {
698 try assignOp(gz, scope, node, .sub);
699 return rvalue(gz, ri, .void_value, node);
700 },
701 .assign_sub_wrap => {
702 try assignOp(gz, scope, node, .subwrap);
703 return rvalue(gz, ri, .void_value, node);
704 },
705 .assign_sub_sat => {
706 try assignOp(gz, scope, node, .sub_sat);
707 return rvalue(gz, ri, .void_value, node);
708 },
709 .assign_mod => {
710 try assignOp(gz, scope, node, .mod_rem);
711 return rvalue(gz, ri, .void_value, node);
712 },
713 .assign_add => {
714 try assignOp(gz, scope, node, .add);
715 return rvalue(gz, ri, .void_value, node);
716 },
717 .assign_add_wrap => {
718 try assignOp(gz, scope, node, .addwrap);
719 return rvalue(gz, ri, .void_value, node);
720 },
721 .assign_add_sat => {
722 try assignOp(gz, scope, node, .add_sat);
723 return rvalue(gz, ri, .void_value, node);
724 },
725 .assign_mul => {
726 try assignOp(gz, scope, node, .mul);
727 return rvalue(gz, ri, .void_value, node);
728 },
729 .assign_mul_wrap => {
730 try assignOp(gz, scope, node, .mulwrap);
731 return rvalue(gz, ri, .void_value, node);
732 },
733 .assign_mul_sat => {
734 try assignOp(gz, scope, node, .mul_sat);
735 return rvalue(gz, ri, .void_value, node);
736 },
737
738 // zig fmt: off
739 .shl => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
740 .shr => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
741
742 .add => return simpleBinOp(gz, scope, ri, node, .add),
743 .add_wrap => return simpleBinOp(gz, scope, ri, node, .addwrap),
744 .add_sat => return simpleBinOp(gz, scope, ri, node, .add_sat),
745 .sub => return simpleBinOp(gz, scope, ri, node, .sub),
746 .sub_wrap => return simpleBinOp(gz, scope, ri, node, .subwrap),
747 .sub_sat => return simpleBinOp(gz, scope, ri, node, .sub_sat),
748 .mul => return simpleBinOp(gz, scope, ri, node, .mul),
749 .mul_wrap => return simpleBinOp(gz, scope, ri, node, .mulwrap),
750 .mul_sat => return simpleBinOp(gz, scope, ri, node, .mul_sat),
751 .div => return simpleBinOp(gz, scope, ri, node, .div),
752 .mod => return simpleBinOp(gz, scope, ri, node, .mod_rem),
753 .shl_sat => return simpleBinOp(gz, scope, ri, node, .shl_sat),
754
755 .bit_and => return simpleBinOp(gz, scope, ri, node, .bit_and),
756 .bit_or => return simpleBinOp(gz, scope, ri, node, .bit_or),
757 .bit_xor => return simpleBinOp(gz, scope, ri, node, .xor),
758 .bang_equal => return simpleBinOp(gz, scope, ri, node, .cmp_neq),
759 .equal_equal => return simpleBinOp(gz, scope, ri, node, .cmp_eq),
760 .greater_than => return simpleBinOp(gz, scope, ri, node, .cmp_gt),
761 .greater_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_gte),
762 .less_than => return simpleBinOp(gz, scope, ri, node, .cmp_lt),
763 .less_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_lte),
764 .array_cat => return simpleBinOp(gz, scope, ri, node, .array_cat),
765
766 .array_mult => {
767 // This syntax form does not currently use the result type in the language specification.
768 // However, the result type can be used to emit more optimal code for large multiplications by
769 // having Sema perform a coercion before the multiplication operation.
770 const result = try gz.addPlNode(.array_mul, node, Zir.Inst.ArrayMul{
771 .res_ty = if (try ri.rl.resultType(gz, node)) |t| t else .none,
772 .lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs),
773 .rhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs),
774 });
775 return rvalue(gz, ri, result, node);
776 },
777
778 .error_union => return simpleBinOp(gz, scope, ri, node, .error_union_type),
779 .merge_error_sets => return simpleBinOp(gz, scope, ri, node, .merge_error_sets),
780
781 .bool_and => return boolBinOp(gz, scope, ri, node, .bool_br_and),
782 .bool_or => return boolBinOp(gz, scope, ri, node, .bool_br_or),
783
784 .bool_not => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, node_datas[node].lhs, .bool_not),
785 .bit_not => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .bit_not),
786
787 .negation => return negation(gz, scope, ri, node),
788 .negation_wrap => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .negate_wrap),
789
790 .identifier => return identifier(gz, scope, ri, node),
791
792 .asm_simple,
793 .@"asm",
794 => return asmExpr(gz, scope, ri, node, tree.fullAsm(node).?),
795
796 .string_literal => return stringLiteral(gz, ri, node),
797 .multiline_string_literal => return multilineStringLiteral(gz, ri, node),
798
799 .number_literal => return numberLiteral(gz, ri, node, node, .positive),
800 // zig fmt: on
801
802 .builtin_call_two, .builtin_call_two_comma => {
803 if (node_datas[node].lhs == 0) {
804 const params = [_]Ast.Node.Index{};
805 return builtinCall(gz, scope, ri, node, &params);
806 } else if (node_datas[node].rhs == 0) {
807 const params = [_]Ast.Node.Index{node_datas[node].lhs};
808 return builtinCall(gz, scope, ri, node, &params);
809 } else {
810 const params = [_]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
811 return builtinCall(gz, scope, ri, node, &params);
812 }
813 },
814 .builtin_call, .builtin_call_comma => {
815 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
816 return builtinCall(gz, scope, ri, node, params);
817 },
818
819 .call_one,
820 .call_one_comma,
821 .async_call_one,
822 .async_call_one_comma,
823 .call,
824 .call_comma,
825 .async_call,
826 .async_call_comma,
827 => {
828 var buf: [1]Ast.Node.Index = undefined;
829 return callExpr(gz, scope, ri, node, tree.fullCall(&buf, node).?);
830 },
831
832 .unreachable_literal => {
833 try emitDbgNode(gz, node);
834 _ = try gz.addAsIndex(.{
835 .tag = .@"unreachable",
836 .data = .{ .@"unreachable" = .{
837 .src_node = gz.nodeIndexToRelative(node),
838 } },
839 });
840 return Zir.Inst.Ref.unreachable_value;
841 },
842 .@"return" => return ret(gz, scope, node),
843 .field_access => return fieldAccess(gz, scope, ri, node),
844
845 .if_simple,
846 .@"if",
847 => {
848 const if_full = tree.fullIf(node).?;
849 no_switch_on_err: {
850 const error_token = if_full.error_token orelse break :no_switch_on_err;
851 switch (node_tags[if_full.ast.else_expr]) {
852 .@"switch", .switch_comma => {},
853 else => break :no_switch_on_err,
854 }
855 const switch_operand = node_datas[if_full.ast.else_expr].lhs;
856 if (node_tags[switch_operand] != .identifier) break :no_switch_on_err;
857 if (!mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(main_tokens[switch_operand]))) break :no_switch_on_err;
858 return switchExprErrUnion(gz, scope, ri.br(), node, .@"if");
859 }
860 return ifExpr(gz, scope, ri.br(), node, if_full);
861 },
862
863 .while_simple,
864 .while_cont,
865 .@"while",
866 => return whileExpr(gz, scope, ri.br(), node, tree.fullWhile(node).?, false),
867
868 .for_simple, .@"for" => return forExpr(gz, scope, ri.br(), node, tree.fullFor(node).?, false),
869
870 .slice_open => {
871 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
872
873 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
874 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
875 try emitDbgStmt(gz, cursor);
876 const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{
877 .lhs = lhs,
878 .start = start,
879 });
880 return rvalue(gz, ri, result, node);
881 },
882 .slice => {
883 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
884 const lhs_node = node_datas[node].lhs;
885 const lhs_tag = node_tags[lhs_node];
886 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
887 const lhs_is_open_slice = lhs_tag == .slice_open or
888 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
889 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
890 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
891
892 const start = if (lhs_is_slice_sentinel) start: {
893 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
894 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
895 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
896
897 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
898 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
899 try emitDbgStmt(gz, cursor);
900 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
901 .lhs = lhs,
902 .start = start,
903 .len = len,
904 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
905 .sentinel = .none,
906 });
907 return rvalue(gz, ri, result, node);
908 }
909 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
910
911 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
912 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
913 const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end);
914 try emitDbgStmt(gz, cursor);
915 const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{
916 .lhs = lhs,
917 .start = start,
918 .end = end,
919 });
920 return rvalue(gz, ri, result, node);
921 },
922 .slice_sentinel => {
923 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
924 const lhs_node = node_datas[node].lhs;
925 const lhs_tag = node_tags[lhs_node];
926 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
927 const lhs_is_open_slice = lhs_tag == .slice_open or
928 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
929 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
930 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
931
932 const start = if (lhs_is_slice_sentinel) start: {
933 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
934 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
935 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
936
937 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
938 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
939 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
940 try emitDbgStmt(gz, cursor);
941 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
942 .lhs = lhs,
943 .start = start,
944 .len = len,
945 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
946 .sentinel = sentinel,
947 });
948 return rvalue(gz, ri, result, node);
949 }
950 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
951
952 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
953 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
954 const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
955 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
956 try emitDbgStmt(gz, cursor);
957 const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
958 .lhs = lhs,
959 .start = start,
960 .end = end,
961 .sentinel = sentinel,
962 });
963 return rvalue(gz, ri, result, node);
964 },
965
966 .deref => {
967 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
968 _ = try gz.addUnNode(.validate_deref, lhs, node);
969 switch (ri.rl) {
970 .ref, .ref_coerced_ty => return lhs,
971 else => {
972 const result = try gz.addUnNode(.load, lhs, node);
973 return rvalue(gz, ri, result, node);
974 },
975 }
976 },
977 .address_of => {
978 const operand_rl: ResultInfo.Loc = if (try ri.rl.resultType(gz, node)) |res_ty_inst| rl: {
979 _ = try gz.addUnTok(.validate_ref_ty, res_ty_inst, tree.firstToken(node));
980 break :rl .{ .ref_coerced_ty = res_ty_inst };
981 } else .ref;
982 const result = try expr(gz, scope, .{ .rl = operand_rl }, node_datas[node].lhs);
983 return rvalue(gz, ri, result, node);
984 },
985 .optional_type => {
986 const operand = try typeExpr(gz, scope, node_datas[node].lhs);
987 const result = try gz.addUnNode(.optional_type, operand, node);
988 return rvalue(gz, ri, result, node);
989 },
990 .unwrap_optional => switch (ri.rl) {
991 .ref, .ref_coerced_ty => {
992 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
993
994 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
995 try emitDbgStmt(gz, cursor);
996
997 return gz.addUnNode(.optional_payload_safe_ptr, lhs, node);
998 },
999 else => {
1000 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
1001
1002 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
1003 try emitDbgStmt(gz, cursor);
1004
1005 return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node);
1006 },
1007 },
1008 .block_two, .block_two_semicolon => {
1009 const statements = [2]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
1010 if (node_datas[node].lhs == 0) {
1011 return blockExpr(gz, scope, ri, node, statements[0..0]);
1012 } else if (node_datas[node].rhs == 0) {
1013 return blockExpr(gz, scope, ri, node, statements[0..1]);
1014 } else {
1015 return blockExpr(gz, scope, ri, node, statements[0..2]);
1016 }
1017 },
1018 .block, .block_semicolon => {
1019 const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
1020 return blockExpr(gz, scope, ri, node, statements);
1021 },
1022 .enum_literal => return simpleStrTok(gz, ri, main_tokens[node], node, .enum_literal),
1023 .error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value),
1024 // TODO restore this when implementing https://github.com/ziglang/zig/issues/6025
1025 // .anyframe_literal => return rvalue(gz, ri, .anyframe_type, node),
1026 .anyframe_literal => {
1027 const result = try gz.addUnNode(.anyframe_type, .void_type, node);
1028 return rvalue(gz, ri, result, node);
1029 },
1030 .anyframe_type => {
1031 const return_type = try typeExpr(gz, scope, node_datas[node].rhs);
1032 const result = try gz.addUnNode(.anyframe_type, return_type, node);
1033 return rvalue(gz, ri, result, node);
1034 },
1035 .@"catch" => {
1036 const catch_token = main_tokens[node];
1037 const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)
1038 catch_token + 2
1039 else
1040 null;
1041 no_switch_on_err: {
1042 const capture_token = payload_token orelse break :no_switch_on_err;
1043 switch (node_tags[node_datas[node].rhs]) {
1044 .@"switch", .switch_comma => {},
1045 else => break :no_switch_on_err,
1046 }
1047 const switch_operand = node_datas[node_datas[node].rhs].lhs;
1048 if (node_tags[switch_operand] != .identifier) break :no_switch_on_err;
1049 if (!mem.eql(u8, tree.tokenSlice(capture_token), tree.tokenSlice(main_tokens[switch_operand]))) break :no_switch_on_err;
1050 return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch");
1051 }
1052 switch (ri.rl) {
1053 .ref, .ref_coerced_ty => return orelseCatchExpr(
1054 gz,
1055 scope,
1056 ri,
1057 node,
1058 node_datas[node].lhs,
1059 .is_non_err_ptr,
1060 .err_union_payload_unsafe_ptr,
1061 .err_union_code_ptr,
1062 node_datas[node].rhs,
1063 payload_token,
1064 ),
1065 else => return orelseCatchExpr(
1066 gz,
1067 scope,
1068 ri,
1069 node,
1070 node_datas[node].lhs,
1071 .is_non_err,
1072 .err_union_payload_unsafe,
1073 .err_union_code,
1074 node_datas[node].rhs,
1075 payload_token,
1076 ),
1077 }
1078 },
1079 .@"orelse" => switch (ri.rl) {
1080 .ref, .ref_coerced_ty => return orelseCatchExpr(
1081 gz,
1082 scope,
1083 ri,
1084 node,
1085 node_datas[node].lhs,
1086 .is_non_null_ptr,
1087 .optional_payload_unsafe_ptr,
1088 undefined,
1089 node_datas[node].rhs,
1090 null,
1091 ),
1092 else => return orelseCatchExpr(
1093 gz,
1094 scope,
1095 ri,
1096 node,
1097 node_datas[node].lhs,
1098 .is_non_null,
1099 .optional_payload_unsafe,
1100 undefined,
1101 node_datas[node].rhs,
1102 null,
1103 ),
1104 },
1105
1106 .ptr_type_aligned,
1107 .ptr_type_sentinel,
1108 .ptr_type,
1109 .ptr_type_bit_range,
1110 => return ptrType(gz, scope, ri, node, tree.fullPtrType(node).?),
1111
1112 .container_decl,
1113 .container_decl_trailing,
1114 .container_decl_arg,
1115 .container_decl_arg_trailing,
1116 .container_decl_two,
1117 .container_decl_two_trailing,
1118 .tagged_union,
1119 .tagged_union_trailing,
1120 .tagged_union_enum_tag,
1121 .tagged_union_enum_tag_trailing,
1122 .tagged_union_two,
1123 .tagged_union_two_trailing,
1124 => {
1125 var buf: [2]Ast.Node.Index = undefined;
1126 return containerDecl(gz, scope, ri, node, tree.fullContainerDecl(&buf, node).?);
1127 },
1128
1129 .@"break" => return breakExpr(gz, scope, node),
1130 .@"continue" => return continueExpr(gz, scope, node),
1131 .grouped_expression => return expr(gz, scope, ri, node_datas[node].lhs),
1132 .array_type => return arrayType(gz, scope, ri, node),
1133 .array_type_sentinel => return arrayTypeSentinel(gz, scope, ri, node),
1134 .char_literal => return charLiteral(gz, ri, node),
1135 .error_set_decl => return errorSetDecl(gz, ri, node),
1136 .array_access => return arrayAccess(gz, scope, ri, node),
1137 .@"comptime" => return comptimeExprAst(gz, scope, ri, node),
1138 .@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node),
1139
1140 .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node),
1141 .@"suspend" => return suspendExpr(gz, scope, node),
1142 .@"await" => return awaitExpr(gz, scope, ri, node),
1143 .@"resume" => return resumeExpr(gz, scope, ri, node),
1144
1145 .@"try" => return tryExpr(gz, scope, ri, node, node_datas[node].lhs),
1146
1147 .array_init_one,
1148 .array_init_one_comma,
1149 .array_init_dot_two,
1150 .array_init_dot_two_comma,
1151 .array_init_dot,
1152 .array_init_dot_comma,
1153 .array_init,
1154 .array_init_comma,
1155 => {
1156 var buf: [2]Ast.Node.Index = undefined;
1157 return arrayInitExpr(gz, scope, ri, node, tree.fullArrayInit(&buf, node).?);
1158 },
1159
1160 .struct_init_one,
1161 .struct_init_one_comma,
1162 .struct_init_dot_two,
1163 .struct_init_dot_two_comma,
1164 .struct_init_dot,
1165 .struct_init_dot_comma,
1166 .struct_init,
1167 .struct_init_comma,
1168 => {
1169 var buf: [2]Ast.Node.Index = undefined;
1170 return structInitExpr(gz, scope, ri, node, tree.fullStructInit(&buf, node).?);
1171 },
1172
1173 .fn_proto_simple,
1174 .fn_proto_multi,
1175 .fn_proto_one,
1176 .fn_proto,
1177 => {
1178 var buf: [1]Ast.Node.Index = undefined;
1179 return fnProtoExpr(gz, scope, ri, node, tree.fullFnProto(&buf, node).?);
1180 },
1181 }
1182}
1183
1184fn nosuspendExpr(
1185 gz: *GenZir,
1186 scope: *Scope,
1187 ri: ResultInfo,
1188 node: Ast.Node.Index,
1189) InnerError!Zir.Inst.Ref {
1190 const astgen = gz.astgen;
1191 const tree = astgen.tree;
1192 const node_datas = tree.nodes.items(.data);
1193 const body_node = node_datas[node].lhs;
1194 assert(body_node != 0);
1195 if (gz.nosuspend_node != 0) {
1196 try astgen.appendErrorNodeNotes(node, "redundant nosuspend block", .{}, &[_]u32{
1197 try astgen.errNoteNode(gz.nosuspend_node, "other nosuspend block here", .{}),
1198 });
1199 }
1200 gz.nosuspend_node = node;
1201 defer gz.nosuspend_node = 0;
1202 return expr(gz, scope, ri, body_node);
1203}
1204
1205fn suspendExpr(
1206 gz: *GenZir,
1207 scope: *Scope,
1208 node: Ast.Node.Index,
1209) InnerError!Zir.Inst.Ref {
1210 const astgen = gz.astgen;
1211 const gpa = astgen.gpa;
1212 const tree = astgen.tree;
1213 const node_datas = tree.nodes.items(.data);
1214 const body_node = node_datas[node].lhs;
1215
1216 if (gz.nosuspend_node != 0) {
1217 return astgen.failNodeNotes(node, "suspend inside nosuspend block", .{}, &[_]u32{
1218 try astgen.errNoteNode(gz.nosuspend_node, "nosuspend block here", .{}),
1219 });
1220 }
1221 if (gz.suspend_node != 0) {
1222 return astgen.failNodeNotes(node, "cannot suspend inside suspend block", .{}, &[_]u32{
1223 try astgen.errNoteNode(gz.suspend_node, "other suspend block here", .{}),
1224 });
1225 }
1226 assert(body_node != 0);
1227
1228 const suspend_inst = try gz.makeBlockInst(.suspend_block, node);
1229 try gz.instructions.append(gpa, suspend_inst);
1230
1231 var suspend_scope = gz.makeSubBlock(scope);
1232 suspend_scope.suspend_node = node;
1233 defer suspend_scope.unstack();
1234
1235 const body_result = try expr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node);
1236 if (!gz.refIsNoReturn(body_result)) {
1237 _ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value);
1238 }
1239 try suspend_scope.setBlockBody(suspend_inst);
1240
1241 return suspend_inst.toRef();
1242}
1243
1244fn awaitExpr(
1245 gz: *GenZir,
1246 scope: *Scope,
1247 ri: ResultInfo,
1248 node: Ast.Node.Index,
1249) InnerError!Zir.Inst.Ref {
1250 const astgen = gz.astgen;
1251 const tree = astgen.tree;
1252 const node_datas = tree.nodes.items(.data);
1253 const rhs_node = node_datas[node].lhs;
1254
1255 if (gz.suspend_node != 0) {
1256 return astgen.failNodeNotes(node, "cannot await inside suspend block", .{}, &[_]u32{
1257 try astgen.errNoteNode(gz.suspend_node, "suspend block here", .{}),
1258 });
1259 }
1260 const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
1261 const result = if (gz.nosuspend_node != 0)
1262 try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{
1263 .node = gz.nodeIndexToRelative(node),
1264 .operand = operand,
1265 })
1266 else
1267 try gz.addUnNode(.@"await", operand, node);
1268
1269 return rvalue(gz, ri, result, node);
1270}
1271
1272fn resumeExpr(
1273 gz: *GenZir,
1274 scope: *Scope,
1275 ri: ResultInfo,
1276 node: Ast.Node.Index,
1277) InnerError!Zir.Inst.Ref {
1278 const astgen = gz.astgen;
1279 const tree = astgen.tree;
1280 const node_datas = tree.nodes.items(.data);
1281 const rhs_node = node_datas[node].lhs;
1282 const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
1283 const result = try gz.addUnNode(.@"resume", operand, node);
1284 return rvalue(gz, ri, result, node);
1285}
1286
1287fn fnProtoExpr(
1288 gz: *GenZir,
1289 scope: *Scope,
1290 ri: ResultInfo,
1291 node: Ast.Node.Index,
1292 fn_proto: Ast.full.FnProto,
1293) InnerError!Zir.Inst.Ref {
1294 const astgen = gz.astgen;
1295 const tree = astgen.tree;
1296 const token_tags = tree.tokens.items(.tag);
1297
1298 if (fn_proto.name_token) |some| {
1299 return astgen.failTok(some, "function type cannot have a name", .{});
1300 }
1301
1302 const is_extern = blk: {
1303 const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
1304 break :blk token_tags[maybe_extern_token] == .keyword_extern;
1305 };
1306 assert(!is_extern);
1307
1308 var block_scope = gz.makeSubBlock(scope);
1309 defer block_scope.unstack();
1310
1311 const block_inst = try gz.makeBlockInst(.block_inline, node);
1312
1313 var noalias_bits: u32 = 0;
1314 const is_var_args = is_var_args: {
1315 var param_type_i: usize = 0;
1316 var it = fn_proto.iterate(tree);
1317 while (it.next()) |param| : (param_type_i += 1) {
1318 const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
1319 .keyword_noalias => is_comptime: {
1320 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
1321 return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
1322 break :is_comptime false;
1323 },
1324 .keyword_comptime => true,
1325 else => false,
1326 } else false;
1327
1328 const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
1329 switch (token_tags[token]) {
1330 .keyword_anytype => break :blk true,
1331 .ellipsis3 => break :is_var_args true,
1332 else => unreachable,
1333 }
1334 } else false;
1335
1336 const param_name = if (param.name_token) |name_token| blk: {
1337 if (mem.eql(u8, "_", tree.tokenSlice(name_token)))
1338 break :blk .empty;
1339
1340 break :blk try astgen.identAsString(name_token);
1341 } else .empty;
1342
1343 if (is_anytype) {
1344 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
1345
1346 const tag: Zir.Inst.Tag = if (is_comptime)
1347 .param_anytype_comptime
1348 else
1349 .param_anytype;
1350 _ = try block_scope.addStrTok(tag, param_name, name_token);
1351 } else {
1352 const param_type_node = param.type_expr;
1353 assert(param_type_node != 0);
1354 var param_gz = block_scope.makeSubBlock(scope);
1355 defer param_gz.unstack();
1356 const param_type = try expr(&param_gz, scope, coerced_type_ri, param_type_node);
1357 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
1358 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
1359 const main_tokens = tree.nodes.items(.main_token);
1360 const name_token = param.name_token orelse main_tokens[param_type_node];
1361 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
1362 const param_inst = try block_scope.addParam(&param_gz, tag, name_token, param_name, param.first_doc_comment);
1363 assert(param_inst_expected == param_inst);
1364 }
1365 }
1366 break :is_var_args false;
1367 };
1368
1369 const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
1370 break :inst try expr(&block_scope, scope, coerced_align_ri, fn_proto.ast.align_expr);
1371 };
1372
1373 if (fn_proto.ast.addrspace_expr != 0) {
1374 return astgen.failNode(fn_proto.ast.addrspace_expr, "addrspace not allowed on function prototypes", .{});
1375 }
1376
1377 if (fn_proto.ast.section_expr != 0) {
1378 return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{});
1379 }
1380
1381 const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0)
1382 try expr(
1383 &block_scope,
1384 scope,
1385 .{ .rl = .{ .coerced_ty = .calling_convention_type } },
1386 fn_proto.ast.callconv_expr,
1387 )
1388 else
1389 Zir.Inst.Ref.none;
1390
1391 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
1392 const is_inferred_error = token_tags[maybe_bang] == .bang;
1393 if (is_inferred_error) {
1394 return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
1395 }
1396 const ret_ty = try expr(&block_scope, scope, coerced_type_ri, fn_proto.ast.return_type);
1397
1398 const result = try block_scope.addFunc(.{
1399 .src_node = fn_proto.ast.proto_node,
1400
1401 .cc_ref = cc,
1402 .cc_gz = null,
1403 .align_ref = align_ref,
1404 .align_gz = null,
1405 .ret_ref = ret_ty,
1406 .ret_gz = null,
1407 .section_ref = .none,
1408 .section_gz = null,
1409 .addrspace_ref = .none,
1410 .addrspace_gz = null,
1411
1412 .param_block = block_inst,
1413 .body_gz = null,
1414 .lib_name = .empty,
1415 .is_var_args = is_var_args,
1416 .is_inferred_error = false,
1417 .is_test = false,
1418 .is_extern = false,
1419 .is_noinline = false,
1420 .noalias_bits = noalias_bits,
1421 });
1422
1423 _ = try block_scope.addBreak(.break_inline, block_inst, result);
1424 try block_scope.setBlockBody(block_inst);
1425 try gz.instructions.append(astgen.gpa, block_inst);
1426
1427 return rvalue(gz, ri, block_inst.toRef(), fn_proto.ast.proto_node);
1428}
1429
1430fn arrayInitExpr(
1431 gz: *GenZir,
1432 scope: *Scope,
1433 ri: ResultInfo,
1434 node: Ast.Node.Index,
1435 array_init: Ast.full.ArrayInit,
1436) InnerError!Zir.Inst.Ref {
1437 const astgen = gz.astgen;
1438 const tree = astgen.tree;
1439 const node_tags = tree.nodes.items(.tag);
1440 const main_tokens = tree.nodes.items(.main_token);
1441
1442 assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init.
1443
1444 const array_ty: Zir.Inst.Ref, const elem_ty: Zir.Inst.Ref = inst: {
1445 if (array_init.ast.type_expr == 0) break :inst .{ .none, .none };
1446
1447 infer: {
1448 const array_type: Ast.full.ArrayType = tree.fullArrayType(array_init.ast.type_expr) orelse break :infer;
1449 // This intentionally does not support `@"_"` syntax.
1450 if (node_tags[array_type.ast.elem_count] == .identifier and
1451 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_"))
1452 {
1453 const len_inst = try gz.addInt(array_init.ast.elements.len);
1454 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
1455 if (array_type.ast.sentinel == 0) {
1456 const array_type_inst = try gz.addPlNode(.array_type, array_init.ast.type_expr, Zir.Inst.Bin{
1457 .lhs = len_inst,
1458 .rhs = elem_type,
1459 });
1460 break :inst .{ array_type_inst, elem_type };
1461 } else {
1462 const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel);
1463 const array_type_inst = try gz.addPlNode(
1464 .array_type_sentinel,
1465 array_init.ast.type_expr,
1466 Zir.Inst.ArrayTypeSentinel{
1467 .len = len_inst,
1468 .elem_type = elem_type,
1469 .sentinel = sentinel,
1470 },
1471 );
1472 break :inst .{ array_type_inst, elem_type };
1473 }
1474 }
1475 }
1476 const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr);
1477 _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{
1478 .ty = array_type_inst,
1479 .init_count = @intCast(array_init.ast.elements.len),
1480 });
1481 break :inst .{ array_type_inst, .none };
1482 };
1483
1484 if (array_ty != .none) {
1485 // Typed inits do not use RLS for language simplicity.
1486 switch (ri.rl) {
1487 .discard => {
1488 if (elem_ty != .none) {
1489 const elem_ri: ResultInfo = .{ .rl = .{ .ty = elem_ty } };
1490 for (array_init.ast.elements) |elem_init| {
1491 _ = try expr(gz, scope, elem_ri, elem_init);
1492 }
1493 } else {
1494 for (array_init.ast.elements, 0..) |elem_init, i| {
1495 const this_elem_ty = try gz.add(.{
1496 .tag = .array_init_elem_type,
1497 .data = .{ .bin = .{
1498 .lhs = array_ty,
1499 .rhs = @enumFromInt(i),
1500 } },
1501 });
1502 _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init);
1503 }
1504 }
1505 return .void_value;
1506 },
1507 .ref => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true),
1508 else => {
1509 const array_inst = try arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, false);
1510 return rvalue(gz, ri, array_inst, node);
1511 },
1512 }
1513 }
1514
1515 switch (ri.rl) {
1516 .none => return arrayInitExprAnon(gz, scope, node, array_init.ast.elements),
1517 .discard => {
1518 for (array_init.ast.elements) |elem_init| {
1519 _ = try expr(gz, scope, .{ .rl = .discard }, elem_init);
1520 }
1521 return Zir.Inst.Ref.void_value;
1522 },
1523 .ref => {
1524 const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);
1525 return gz.addUnTok(.ref, result, tree.firstToken(node));
1526 },
1527 .ref_coerced_ty => |ptr_ty_inst| {
1528 const dest_arr_ty_inst = try gz.addPlNode(.validate_array_init_ref_ty, node, Zir.Inst.ArrayInitRefTy{
1529 .ptr_ty = ptr_ty_inst,
1530 .elem_count = @intCast(array_init.ast.elements.len),
1531 });
1532 return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, dest_arr_ty_inst, .none, true);
1533 },
1534 .ty, .coerced_ty => |result_ty_inst| {
1535 _ = try gz.addPlNode(.validate_array_init_result_ty, node, Zir.Inst.ArrayInit{
1536 .ty = result_ty_inst,
1537 .init_count = @intCast(array_init.ast.elements.len),
1538 });
1539 return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, result_ty_inst, .none, false);
1540 },
1541 .ptr => |ptr| {
1542 try arrayInitExprPtr(gz, scope, node, array_init.ast.elements, ptr.inst);
1543 return .void_value;
1544 },
1545 .inferred_ptr => {
1546 // We can't get elem pointers of an untyped inferred alloc, so must perform a
1547 // standard anonymous initialization followed by an rvalue store.
1548 // See corresponding logic in structInitExpr.
1549 const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);
1550 return rvalue(gz, ri, result, node);
1551 },
1552 .destructure => |destructure| {
1553 // Untyped init - destructure directly into result pointers
1554 if (array_init.ast.elements.len != destructure.components.len) {
1555 return astgen.failNodeNotes(node, "expected {} elements for destructure, found {}", .{
1556 destructure.components.len,
1557 array_init.ast.elements.len,
1558 }, &.{
1559 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
1560 });
1561 }
1562 for (array_init.ast.elements, destructure.components) |elem_init, ds_comp| {
1563 const elem_ri: ResultInfo = .{ .rl = switch (ds_comp) {
1564 .typed_ptr => |ptr_rl| .{ .ptr = ptr_rl },
1565 .inferred_ptr => |ptr_inst| .{ .inferred_ptr = ptr_inst },
1566 .discard => .discard,
1567 } };
1568 _ = try expr(gz, scope, elem_ri, elem_init);
1569 }
1570 return .void_value;
1571 },
1572 }
1573}
1574
1575/// An array initialization expression using an `array_init_anon` instruction.
1576fn arrayInitExprAnon(
1577 gz: *GenZir,
1578 scope: *Scope,
1579 node: Ast.Node.Index,
1580 elements: []const Ast.Node.Index,
1581) InnerError!Zir.Inst.Ref {
1582 const astgen = gz.astgen;
1583
1584 const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
1585 .operands_len = @intCast(elements.len),
1586 });
1587 var extra_index = try reserveExtra(astgen, elements.len);
1588
1589 for (elements) |elem_init| {
1590 const elem_ref = try expr(gz, scope, .{ .rl = .none }, elem_init);
1591 astgen.extra.items[extra_index] = @intFromEnum(elem_ref);
1592 extra_index += 1;
1593 }
1594 return try gz.addPlNodePayloadIndex(.array_init_anon, node, payload_index);
1595}
1596
1597/// An array initialization expression using an `array_init` or `array_init_ref` instruction.
1598fn arrayInitExprTyped(
1599 gz: *GenZir,
1600 scope: *Scope,
1601 node: Ast.Node.Index,
1602 elements: []const Ast.Node.Index,
1603 ty_inst: Zir.Inst.Ref,
1604 maybe_elem_ty_inst: Zir.Inst.Ref,
1605 is_ref: bool,
1606) InnerError!Zir.Inst.Ref {
1607 const astgen = gz.astgen;
1608
1609 const len = elements.len + 1; // +1 for type
1610 const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
1611 .operands_len = @intCast(len),
1612 });
1613 var extra_index = try reserveExtra(astgen, len);
1614 astgen.extra.items[extra_index] = @intFromEnum(ty_inst);
1615 extra_index += 1;
1616
1617 if (maybe_elem_ty_inst != .none) {
1618 const elem_ri: ResultInfo = .{ .rl = .{ .coerced_ty = maybe_elem_ty_inst } };
1619 for (elements) |elem_init| {
1620 const elem_inst = try expr(gz, scope, elem_ri, elem_init);
1621 astgen.extra.items[extra_index] = @intFromEnum(elem_inst);
1622 extra_index += 1;
1623 }
1624 } else {
1625 for (elements, 0..) |elem_init, i| {
1626 const ri: ResultInfo = .{ .rl = .{ .coerced_ty = try gz.add(.{
1627 .tag = .array_init_elem_type,
1628 .data = .{ .bin = .{
1629 .lhs = ty_inst,
1630 .rhs = @enumFromInt(i),
1631 } },
1632 }) } };
1633
1634 const elem_inst = try expr(gz, scope, ri, elem_init);
1635 astgen.extra.items[extra_index] = @intFromEnum(elem_inst);
1636 extra_index += 1;
1637 }
1638 }
1639
1640 const tag: Zir.Inst.Tag = if (is_ref) .array_init_ref else .array_init;
1641 return try gz.addPlNodePayloadIndex(tag, node, payload_index);
1642}
1643
1644/// An array initialization expression using element pointers.
1645fn arrayInitExprPtr(
1646 gz: *GenZir,
1647 scope: *Scope,
1648 node: Ast.Node.Index,
1649 elements: []const Ast.Node.Index,
1650 ptr_inst: Zir.Inst.Ref,
1651) InnerError!void {
1652 const astgen = gz.astgen;
1653
1654 const array_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node);
1655
1656 const payload_index = try addExtra(astgen, Zir.Inst.Block{
1657 .body_len = @intCast(elements.len),
1658 });
1659 var extra_index = try reserveExtra(astgen, elements.len);
1660
1661 for (elements, 0..) |elem_init, i| {
1662 const elem_ptr_inst = try gz.addPlNode(.array_init_elem_ptr, elem_init, Zir.Inst.ElemPtrImm{
1663 .ptr = array_ptr_inst,
1664 .index = @intCast(i),
1665 });
1666 astgen.extra.items[extra_index] = @intFromEnum(elem_ptr_inst.toIndex().?);
1667 extra_index += 1;
1668 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = elem_ptr_inst } } }, elem_init);
1669 }
1670
1671 _ = try gz.addPlNodePayloadIndex(.validate_ptr_array_init, node, payload_index);
1672}
1673
1674fn structInitExpr(
1675 gz: *GenZir,
1676 scope: *Scope,
1677 ri: ResultInfo,
1678 node: Ast.Node.Index,
1679 struct_init: Ast.full.StructInit,
1680) InnerError!Zir.Inst.Ref {
1681 const astgen = gz.astgen;
1682 const tree = astgen.tree;
1683
1684 if (struct_init.ast.type_expr == 0) {
1685 if (struct_init.ast.fields.len == 0) {
1686 // Anonymous init with no fields.
1687 switch (ri.rl) {
1688 .discard => return .void_value,
1689 .ref_coerced_ty => |ptr_ty_inst| return gz.addUnNode(.struct_init_empty_ref_result, ptr_ty_inst, node),
1690 .ty, .coerced_ty => |ty_inst| return gz.addUnNode(.struct_init_empty_result, ty_inst, node),
1691 .ptr => {
1692 // TODO: should we modify this to use RLS for the field stores here?
1693 const ty_inst = (try ri.rl.resultType(gz, node)).?;
1694 const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node);
1695 return rvalue(gz, ri, val, node);
1696 },
1697 .none, .ref, .inferred_ptr => {
1698 return rvalue(gz, ri, .empty_struct, node);
1699 },
1700 .destructure => |destructure| {
1701 return astgen.failNodeNotes(node, "empty initializer cannot be destructured", .{}, &.{
1702 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
1703 });
1704 },
1705 }
1706 }
1707 } else array: {
1708 const node_tags = tree.nodes.items(.tag);
1709 const main_tokens = tree.nodes.items(.main_token);
1710 const array_type: Ast.full.ArrayType = tree.fullArrayType(struct_init.ast.type_expr) orelse {
1711 if (struct_init.ast.fields.len == 0) {
1712 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
1713 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
1714 return rvalue(gz, ri, result, node);
1715 }
1716 break :array;
1717 };
1718 const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and
1719 // This intentionally does not support `@"_"` syntax.
1720 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_");
1721 if (struct_init.ast.fields.len == 0) {
1722 if (is_inferred_array_len) {
1723 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
1724 const array_type_inst = if (array_type.ast.sentinel == 0) blk: {
1725 break :blk try gz.addPlNode(.array_type, struct_init.ast.type_expr, Zir.Inst.Bin{
1726 .lhs = .zero_usize,
1727 .rhs = elem_type,
1728 });
1729 } else blk: {
1730 const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel);
1731 break :blk try gz.addPlNode(
1732 .array_type_sentinel,
1733 struct_init.ast.type_expr,
1734 Zir.Inst.ArrayTypeSentinel{
1735 .len = .zero_usize,
1736 .elem_type = elem_type,
1737 .sentinel = sentinel,
1738 },
1739 );
1740 };
1741 const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node);
1742 return rvalue(gz, ri, result, node);
1743 }
1744 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
1745 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
1746 return rvalue(gz, ri, result, node);
1747 } else {
1748 return astgen.failNode(
1749 struct_init.ast.type_expr,
1750 "initializing array with struct syntax",
1751 .{},
1752 );
1753 }
1754 }
1755
1756 {
1757 var sfba = std.heap.stackFallback(256, astgen.arena);
1758 const sfba_allocator = sfba.get();
1759
1760 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
1761 try duplicate_names.ensureTotalCapacity(@intCast(struct_init.ast.fields.len));
1762
1763 // When there aren't errors, use this to avoid a second iteration.
1764 var any_duplicate = false;
1765
1766 for (struct_init.ast.fields) |field| {
1767 const name_token = tree.firstToken(field) - 2;
1768 const name_index = try astgen.identAsString(name_token);
1769
1770 const gop = try duplicate_names.getOrPut(name_index);
1771
1772 if (gop.found_existing) {
1773 try gop.value_ptr.append(sfba_allocator, name_token);
1774 any_duplicate = true;
1775 } else {
1776 gop.value_ptr.* = .{};
1777 try gop.value_ptr.append(sfba_allocator, name_token);
1778 }
1779 }
1780
1781 if (any_duplicate) {
1782 var it = duplicate_names.iterator();
1783
1784 while (it.next()) |entry| {
1785 const record = entry.value_ptr.*;
1786 if (record.items.len > 1) {
1787 var error_notes = std.ArrayList(u32).init(astgen.arena);
1788
1789 for (record.items[1..]) |duplicate| {
1790 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate name here", .{}));
1791 }
1792
1793 try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));
1794
1795 try astgen.appendErrorTokNotes(
1796 record.items[0],
1797 "duplicate struct field name",
1798 .{},
1799 error_notes.items,
1800 );
1801 }
1802 }
1803
1804 return error.AnalysisFail;
1805 }
1806 }
1807
1808 if (struct_init.ast.type_expr != 0) {
1809 // Typed inits do not use RLS for language simplicity.
1810 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
1811 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
1812 switch (ri.rl) {
1813 .ref => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true),
1814 else => {
1815 const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false);
1816 return rvalue(gz, ri, struct_inst, node);
1817 },
1818 }
1819 }
1820
1821 switch (ri.rl) {
1822 .none => return structInitExprAnon(gz, scope, node, struct_init),
1823 .discard => {
1824 // Even if discarding we must perform side-effects.
1825 for (struct_init.ast.fields) |field_init| {
1826 _ = try expr(gz, scope, .{ .rl = .discard }, field_init);
1827 }
1828 return .void_value;
1829 },
1830 .ref => {
1831 const result = try structInitExprAnon(gz, scope, node, struct_init);
1832 return gz.addUnTok(.ref, result, tree.firstToken(node));
1833 },
1834 .ref_coerced_ty => |ptr_ty_inst| {
1835 const result_ty_inst = try gz.addUnNode(.elem_type, ptr_ty_inst, node);
1836 _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node);
1837 return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, true);
1838 },
1839 .ty, .coerced_ty => |result_ty_inst| {
1840 _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node);
1841 return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, false);
1842 },
1843 .ptr => |ptr| {
1844 try structInitExprPtr(gz, scope, node, struct_init, ptr.inst);
1845 return .void_value;
1846 },
1847 .inferred_ptr => {
1848 // We can't get field pointers of an untyped inferred alloc, so must perform a
1849 // standard anonymous initialization followed by an rvalue store.
1850 // See corresponding logic in arrayInitExpr.
1851 const struct_inst = try structInitExprAnon(gz, scope, node, struct_init);
1852 return rvalue(gz, ri, struct_inst, node);
1853 },
1854 .destructure => |destructure| {
1855 // This is an untyped init, so is an actual struct, which does
1856 // not support destructuring.
1857 return astgen.failNodeNotes(node, "struct value cannot be destructured", .{}, &.{
1858 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
1859 });
1860 },
1861 }
1862}
1863
1864/// A struct initialization expression using a `struct_init_anon` instruction.
1865fn structInitExprAnon(
1866 gz: *GenZir,
1867 scope: *Scope,
1868 node: Ast.Node.Index,
1869 struct_init: Ast.full.StructInit,
1870) InnerError!Zir.Inst.Ref {
1871 const astgen = gz.astgen;
1872 const tree = astgen.tree;
1873
1874 const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{
1875 .fields_len = @intCast(struct_init.ast.fields.len),
1876 });
1877 const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).Struct.fields.len;
1878 var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
1879
1880 for (struct_init.ast.fields) |field_init| {
1881 const name_token = tree.firstToken(field_init) - 2;
1882 const str_index = try astgen.identAsString(name_token);
1883 setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{
1884 .field_name = str_index,
1885 .init = try expr(gz, scope, .{ .rl = .none }, field_init),
1886 });
1887 extra_index += field_size;
1888 }
1889
1890 return gz.addPlNodePayloadIndex(.struct_init_anon, node, payload_index);
1891}
1892
1893/// A struct initialization expression using a `struct_init` or `struct_init_ref` instruction.
1894fn structInitExprTyped(
1895 gz: *GenZir,
1896 scope: *Scope,
1897 node: Ast.Node.Index,
1898 struct_init: Ast.full.StructInit,
1899 ty_inst: Zir.Inst.Ref,
1900 is_ref: bool,
1901) InnerError!Zir.Inst.Ref {
1902 const astgen = gz.astgen;
1903 const tree = astgen.tree;
1904
1905 const payload_index = try addExtra(astgen, Zir.Inst.StructInit{
1906 .fields_len = @intCast(struct_init.ast.fields.len),
1907 });
1908 const field_size = @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len;
1909 var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
1910
1911 for (struct_init.ast.fields) |field_init| {
1912 const name_token = tree.firstToken(field_init) - 2;
1913 const str_index = try astgen.identAsString(name_token);
1914 const field_ty_inst = try gz.addPlNode(.struct_init_field_type, field_init, Zir.Inst.FieldType{
1915 .container_type = ty_inst,
1916 .name_start = str_index,
1917 });
1918 setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{
1919 .field_type = field_ty_inst.toIndex().?,
1920 .init = try expr(gz, scope, .{ .rl = .{ .coerced_ty = field_ty_inst } }, field_init),
1921 });
1922 extra_index += field_size;
1923 }
1924
1925 const tag: Zir.Inst.Tag = if (is_ref) .struct_init_ref else .struct_init;
1926 return gz.addPlNodePayloadIndex(tag, node, payload_index);
1927}
1928
1929/// A struct initialization expression using field pointers.
1930fn structInitExprPtr(
1931 gz: *GenZir,
1932 scope: *Scope,
1933 node: Ast.Node.Index,
1934 struct_init: Ast.full.StructInit,
1935 ptr_inst: Zir.Inst.Ref,
1936) InnerError!void {
1937 const astgen = gz.astgen;
1938 const tree = astgen.tree;
1939
1940 const struct_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node);
1941
1942 const payload_index = try addExtra(astgen, Zir.Inst.Block{
1943 .body_len = @intCast(struct_init.ast.fields.len),
1944 });
1945 var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len);
1946
1947 for (struct_init.ast.fields) |field_init| {
1948 const name_token = tree.firstToken(field_init) - 2;
1949 const str_index = try astgen.identAsString(name_token);
1950 const field_ptr = try gz.addPlNode(.struct_init_field_ptr, field_init, Zir.Inst.Field{
1951 .lhs = struct_ptr_inst,
1952 .field_name_start = str_index,
1953 });
1954 astgen.extra.items[extra_index] = @intFromEnum(field_ptr.toIndex().?);
1955 extra_index += 1;
1956 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init);
1957 }
1958
1959 _ = try gz.addPlNodePayloadIndex(.validate_ptr_struct_init, node, payload_index);
1960}
1961
1962/// This explicitly calls expr in a comptime scope by wrapping it in a `block_comptime` if
1963/// necessary. It should be used whenever we need to force compile-time evaluation of something,
1964/// such as a type.
1965/// The function corresponding to `comptime` expression syntax is `comptimeExprAst`.
1966fn comptimeExpr(
1967 gz: *GenZir,
1968 scope: *Scope,
1969 ri: ResultInfo,
1970 node: Ast.Node.Index,
1971) InnerError!Zir.Inst.Ref {
1972 if (gz.is_comptime) {
1973 // No need to change anything!
1974 return expr(gz, scope, ri, node);
1975 }
1976
1977 // There's an optimization here: if the body will be evaluated at comptime regardless, there's
1978 // no need to wrap it in a block. This is hard to determine in general, but we can identify a
1979 // common subset of trivially comptime expressions to take down the size of the ZIR a bit.
1980 const tree = gz.astgen.tree;
1981 const main_tokens = tree.nodes.items(.main_token);
1982 const node_tags = tree.nodes.items(.tag);
1983 switch (node_tags[node]) {
1984 // Any identifier in `primitive_instrs` is trivially comptime. In particular, this includes
1985 // some common types, so we can elide `block_comptime` for a few common type annotations.
1986 .identifier => {
1987 const ident_token = main_tokens[node];
1988 const ident_name_raw = tree.tokenSlice(ident_token);
1989 if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
1990 // No need to worry about result location here, we're not creating a comptime block!
1991 return rvalue(gz, ri, zir_const_ref, node);
1992 }
1993 },
1994
1995 // We can also avoid the block for a few trivial AST tags which are always comptime-known.
1996 .number_literal, .string_literal, .multiline_string_literal, .enum_literal, .error_value => {
1997 // No need to worry about result location here, we're not creating a comptime block!
1998 return expr(gz, scope, ri, node);
1999 },
2000
2001 // Lastly, for labelled blocks, avoid emitting a labelled block directly inside this
2002 // comptime block, because that would be silly! Note that we don't bother doing this for
2003 // unlabelled blocks, since they don't generate blocks at comptime anyway (see `blockExpr`).
2004 .block_two, .block_two_semicolon, .block, .block_semicolon => {
2005 const token_tags = tree.tokens.items(.tag);
2006 const lbrace = main_tokens[node];
2007 // Careful! We can't pass in the real result location here, since it may
2008 // refer to runtime memory. A runtime-to-comptime boundary has to remove
2009 // result location information, compute the result, and copy it to the true
2010 // result location at runtime. We do this below as well.
2011 const ty_only_ri: ResultInfo = .{
2012 .ctx = ri.ctx,
2013 .rl = if (try ri.rl.resultType(gz, node)) |res_ty|
2014 .{ .coerced_ty = res_ty }
2015 else
2016 .none,
2017 };
2018 if (token_tags[lbrace - 1] == .colon and
2019 token_tags[lbrace - 2] == .identifier)
2020 {
2021 const node_datas = tree.nodes.items(.data);
2022 switch (node_tags[node]) {
2023 .block_two, .block_two_semicolon => {
2024 const stmts: [2]Ast.Node.Index = .{ node_datas[node].lhs, node_datas[node].rhs };
2025 const stmt_slice = if (stmts[0] == 0)
2026 stmts[0..0]
2027 else if (stmts[1] == 0)
2028 stmts[0..1]
2029 else
2030 stmts[0..2];
2031
2032 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmt_slice, true);
2033 return rvalue(gz, ri, block_ref, node);
2034 },
2035 .block, .block_semicolon => {
2036 const stmts = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
2037 // Replace result location and copy back later - see above.
2038 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true);
2039 return rvalue(gz, ri, block_ref, node);
2040 },
2041 else => unreachable,
2042 }
2043 }
2044 },
2045
2046 // In other cases, we don't optimize anything - we need a wrapper comptime block.
2047 else => {},
2048 }
2049
2050 var block_scope = gz.makeSubBlock(scope);
2051 block_scope.is_comptime = true;
2052 defer block_scope.unstack();
2053
2054 const block_inst = try gz.makeBlockInst(.block_comptime, node);
2055 // Replace result location and copy back later - see above.
2056 const ty_only_ri: ResultInfo = .{
2057 .ctx = ri.ctx,
2058 .rl = if (try ri.rl.resultType(gz, node)) |res_ty|
2059 .{ .coerced_ty = res_ty }
2060 else
2061 .none,
2062 };
2063 const block_result = try expr(&block_scope, scope, ty_only_ri, node);
2064 if (!gz.refIsNoReturn(block_result)) {
2065 _ = try block_scope.addBreak(.@"break", block_inst, block_result);
2066 }
2067 try block_scope.setBlockBody(block_inst);
2068 try gz.instructions.append(gz.astgen.gpa, block_inst);
2069
2070 return rvalue(gz, ri, block_inst.toRef(), node);
2071}
2072
2073/// This one is for an actual `comptime` syntax, and will emit a compile error if
2074/// the scope is already known to be comptime-evaluated.
2075/// See `comptimeExpr` for the helper function for calling expr in a comptime scope.
2076fn comptimeExprAst(
2077 gz: *GenZir,
2078 scope: *Scope,
2079 ri: ResultInfo,
2080 node: Ast.Node.Index,
2081) InnerError!Zir.Inst.Ref {
2082 const astgen = gz.astgen;
2083 if (gz.is_comptime) {
2084 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
2085 }
2086 const tree = astgen.tree;
2087 const node_datas = tree.nodes.items(.data);
2088 const body_node = node_datas[node].lhs;
2089 return comptimeExpr(gz, scope, ri, body_node);
2090}
2091
2092/// Restore the error return trace index. Performs the restore only if the result is a non-error or
2093/// if the result location is a non-error-handling expression.
2094fn restoreErrRetIndex(
2095 gz: *GenZir,
2096 bt: GenZir.BranchTarget,
2097 ri: ResultInfo,
2098 node: Ast.Node.Index,
2099 result: Zir.Inst.Ref,
2100) !void {
2101 const op = switch (nodeMayEvalToError(gz.astgen.tree, node)) {
2102 .always => return, // never restore/pop
2103 .never => .none, // always restore/pop
2104 .maybe => switch (ri.ctx) {
2105 .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) {
2106 .ptr => |ptr_res| try gz.addUnNode(.load, ptr_res.inst, node),
2107 .inferred_ptr => blk: {
2108 // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr
2109 // before its type has been resolved. There is no valid operand to use here, so error
2110 // traces will be popped prematurely.
2111 // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it.
2112 break :blk .none;
2113 },
2114 .destructure => return, // value must be a tuple or array, so never restore/pop
2115 else => result,
2116 },
2117 else => .none, // always restore/pop
2118 },
2119 };
2120 _ = try gz.addRestoreErrRetIndex(bt, .{ .if_non_error = op }, node);
2121}
2122
2123fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
2124 const astgen = parent_gz.astgen;
2125 const tree = astgen.tree;
2126 const node_datas = tree.nodes.items(.data);
2127 const break_label = node_datas[node].lhs;
2128 const rhs = node_datas[node].rhs;
2129
2130 // Look for the label in the scope.
2131 var scope = parent_scope;
2132 while (true) {
2133 switch (scope.tag) {
2134 .gen_zir => {
2135 const block_gz = scope.cast(GenZir).?;
2136
2137 if (block_gz.cur_defer_node != 0) {
2138 // We are breaking out of a `defer` block.
2139 return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
2140 try astgen.errNoteNode(
2141 block_gz.cur_defer_node,
2142 "defer expression here",
2143 .{},
2144 ),
2145 });
2146 }
2147
2148 const block_inst = blk: {
2149 if (break_label != 0) {
2150 if (block_gz.label) |*label| {
2151 if (try astgen.tokenIdentEql(label.token, break_label)) {
2152 label.used = true;
2153 break :blk label.block_inst;
2154 }
2155 }
2156 } else if (block_gz.break_block.unwrap()) |i| {
2157 break :blk i;
2158 }
2159 // If not the target, start over with the parent
2160 scope = block_gz.parent;
2161 continue;
2162 };
2163 // If we made it here, this block is the target of the break expr
2164
2165 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline)
2166 .break_inline
2167 else
2168 .@"break";
2169
2170 if (rhs == 0) {
2171 _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node);
2172
2173 try genDefers(parent_gz, scope, parent_scope, .normal_only);
2174
2175 // As our last action before the break, "pop" the error trace if needed
2176 if (!block_gz.is_comptime)
2177 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, node);
2178
2179 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
2180 return Zir.Inst.Ref.unreachable_value;
2181 }
2182
2183 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node);
2184
2185 try genDefers(parent_gz, scope, parent_scope, .normal_only);
2186
2187 // As our last action before the break, "pop" the error trace if needed
2188 if (!block_gz.is_comptime)
2189 try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand);
2190
2191 switch (block_gz.break_result_info.rl) {
2192 .ptr => {
2193 // In this case we don't have any mechanism to intercept it;
2194 // we assume the result location is written, and we break with void.
2195 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
2196 },
2197 .discard => {
2198 _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
2199 },
2200 else => {
2201 _ = try parent_gz.addBreakWithSrcNode(break_tag, block_inst, operand, rhs);
2202 },
2203 }
2204 return Zir.Inst.Ref.unreachable_value;
2205 },
2206 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2207 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2208 .namespace, .enum_namespace => break,
2209 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2210 .top => unreachable,
2211 }
2212 }
2213 if (break_label != 0) {
2214 const label_name = try astgen.identifierTokenString(break_label);
2215 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
2216 } else {
2217 return astgen.failNode(node, "break expression outside loop", .{});
2218 }
2219}
2220
2221fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
2222 const astgen = parent_gz.astgen;
2223 const tree = astgen.tree;
2224 const node_datas = tree.nodes.items(.data);
2225 const break_label = node_datas[node].lhs;
2226
2227 // Look for the label in the scope.
2228 var scope = parent_scope;
2229 while (true) {
2230 switch (scope.tag) {
2231 .gen_zir => {
2232 const gen_zir = scope.cast(GenZir).?;
2233
2234 if (gen_zir.cur_defer_node != 0) {
2235 return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
2236 try astgen.errNoteNode(
2237 gen_zir.cur_defer_node,
2238 "defer expression here",
2239 .{},
2240 ),
2241 });
2242 }
2243 const continue_block = gen_zir.continue_block.unwrap() orelse {
2244 scope = gen_zir.parent;
2245 continue;
2246 };
2247 if (break_label != 0) blk: {
2248 if (gen_zir.label) |*label| {
2249 if (try astgen.tokenIdentEql(label.token, break_label)) {
2250 label.used = true;
2251 break :blk;
2252 }
2253 }
2254 // found continue but either it has a different label, or no label
2255 scope = gen_zir.parent;
2256 continue;
2257 }
2258
2259 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
2260 .break_inline
2261 else
2262 .@"break";
2263 if (break_tag == .break_inline) {
2264 _ = try parent_gz.addUnNode(.check_comptime_control_flow, continue_block.toRef(), node);
2265 }
2266
2267 // As our last action before the continue, "pop" the error trace if needed
2268 if (!gen_zir.is_comptime)
2269 _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node);
2270
2271 _ = try parent_gz.addBreak(break_tag, continue_block, .void_value);
2272 return Zir.Inst.Ref.unreachable_value;
2273 },
2274 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2275 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2276 .defer_normal => {
2277 const defer_scope = scope.cast(Scope.Defer).?;
2278 scope = defer_scope.parent;
2279 try parent_gz.addDefer(defer_scope.index, defer_scope.len);
2280 },
2281 .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2282 .namespace, .enum_namespace => break,
2283 .top => unreachable,
2284 }
2285 }
2286 if (break_label != 0) {
2287 const label_name = try astgen.identifierTokenString(break_label);
2288 return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
2289 } else {
2290 return astgen.failNode(node, "continue expression outside loop", .{});
2291 }
2292}
2293
2294fn blockExpr(
2295 gz: *GenZir,
2296 scope: *Scope,
2297 ri: ResultInfo,
2298 block_node: Ast.Node.Index,
2299 statements: []const Ast.Node.Index,
2300) InnerError!Zir.Inst.Ref {
2301 const astgen = gz.astgen;
2302 const tree = astgen.tree;
2303 const main_tokens = tree.nodes.items(.main_token);
2304 const token_tags = tree.tokens.items(.tag);
2305
2306 const lbrace = main_tokens[block_node];
2307 if (token_tags[lbrace - 1] == .colon and
2308 token_tags[lbrace - 2] == .identifier)
2309 {
2310 return labeledBlockExpr(gz, scope, ri, block_node, statements, false);
2311 }
2312
2313 if (!gz.is_comptime) {
2314 // Since this block is unlabeled, its control flow is effectively linear and we
2315 // can *almost* get away with inlining the block here. However, we actually need
2316 // to preserve the .block for Sema, to properly pop the error return trace.
2317
2318 const block_tag: Zir.Inst.Tag = .block;
2319 const block_inst = try gz.makeBlockInst(block_tag, block_node);
2320 try gz.instructions.append(astgen.gpa, block_inst);
2321
2322 var block_scope = gz.makeSubBlock(scope);
2323 defer block_scope.unstack();
2324
2325 try blockExprStmts(&block_scope, &block_scope.base, statements);
2326
2327 if (!block_scope.endsWithNoReturn()) {
2328 // As our last action before the break, "pop" the error trace if needed
2329 _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node);
2330 _ = try block_scope.addBreak(.@"break", block_inst, .void_value);
2331 }
2332
2333 try block_scope.setBlockBody(block_inst);
2334 } else {
2335 var sub_gz = gz.makeSubBlock(scope);
2336 try blockExprStmts(&sub_gz, &sub_gz.base, statements);
2337 }
2338
2339 return rvalue(gz, ri, .void_value, block_node);
2340}
2341
2342fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void {
2343 // Look for the label in the scope.
2344 var scope = parent_scope;
2345 while (true) {
2346 switch (scope.tag) {
2347 .gen_zir => {
2348 const gen_zir = scope.cast(GenZir).?;
2349 if (gen_zir.label) |prev_label| {
2350 if (try astgen.tokenIdentEql(label, prev_label.token)) {
2351 const label_name = try astgen.identifierTokenString(label);
2352 return astgen.failTokNotes(label, "redefinition of label '{s}'", .{
2353 label_name,
2354 }, &[_]u32{
2355 try astgen.errNoteTok(
2356 prev_label.token,
2357 "previous definition here",
2358 .{},
2359 ),
2360 });
2361 }
2362 }
2363 scope = gen_zir.parent;
2364 },
2365 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2366 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2367 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2368 .namespace, .enum_namespace => break,
2369 .top => unreachable,
2370 }
2371 }
2372}
2373
2374fn labeledBlockExpr(
2375 gz: *GenZir,
2376 parent_scope: *Scope,
2377 ri: ResultInfo,
2378 block_node: Ast.Node.Index,
2379 statements: []const Ast.Node.Index,
2380 force_comptime: bool,
2381) InnerError!Zir.Inst.Ref {
2382 const astgen = gz.astgen;
2383 const tree = astgen.tree;
2384 const main_tokens = tree.nodes.items(.main_token);
2385 const token_tags = tree.tokens.items(.tag);
2386
2387 const lbrace = main_tokens[block_node];
2388 const label_token = lbrace - 2;
2389 assert(token_tags[label_token] == .identifier);
2390
2391 try astgen.checkLabelRedefinition(parent_scope, label_token);
2392
2393 const need_rl = astgen.nodes_need_rl.contains(block_node);
2394 const block_ri: ResultInfo = if (need_rl) ri else .{
2395 .rl = switch (ri.rl) {
2396 .ptr => .{ .ty = (try ri.rl.resultType(gz, block_node)).? },
2397 .inferred_ptr => .none,
2398 else => ri.rl,
2399 },
2400 .ctx = ri.ctx,
2401 };
2402 // We need to call `rvalue` to write through to the pointer only if we had a
2403 // result pointer and aren't forwarding it.
2404 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
2405 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
2406
2407 // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
2408 // so that break statements can reference it.
2409 const block_tag: Zir.Inst.Tag = if (force_comptime) .block_comptime else .block;
2410 const block_inst = try gz.makeBlockInst(block_tag, block_node);
2411 try gz.instructions.append(astgen.gpa, block_inst);
2412 var block_scope = gz.makeSubBlock(parent_scope);
2413 block_scope.label = GenZir.Label{
2414 .token = label_token,
2415 .block_inst = block_inst,
2416 };
2417 block_scope.setBreakResultInfo(block_ri);
2418 if (force_comptime) block_scope.is_comptime = true;
2419 defer block_scope.unstack();
2420
2421 try blockExprStmts(&block_scope, &block_scope.base, statements);
2422 if (!block_scope.endsWithNoReturn()) {
2423 // As our last action before the return, "pop" the error trace if needed
2424 _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node);
2425 _ = try block_scope.addBreak(.@"break", block_inst, .void_value);
2426 }
2427
2428 if (!block_scope.label.?.used) {
2429 try astgen.appendErrorTok(label_token, "unused block label", .{});
2430 }
2431
2432 try block_scope.setBlockBody(block_inst);
2433 if (need_result_rvalue) {
2434 return rvalue(gz, ri, block_inst.toRef(), block_node);
2435 } else {
2436 return block_inst.toRef();
2437 }
2438}
2439
2440fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index) !void {
2441 const astgen = gz.astgen;
2442 const tree = astgen.tree;
2443 const node_tags = tree.nodes.items(.tag);
2444 const node_data = tree.nodes.items(.data);
2445
2446 if (statements.len == 0) return;
2447
2448 var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa);
2449 defer block_arena.deinit();
2450 const block_arena_allocator = block_arena.allocator();
2451
2452 var noreturn_src_node: Ast.Node.Index = 0;
2453 var scope = parent_scope;
2454 for (statements) |statement| {
2455 if (noreturn_src_node != 0) {
2456 try astgen.appendErrorNodeNotes(
2457 statement,
2458 "unreachable code",
2459 .{},
2460 &[_]u32{
2461 try astgen.errNoteNode(
2462 noreturn_src_node,
2463 "control flow is diverted here",
2464 .{},
2465 ),
2466 },
2467 );
2468 }
2469 var inner_node = statement;
2470 while (true) {
2471 switch (node_tags[inner_node]) {
2472 // zig fmt: off
2473 .global_var_decl,
2474 .local_var_decl,
2475 .simple_var_decl,
2476 .aligned_var_decl, => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.fullVarDecl(statement).?),
2477
2478 .assign_destructure => scope = try assignDestructureMaybeDecls(gz, scope, statement, block_arena_allocator),
2479
2480 .@"defer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal),
2481 .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error),
2482
2483 .assign => try assign(gz, scope, statement),
2484
2485 .assign_shl => try assignShift(gz, scope, statement, .shl),
2486 .assign_shr => try assignShift(gz, scope, statement, .shr),
2487
2488 .assign_bit_and => try assignOp(gz, scope, statement, .bit_and),
2489 .assign_bit_or => try assignOp(gz, scope, statement, .bit_or),
2490 .assign_bit_xor => try assignOp(gz, scope, statement, .xor),
2491 .assign_div => try assignOp(gz, scope, statement, .div),
2492 .assign_sub => try assignOp(gz, scope, statement, .sub),
2493 .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap),
2494 .assign_mod => try assignOp(gz, scope, statement, .mod_rem),
2495 .assign_add => try assignOp(gz, scope, statement, .add),
2496 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
2497 .assign_mul => try assignOp(gz, scope, statement, .mul),
2498 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
2499
2500 .grouped_expression => {
2501 inner_node = node_data[statement].lhs;
2502 continue;
2503 },
2504
2505 .while_simple,
2506 .while_cont,
2507 .@"while", => _ = try whileExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullWhile(inner_node).?, true),
2508
2509 .for_simple,
2510 .@"for", => _ = try forExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullFor(inner_node).?, true),
2511
2512 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
2513 // zig fmt: on
2514 }
2515 break;
2516 }
2517 }
2518
2519 try genDefers(gz, parent_scope, scope, .normal_only);
2520 try checkUsed(gz, parent_scope, scope);
2521}
2522
2523/// Returns AST source node of the thing that is noreturn if the statement is
2524/// definitely `noreturn`. Otherwise returns 0.
2525fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
2526 try emitDbgNode(gz, statement);
2527 // We need to emit an error if the result is not `noreturn` or `void`, but
2528 // we want to avoid adding the ZIR instruction if possible for performance.
2529 const maybe_unused_result = try expr(gz, scope, .{ .rl = .none }, statement);
2530 return addEnsureResult(gz, maybe_unused_result, statement);
2531}
2532
2533fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
2534 var noreturn_src_node: Ast.Node.Index = 0;
2535 const elide_check = if (maybe_unused_result.toIndex()) |inst| b: {
2536 // Note that this array becomes invalid after appending more items to it
2537 // in the above while loop.
2538 const zir_tags = gz.astgen.instructions.items(.tag);
2539 switch (zir_tags[@intFromEnum(inst)]) {
2540 // For some instructions, modify the zir data
2541 // so we can avoid a separate ensure_result_used instruction.
2542 .call, .field_call => {
2543 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index;
2544 comptime assert(std.meta.fieldIndex(Zir.Inst.Call, "flags") ==
2545 std.meta.fieldIndex(Zir.Inst.FieldCall, "flags"));
2546 const flags: *Zir.Inst.Call.Flags = @ptrCast(&gz.astgen.extra.items[
2547 break_extra + std.meta.fieldIndex(Zir.Inst.Call, "flags").?
2548 ]);
2549 flags.ensure_result_used = true;
2550 break :b true;
2551 },
2552 .builtin_call => {
2553 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index;
2554 const flags: *Zir.Inst.BuiltinCall.Flags = @ptrCast(&gz.astgen.extra.items[
2555 break_extra + std.meta.fieldIndex(Zir.Inst.BuiltinCall, "flags").?
2556 ]);
2557 flags.ensure_result_used = true;
2558 break :b true;
2559 },
2560
2561 // ZIR instructions that might be a type other than `noreturn` or `void`.
2562 .add,
2563 .addwrap,
2564 .add_sat,
2565 .add_unsafe,
2566 .param,
2567 .param_comptime,
2568 .param_anytype,
2569 .param_anytype_comptime,
2570 .alloc,
2571 .alloc_mut,
2572 .alloc_comptime_mut,
2573 .alloc_inferred,
2574 .alloc_inferred_mut,
2575 .alloc_inferred_comptime,
2576 .alloc_inferred_comptime_mut,
2577 .make_ptr_const,
2578 .array_cat,
2579 .array_mul,
2580 .array_type,
2581 .array_type_sentinel,
2582 .elem_type,
2583 .indexable_ptr_elem_type,
2584 .vector_elem_type,
2585 .vector_type,
2586 .indexable_ptr_len,
2587 .anyframe_type,
2588 .as_node,
2589 .as_shift_operand,
2590 .bit_and,
2591 .bitcast,
2592 .bit_or,
2593 .block,
2594 .block_comptime,
2595 .block_inline,
2596 .declaration,
2597 .suspend_block,
2598 .loop,
2599 .bool_br_and,
2600 .bool_br_or,
2601 .bool_not,
2602 .cmp_lt,
2603 .cmp_lte,
2604 .cmp_eq,
2605 .cmp_gte,
2606 .cmp_gt,
2607 .cmp_neq,
2608 .decl_ref,
2609 .decl_val,
2610 .load,
2611 .div,
2612 .elem_ptr,
2613 .elem_val,
2614 .elem_ptr_node,
2615 .elem_val_node,
2616 .elem_val_imm,
2617 .field_ptr,
2618 .field_val,
2619 .field_ptr_named,
2620 .field_val_named,
2621 .func,
2622 .func_inferred,
2623 .func_fancy,
2624 .int,
2625 .int_big,
2626 .float,
2627 .float128,
2628 .int_type,
2629 .is_non_null,
2630 .is_non_null_ptr,
2631 .is_non_err,
2632 .is_non_err_ptr,
2633 .ret_is_non_err,
2634 .mod_rem,
2635 .mul,
2636 .mulwrap,
2637 .mul_sat,
2638 .ref,
2639 .shl,
2640 .shl_sat,
2641 .shr,
2642 .str,
2643 .sub,
2644 .subwrap,
2645 .sub_sat,
2646 .negate,
2647 .negate_wrap,
2648 .typeof,
2649 .typeof_builtin,
2650 .xor,
2651 .optional_type,
2652 .optional_payload_safe,
2653 .optional_payload_unsafe,
2654 .optional_payload_safe_ptr,
2655 .optional_payload_unsafe_ptr,
2656 .err_union_payload_unsafe,
2657 .err_union_payload_unsafe_ptr,
2658 .err_union_code,
2659 .err_union_code_ptr,
2660 .ptr_type,
2661 .enum_literal,
2662 .merge_error_sets,
2663 .error_union_type,
2664 .bit_not,
2665 .error_value,
2666 .slice_start,
2667 .slice_end,
2668 .slice_sentinel,
2669 .slice_length,
2670 .import,
2671 .switch_block,
2672 .switch_block_ref,
2673 .switch_block_err_union,
2674 .union_init,
2675 .field_type_ref,
2676 .error_set_decl,
2677 .error_set_decl_anon,
2678 .error_set_decl_func,
2679 .enum_from_int,
2680 .int_from_enum,
2681 .type_info,
2682 .size_of,
2683 .bit_size_of,
2684 .typeof_log2_int_type,
2685 .int_from_ptr,
2686 .align_of,
2687 .int_from_bool,
2688 .embed_file,
2689 .error_name,
2690 .sqrt,
2691 .sin,
2692 .cos,
2693 .tan,
2694 .exp,
2695 .exp2,
2696 .log,
2697 .log2,
2698 .log10,
2699 .abs,
2700 .floor,
2701 .ceil,
2702 .trunc,
2703 .round,
2704 .tag_name,
2705 .type_name,
2706 .frame_type,
2707 .frame_size,
2708 .int_from_float,
2709 .float_from_int,
2710 .ptr_from_int,
2711 .float_cast,
2712 .int_cast,
2713 .ptr_cast,
2714 .truncate,
2715 .has_decl,
2716 .has_field,
2717 .clz,
2718 .ctz,
2719 .pop_count,
2720 .byte_swap,
2721 .bit_reverse,
2722 .div_exact,
2723 .div_floor,
2724 .div_trunc,
2725 .mod,
2726 .rem,
2727 .shl_exact,
2728 .shr_exact,
2729 .bit_offset_of,
2730 .offset_of,
2731 .splat,
2732 .reduce,
2733 .shuffle,
2734 .atomic_load,
2735 .atomic_rmw,
2736 .mul_add,
2737 .field_parent_ptr,
2738 .max,
2739 .min,
2740 .c_import,
2741 .@"resume",
2742 .@"await",
2743 .ret_err_value_code,
2744 .closure_get,
2745 .ret_ptr,
2746 .ret_type,
2747 .for_len,
2748 .@"try",
2749 .try_ptr,
2750 .opt_eu_base_ptr_init,
2751 .coerce_ptr_elem_ty,
2752 .struct_init_empty,
2753 .struct_init_empty_result,
2754 .struct_init_empty_ref_result,
2755 .struct_init_anon,
2756 .struct_init,
2757 .struct_init_ref,
2758 .struct_init_field_type,
2759 .struct_init_field_ptr,
2760 .array_init_anon,
2761 .array_init,
2762 .array_init_ref,
2763 .validate_array_init_ref_ty,
2764 .array_init_elem_type,
2765 .array_init_elem_ptr,
2766 => break :b false,
2767
2768 .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {
2769 .breakpoint,
2770 .fence,
2771 .set_float_mode,
2772 .set_align_stack,
2773 .set_cold,
2774 => break :b true,
2775 else => break :b false,
2776 },
2777
2778 // ZIR instructions that are always `noreturn`.
2779 .@"break",
2780 .break_inline,
2781 .condbr,
2782 .condbr_inline,
2783 .compile_error,
2784 .ret_node,
2785 .ret_load,
2786 .ret_implicit,
2787 .ret_err_value,
2788 .@"unreachable",
2789 .repeat,
2790 .repeat_inline,
2791 .panic,
2792 .trap,
2793 .check_comptime_control_flow,
2794 => {
2795 noreturn_src_node = statement;
2796 break :b true;
2797 },
2798
2799 // ZIR instructions that are always `void`.
2800 .dbg_stmt,
2801 .dbg_var_ptr,
2802 .dbg_var_val,
2803 .ensure_result_used,
2804 .ensure_result_non_error,
2805 .ensure_err_union_payload_void,
2806 .@"export",
2807 .export_value,
2808 .set_eval_branch_quota,
2809 .atomic_store,
2810 .store_node,
2811 .store_to_inferred_ptr,
2812 .resolve_inferred_alloc,
2813 .set_runtime_safety,
2814 .closure_capture,
2815 .memcpy,
2816 .memset,
2817 .validate_deref,
2818 .validate_destructure,
2819 .save_err_ret_index,
2820 .restore_err_ret_index_unconditional,
2821 .restore_err_ret_index_fn_entry,
2822 .validate_struct_init_ty,
2823 .validate_struct_init_result_ty,
2824 .validate_ptr_struct_init,
2825 .validate_array_init_ty,
2826 .validate_array_init_result_ty,
2827 .validate_ptr_array_init,
2828 .validate_ref_ty,
2829 => break :b true,
2830
2831 .@"defer" => unreachable,
2832 .defer_err_code => unreachable,
2833 }
2834 } else switch (maybe_unused_result) {
2835 .none => unreachable,
2836
2837 .unreachable_value => b: {
2838 noreturn_src_node = statement;
2839 break :b true;
2840 },
2841
2842 .void_value => true,
2843
2844 else => false,
2845 };
2846 if (!elide_check) {
2847 _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
2848 }
2849 return noreturn_src_node;
2850}
2851
2852fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
2853 have_any: bool,
2854 have_normal: bool,
2855 have_err: bool,
2856 need_err_code: bool,
2857} {
2858 var have_normal = false;
2859 var have_err = false;
2860 var need_err_code = false;
2861 var scope = inner_scope;
2862 while (scope != outer_scope) {
2863 switch (scope.tag) {
2864 .gen_zir => scope = scope.cast(GenZir).?.parent,
2865 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2866 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2867 .defer_normal => {
2868 const defer_scope = scope.cast(Scope.Defer).?;
2869 scope = defer_scope.parent;
2870
2871 have_normal = true;
2872 },
2873 .defer_error => {
2874 const defer_scope = scope.cast(Scope.Defer).?;
2875 scope = defer_scope.parent;
2876
2877 have_err = true;
2878
2879 const have_err_payload = defer_scope.remapped_err_code != .none;
2880 need_err_code = need_err_code or have_err_payload;
2881 },
2882 .namespace, .enum_namespace => unreachable,
2883 .top => unreachable,
2884 }
2885 }
2886 return .{
2887 .have_any = have_normal or have_err,
2888 .have_normal = have_normal,
2889 .have_err = have_err,
2890 .need_err_code = need_err_code,
2891 };
2892}
2893
2894const DefersToEmit = union(enum) {
2895 both: Zir.Inst.Ref, // err code
2896 both_sans_err,
2897 normal_only,
2898};
2899
2900fn genDefers(
2901 gz: *GenZir,
2902 outer_scope: *Scope,
2903 inner_scope: *Scope,
2904 which_ones: DefersToEmit,
2905) InnerError!void {
2906 const gpa = gz.astgen.gpa;
2907
2908 var scope = inner_scope;
2909 while (scope != outer_scope) {
2910 switch (scope.tag) {
2911 .gen_zir => scope = scope.cast(GenZir).?.parent,
2912 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2913 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2914 .defer_normal => {
2915 const defer_scope = scope.cast(Scope.Defer).?;
2916 scope = defer_scope.parent;
2917 try gz.addDefer(defer_scope.index, defer_scope.len);
2918 },
2919 .defer_error => {
2920 const defer_scope = scope.cast(Scope.Defer).?;
2921 scope = defer_scope.parent;
2922 switch (which_ones) {
2923 .both_sans_err => {
2924 try gz.addDefer(defer_scope.index, defer_scope.len);
2925 },
2926 .both => |err_code| {
2927 if (defer_scope.remapped_err_code.unwrap()) |remapped_err_code| {
2928 try gz.instructions.ensureUnusedCapacity(gpa, 1);
2929 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
2930
2931 const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{
2932 .remapped_err_code = remapped_err_code,
2933 .index = defer_scope.index,
2934 .len = defer_scope.len,
2935 });
2936 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
2937 gz.astgen.instructions.appendAssumeCapacity(.{
2938 .tag = .defer_err_code,
2939 .data = .{ .defer_err_code = .{
2940 .err_code = err_code,
2941 .payload_index = payload_index,
2942 } },
2943 });
2944 gz.instructions.appendAssumeCapacity(new_index);
2945 } else {
2946 try gz.addDefer(defer_scope.index, defer_scope.len);
2947 }
2948 },
2949 .normal_only => continue,
2950 }
2951 },
2952 .namespace, .enum_namespace => unreachable,
2953 .top => unreachable,
2954 }
2955 }
2956}
2957
2958fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!void {
2959 const astgen = gz.astgen;
2960
2961 var scope = inner_scope;
2962 while (scope != outer_scope) {
2963 switch (scope.tag) {
2964 .gen_zir => scope = scope.cast(GenZir).?.parent,
2965 .local_val => {
2966 const s = scope.cast(Scope.LocalVal).?;
2967 if (s.used == 0 and s.discarded == 0) {
2968 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
2969 } else if (s.used != 0 and s.discarded != 0) {
2970 try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
2971 try gz.astgen.errNoteTok(s.used, "used here", .{}),
2972 });
2973 }
2974 scope = s.parent;
2975 },
2976 .local_ptr => {
2977 const s = scope.cast(Scope.LocalPtr).?;
2978 if (s.used == 0 and s.discarded == 0) {
2979 try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
2980 } else {
2981 if (s.used != 0 and s.discarded != 0) {
2982 try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
2983 try astgen.errNoteTok(s.used, "used here", .{}),
2984 });
2985 }
2986 if (s.id_cat == .@"local variable" and !s.used_as_lvalue) {
2987 try astgen.appendErrorTokNotes(s.token_src, "local variable is never mutated", .{}, &.{
2988 try astgen.errNoteTok(s.token_src, "consider using 'const'", .{}),
2989 });
2990 }
2991 }
2992
2993 scope = s.parent;
2994 },
2995 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2996 .namespace, .enum_namespace => unreachable,
2997 .top => unreachable,
2998 }
2999 }
3000}
3001
3002fn deferStmt(
3003 gz: *GenZir,
3004 scope: *Scope,
3005 node: Ast.Node.Index,
3006 block_arena: Allocator,
3007 scope_tag: Scope.Tag,
3008) InnerError!*Scope {
3009 var defer_gen = gz.makeSubBlock(scope);
3010 defer_gen.cur_defer_node = node;
3011 defer_gen.any_defer_node = node;
3012 defer defer_gen.unstack();
3013
3014 const tree = gz.astgen.tree;
3015 const node_datas = tree.nodes.items(.data);
3016 const expr_node = node_datas[node].rhs;
3017
3018 const payload_token = node_datas[node].lhs;
3019 var local_val_scope: Scope.LocalVal = undefined;
3020 var opt_remapped_err_code: Zir.Inst.OptionalIndex = .none;
3021 const have_err_code = scope_tag == .defer_error and payload_token != 0;
3022 const sub_scope = if (!have_err_code) &defer_gen.base else blk: {
3023 const ident_name = try gz.astgen.identAsString(payload_token);
3024 const remapped_err_code: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
3025 opt_remapped_err_code = remapped_err_code.toOptional();
3026 try gz.astgen.instructions.append(gz.astgen.gpa, .{
3027 .tag = .extended,
3028 .data = .{ .extended = .{
3029 .opcode = .value_placeholder,
3030 .small = undefined,
3031 .operand = undefined,
3032 } },
3033 });
3034 const remapped_err_code_ref = remapped_err_code.toRef();
3035 local_val_scope = .{
3036 .parent = &defer_gen.base,
3037 .gen_zir = gz,
3038 .name = ident_name,
3039 .inst = remapped_err_code_ref,
3040 .token_src = payload_token,
3041 .id_cat = .capture,
3042 };
3043 try gz.addDbgVar(.dbg_var_val, ident_name, remapped_err_code_ref);
3044 break :blk &local_val_scope.base;
3045 };
3046 _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node);
3047 try checkUsed(gz, scope, sub_scope);
3048 _ = try defer_gen.addBreak(.break_inline, @enumFromInt(0), .void_value);
3049
3050 // We must handle ref_table for remapped_err_code manually.
3051 const body = defer_gen.instructionsSlice();
3052 const body_len = blk: {
3053 var refs: u32 = 0;
3054 if (opt_remapped_err_code.unwrap()) |remapped_err_code| {
3055 var cur_inst = remapped_err_code;
3056 while (gz.astgen.ref_table.get(cur_inst)) |ref_inst| {
3057 refs += 1;
3058 cur_inst = ref_inst;
3059 }
3060 }
3061 break :blk gz.astgen.countBodyLenAfterFixups(body) + refs;
3062 };
3063
3064 const index: u32 = @intCast(gz.astgen.extra.items.len);
3065 try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len);
3066 if (opt_remapped_err_code.unwrap()) |remapped_err_code| {
3067 if (gz.astgen.ref_table.fetchRemove(remapped_err_code)) |kv| {
3068 gz.astgen.appendPossiblyRefdBodyInst(&gz.astgen.extra, kv.value);
3069 }
3070 }
3071 gz.astgen.appendBodyWithFixups(body);
3072
3073 const defer_scope = try block_arena.create(Scope.Defer);
3074
3075 defer_scope.* = .{
3076 .base = .{ .tag = scope_tag },
3077 .parent = scope,
3078 .index = index,
3079 .len = body_len,
3080 .remapped_err_code = opt_remapped_err_code,
3081 };
3082 return &defer_scope.base;
3083}
3084
3085fn varDecl(
3086 gz: *GenZir,
3087 scope: *Scope,
3088 node: Ast.Node.Index,
3089 block_arena: Allocator,
3090 var_decl: Ast.full.VarDecl,
3091) InnerError!*Scope {
3092 try emitDbgNode(gz, node);
3093 const astgen = gz.astgen;
3094 const tree = astgen.tree;
3095 const token_tags = tree.tokens.items(.tag);
3096 const main_tokens = tree.nodes.items(.main_token);
3097
3098 const name_token = var_decl.ast.mut_token + 1;
3099 const ident_name_raw = tree.tokenSlice(name_token);
3100 if (mem.eql(u8, ident_name_raw, "_")) {
3101 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
3102 }
3103 const ident_name = try astgen.identAsString(name_token);
3104
3105 try astgen.detectLocalShadowing(
3106 scope,
3107 ident_name,
3108 name_token,
3109 ident_name_raw,
3110 if (token_tags[var_decl.ast.mut_token] == .keyword_const) .@"local constant" else .@"local variable",
3111 );
3112
3113 if (var_decl.ast.init_node == 0) {
3114 return astgen.failNode(node, "variables must be initialized", .{});
3115 }
3116
3117 if (var_decl.ast.addrspace_node != 0) {
3118 return astgen.failTok(main_tokens[var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
3119 }
3120
3121 if (var_decl.ast.section_node != 0) {
3122 return astgen.failTok(main_tokens[var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
3123 }
3124
3125 const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0)
3126 try expr(gz, scope, coerced_align_ri, var_decl.ast.align_node)
3127 else
3128 .none;
3129
3130 switch (token_tags[var_decl.ast.mut_token]) {
3131 .keyword_const => {
3132 if (var_decl.comptime_token) |comptime_token| {
3133 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
3134 }
3135
3136 // Depending on the type of AST the initialization expression is, we may need an lvalue
3137 // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
3138 // the variable, no memory location needed.
3139 const type_node = var_decl.ast.type_node;
3140 if (align_inst == .none and
3141 !astgen.nodes_need_rl.contains(node))
3142 {
3143 const result_info: ResultInfo = if (type_node != 0) .{
3144 .rl = .{ .ty = try typeExpr(gz, scope, type_node) },
3145 .ctx = .const_init,
3146 } else .{ .rl = .none, .ctx = .const_init };
3147 const prev_anon_name_strategy = gz.anon_name_strategy;
3148 gz.anon_name_strategy = .dbg_var;
3149 const init_inst = try reachableExpr(gz, scope, result_info, var_decl.ast.init_node, node);
3150 gz.anon_name_strategy = prev_anon_name_strategy;
3151
3152 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
3153
3154 // The const init expression may have modified the error return trace, so signal
3155 // to Sema that it should save the new index for restoring later.
3156 if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node))
3157 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
3158
3159 const sub_scope = try block_arena.create(Scope.LocalVal);
3160 sub_scope.* = .{
3161 .parent = scope,
3162 .gen_zir = gz,
3163 .name = ident_name,
3164 .inst = init_inst,
3165 .token_src = name_token,
3166 .id_cat = .@"local constant",
3167 };
3168 return &sub_scope.base;
3169 }
3170
3171 const is_comptime = gz.is_comptime or
3172 tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime";
3173
3174 var resolve_inferred_alloc: Zir.Inst.Ref = .none;
3175 var opt_type_inst: Zir.Inst.Ref = .none;
3176 const init_rl: ResultInfo.Loc = if (type_node != 0) init_rl: {
3177 const type_inst = try typeExpr(gz, scope, type_node);
3178 opt_type_inst = type_inst;
3179 if (align_inst == .none) {
3180 break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } };
3181 } else {
3182 break :init_rl .{ .ptr = .{ .inst = try gz.addAllocExtended(.{
3183 .node = node,
3184 .type_inst = type_inst,
3185 .align_inst = align_inst,
3186 .is_const = true,
3187 .is_comptime = is_comptime,
3188 }) } };
3189 }
3190 } else init_rl: {
3191 const alloc_inst = if (align_inst == .none) ptr: {
3192 const tag: Zir.Inst.Tag = if (is_comptime)
3193 .alloc_inferred_comptime
3194 else
3195 .alloc_inferred;
3196 break :ptr try gz.addNode(tag, node);
3197 } else ptr: {
3198 break :ptr try gz.addAllocExtended(.{
3199 .node = node,
3200 .type_inst = .none,
3201 .align_inst = align_inst,
3202 .is_const = true,
3203 .is_comptime = is_comptime,
3204 });
3205 };
3206 resolve_inferred_alloc = alloc_inst;
3207 break :init_rl .{ .inferred_ptr = alloc_inst };
3208 };
3209 const var_ptr = switch (init_rl) {
3210 .ptr => |ptr| ptr.inst,
3211 .inferred_ptr => |inst| inst,
3212 else => unreachable,
3213 };
3214 const init_result_info: ResultInfo = .{ .rl = init_rl, .ctx = .const_init };
3215
3216 const prev_anon_name_strategy = gz.anon_name_strategy;
3217 gz.anon_name_strategy = .dbg_var;
3218 defer gz.anon_name_strategy = prev_anon_name_strategy;
3219 const init_inst = try reachableExpr(gz, scope, init_result_info, var_decl.ast.init_node, node);
3220
3221 // The const init expression may have modified the error return trace, so signal
3222 // to Sema that it should save the new index for restoring later.
3223 if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node))
3224 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
3225
3226 const const_ptr = if (resolve_inferred_alloc != .none) p: {
3227 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
3228 break :p var_ptr;
3229 } else try gz.addUnNode(.make_ptr_const, var_ptr, node);
3230
3231 try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr);
3232
3233 const sub_scope = try block_arena.create(Scope.LocalPtr);
3234 sub_scope.* = .{
3235 .parent = scope,
3236 .gen_zir = gz,
3237 .name = ident_name,
3238 .ptr = const_ptr,
3239 .token_src = name_token,
3240 .maybe_comptime = true,
3241 .id_cat = .@"local constant",
3242 };
3243 return &sub_scope.base;
3244 },
3245 .keyword_var => {
3246 if (var_decl.comptime_token != null and gz.is_comptime)
3247 return astgen.failTok(var_decl.comptime_token.?, "'comptime var' is redundant in comptime scope", .{});
3248 const is_comptime = var_decl.comptime_token != null or gz.is_comptime;
3249 var resolve_inferred_alloc: Zir.Inst.Ref = .none;
3250 const alloc: Zir.Inst.Ref, const result_info: ResultInfo = if (var_decl.ast.type_node != 0) a: {
3251 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
3252 const alloc = alloc: {
3253 if (align_inst == .none) {
3254 const tag: Zir.Inst.Tag = if (is_comptime)
3255 .alloc_comptime_mut
3256 else
3257 .alloc_mut;
3258 break :alloc try gz.addUnNode(tag, type_inst, node);
3259 } else {
3260 break :alloc try gz.addAllocExtended(.{
3261 .node = node,
3262 .type_inst = type_inst,
3263 .align_inst = align_inst,
3264 .is_const = false,
3265 .is_comptime = is_comptime,
3266 });
3267 }
3268 };
3269 break :a .{ alloc, .{ .rl = .{ .ptr = .{ .inst = alloc } } } };
3270 } else a: {
3271 const alloc = alloc: {
3272 if (align_inst == .none) {
3273 const tag: Zir.Inst.Tag = if (is_comptime)
3274 .alloc_inferred_comptime_mut
3275 else
3276 .alloc_inferred_mut;
3277 break :alloc try gz.addNode(tag, node);
3278 } else {
3279 break :alloc try gz.addAllocExtended(.{
3280 .node = node,
3281 .type_inst = .none,
3282 .align_inst = align_inst,
3283 .is_const = false,
3284 .is_comptime = is_comptime,
3285 });
3286 }
3287 };
3288 resolve_inferred_alloc = alloc;
3289 break :a .{ alloc, .{ .rl = .{ .inferred_ptr = alloc } } };
3290 };
3291 const prev_anon_name_strategy = gz.anon_name_strategy;
3292 gz.anon_name_strategy = .dbg_var;
3293 _ = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, is_comptime);
3294 gz.anon_name_strategy = prev_anon_name_strategy;
3295 if (resolve_inferred_alloc != .none) {
3296 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
3297 }
3298
3299 try gz.addDbgVar(.dbg_var_ptr, ident_name, alloc);
3300
3301 const sub_scope = try block_arena.create(Scope.LocalPtr);
3302 sub_scope.* = .{
3303 .parent = scope,
3304 .gen_zir = gz,
3305 .name = ident_name,
3306 .ptr = alloc,
3307 .token_src = name_token,
3308 .maybe_comptime = is_comptime,
3309 .id_cat = .@"local variable",
3310 };
3311 return &sub_scope.base;
3312 },
3313 else => unreachable,
3314 }
3315}
3316
3317fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void {
3318 // The instruction emitted here is for debugging runtime code.
3319 // If the current block will be evaluated only during semantic analysis
3320 // then no dbg_stmt ZIR instruction is needed.
3321 if (gz.is_comptime) return;
3322 const astgen = gz.astgen;
3323 astgen.advanceSourceCursorToNode(node);
3324 const line = astgen.source_line - gz.decl_line;
3325 const column = astgen.source_column;
3326 try emitDbgStmt(gz, .{ line, column });
3327}
3328
3329fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
3330 try emitDbgNode(gz, infix_node);
3331 const astgen = gz.astgen;
3332 const tree = astgen.tree;
3333 const node_datas = tree.nodes.items(.data);
3334 const main_tokens = tree.nodes.items(.main_token);
3335 const node_tags = tree.nodes.items(.tag);
3336
3337 const lhs = node_datas[infix_node].lhs;
3338 const rhs = node_datas[infix_node].rhs;
3339 if (node_tags[lhs] == .identifier) {
3340 // This intentionally does not support `@"_"` syntax.
3341 const ident_name = tree.tokenSlice(main_tokens[lhs]);
3342 if (mem.eql(u8, ident_name, "_")) {
3343 _ = try expr(gz, scope, .{ .rl = .discard, .ctx = .assignment }, rhs);
3344 return;
3345 }
3346 }
3347 const lvalue = try lvalExpr(gz, scope, lhs);
3348 _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{
3349 .inst = lvalue,
3350 .src_node = infix_node,
3351 } } }, rhs);
3352}
3353
3354/// Handles destructure assignments where no LHS is a `const` or `var` decl.
3355fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!void {
3356 try emitDbgNode(gz, node);
3357 const astgen = gz.astgen;
3358 const tree = astgen.tree;
3359 const token_tags = tree.tokens.items(.tag);
3360 const node_datas = tree.nodes.items(.data);
3361 const main_tokens = tree.nodes.items(.main_token);
3362 const node_tags = tree.nodes.items(.tag);
3363
3364 const extra_index = node_datas[node].lhs;
3365 const lhs_count = tree.extra_data[extra_index];
3366 const lhs_nodes: []const Ast.Node.Index = @ptrCast(tree.extra_data[extra_index + 1 ..][0..lhs_count]);
3367 const rhs = node_datas[node].rhs;
3368
3369 const maybe_comptime_token = tree.firstToken(node) - 1;
3370 const declared_comptime = token_tags[maybe_comptime_token] == .keyword_comptime;
3371
3372 if (declared_comptime and gz.is_comptime) {
3373 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
3374 }
3375
3376 // If this expression is marked comptime, we must wrap the whole thing in a comptime block.
3377 var gz_buf: GenZir = undefined;
3378 const inner_gz = if (declared_comptime) bs: {
3379 gz_buf = gz.makeSubBlock(scope);
3380 gz_buf.is_comptime = true;
3381 break :bs &gz_buf;
3382 } else gz;
3383 defer if (declared_comptime) inner_gz.unstack();
3384
3385 const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, lhs_nodes.len);
3386 for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| {
3387 if (node_tags[lhs_node] == .identifier) {
3388 // This intentionally does not support `@"_"` syntax.
3389 const ident_name = tree.tokenSlice(main_tokens[lhs_node]);
3390 if (mem.eql(u8, ident_name, "_")) {
3391 lhs_rl.* = .discard;
3392 continue;
3393 }
3394 }
3395 lhs_rl.* = .{ .typed_ptr = .{
3396 .inst = try lvalExpr(inner_gz, scope, lhs_node),
3397 .src_node = lhs_node,
3398 } };
3399 }
3400
3401 const ri: ResultInfo = .{ .rl = .{ .destructure = .{
3402 .src_node = node,
3403 .components = rl_components,
3404 } } };
3405
3406 _ = try expr(inner_gz, scope, ri, rhs);
3407
3408 if (declared_comptime) {
3409 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
3410 _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value);
3411 try inner_gz.setBlockBody(comptime_block_inst);
3412 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);
3413 }
3414}
3415
3416/// Handles destructure assignments where the LHS may contain `const` or `var` decls.
3417fn assignDestructureMaybeDecls(
3418 gz: *GenZir,
3419 scope: *Scope,
3420 node: Ast.Node.Index,
3421 block_arena: Allocator,
3422) InnerError!*Scope {
3423 try emitDbgNode(gz, node);
3424 const astgen = gz.astgen;
3425 const tree = astgen.tree;
3426 const token_tags = tree.tokens.items(.tag);
3427 const node_datas = tree.nodes.items(.data);
3428 const main_tokens = tree.nodes.items(.main_token);
3429 const node_tags = tree.nodes.items(.tag);
3430
3431 const extra_index = node_datas[node].lhs;
3432 const lhs_count = tree.extra_data[extra_index];
3433 const lhs_nodes: []const Ast.Node.Index = @ptrCast(tree.extra_data[extra_index + 1 ..][0..lhs_count]);
3434 const rhs = node_datas[node].rhs;
3435
3436 const maybe_comptime_token = tree.firstToken(node) - 1;
3437 const declared_comptime = token_tags[maybe_comptime_token] == .keyword_comptime;
3438 if (declared_comptime and gz.is_comptime) {
3439 return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
3440 }
3441
3442 const is_comptime = declared_comptime or gz.is_comptime;
3443 const rhs_is_comptime = tree.nodes.items(.tag)[rhs] == .@"comptime";
3444
3445 // When declaring consts via a destructure, we always use a result pointer.
3446 // This avoids the need to create tuple types, and is also likely easier to
3447 // optimize, since it's a bit tricky for the optimizer to "split up" the
3448 // value into individual pointer writes down the line.
3449
3450 // We know this rl information won't live past the evaluation of this
3451 // expression, so it may as well go in the block arena.
3452 const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, lhs_nodes.len);
3453 var any_non_const_lhs = false;
3454 var any_lvalue_expr = false;
3455 for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| {
3456 switch (node_tags[lhs_node]) {
3457 .identifier => {
3458 // This intentionally does not support `@"_"` syntax.
3459 const ident_name = tree.tokenSlice(main_tokens[lhs_node]);
3460 if (mem.eql(u8, ident_name, "_")) {
3461 any_non_const_lhs = true;
3462 lhs_rl.* = .discard;
3463 continue;
3464 }
3465 },
3466 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => {
3467 const full = tree.fullVarDecl(lhs_node).?;
3468
3469 const name_token = full.ast.mut_token + 1;
3470 const ident_name_raw = tree.tokenSlice(name_token);
3471 if (mem.eql(u8, ident_name_raw, "_")) {
3472 return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
3473 }
3474
3475 // We detect shadowing in the second pass over these, while we're creating scopes.
3476
3477 if (full.ast.addrspace_node != 0) {
3478 return astgen.failTok(main_tokens[full.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
3479 }
3480 if (full.ast.section_node != 0) {
3481 return astgen.failTok(main_tokens[full.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
3482 }
3483
3484 const is_const = switch (token_tags[full.ast.mut_token]) {
3485 .keyword_var => false,
3486 .keyword_const => true,
3487 else => unreachable,
3488 };
3489 if (!is_const) any_non_const_lhs = true;
3490
3491 // We also mark `const`s as comptime if the RHS is definitely comptime-known.
3492 const this_lhs_comptime = is_comptime or (is_const and rhs_is_comptime);
3493
3494 const align_inst: Zir.Inst.Ref = if (full.ast.align_node != 0)
3495 try expr(gz, scope, coerced_align_ri, full.ast.align_node)
3496 else
3497 .none;
3498
3499 if (full.ast.type_node != 0) {
3500 // Typed alloc
3501 const type_inst = try typeExpr(gz, scope, full.ast.type_node);
3502 const ptr = if (align_inst == .none) ptr: {
3503 const tag: Zir.Inst.Tag = if (is_const)
3504 .alloc
3505 else if (this_lhs_comptime)
3506 .alloc_comptime_mut
3507 else
3508 .alloc_mut;
3509 break :ptr try gz.addUnNode(tag, type_inst, node);
3510 } else try gz.addAllocExtended(.{
3511 .node = node,
3512 .type_inst = type_inst,
3513 .align_inst = align_inst,
3514 .is_const = is_const,
3515 .is_comptime = this_lhs_comptime,
3516 });
3517 lhs_rl.* = .{ .typed_ptr = .{ .inst = ptr } };
3518 } else {
3519 // Inferred alloc
3520 const ptr = if (align_inst == .none) ptr: {
3521 const tag: Zir.Inst.Tag = if (is_const) tag: {
3522 break :tag if (this_lhs_comptime) .alloc_inferred_comptime else .alloc_inferred;
3523 } else tag: {
3524 break :tag if (this_lhs_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut;
3525 };
3526 break :ptr try gz.addNode(tag, node);
3527 } else try gz.addAllocExtended(.{
3528 .node = node,
3529 .type_inst = .none,
3530 .align_inst = align_inst,
3531 .is_const = is_const,
3532 .is_comptime = this_lhs_comptime,
3533 });
3534 lhs_rl.* = .{ .inferred_ptr = ptr };
3535 }
3536
3537 continue;
3538 },
3539 else => {},
3540 }
3541 // This LHS is just an lvalue expression.
3542 // We will fill in its result pointer later, inside a comptime block.
3543 any_non_const_lhs = true;
3544 any_lvalue_expr = true;
3545 lhs_rl.* = .{ .typed_ptr = .{
3546 .inst = undefined,
3547 .src_node = lhs_node,
3548 } };
3549 }
3550
3551 if (declared_comptime and !any_non_const_lhs) {
3552 try astgen.appendErrorTok(maybe_comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
3553 }
3554
3555 // If this expression is marked comptime, we must wrap it in a comptime block.
3556 var gz_buf: GenZir = undefined;
3557 const inner_gz = if (declared_comptime) bs: {
3558 gz_buf = gz.makeSubBlock(scope);
3559 gz_buf.is_comptime = true;
3560 break :bs &gz_buf;
3561 } else gz;
3562 defer if (declared_comptime) inner_gz.unstack();
3563
3564 if (any_lvalue_expr) {
3565 // At least one LHS was an lvalue expr. Iterate again in order to
3566 // evaluate the lvalues from within the possible block_comptime.
3567 for (rl_components, lhs_nodes) |*lhs_rl, lhs_node| {
3568 if (lhs_rl.* != .typed_ptr) continue;
3569 switch (node_tags[lhs_node]) {
3570 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue,
3571 else => {},
3572 }
3573 lhs_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, lhs_node);
3574 }
3575 }
3576
3577 // We can't give a reasonable anon name strategy for destructured inits, so
3578 // leave it at its default of `.anon`.
3579 _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{
3580 .src_node = node,
3581 .components = rl_components,
3582 } } }, rhs, node);
3583
3584 if (declared_comptime) {
3585 // Finish the block_comptime. Inferred alloc resolution etc will occur
3586 // in the parent block.
3587 const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
3588 _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value);
3589 try inner_gz.setBlockBody(comptime_block_inst);
3590 try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);
3591 }
3592
3593 // Now, iterate over the LHS exprs to construct any new scopes.
3594 // If there were any inferred allocations, resolve them.
3595 // If there were any `const` decls, make the pointer constant.
3596 var cur_scope = scope;
3597 for (rl_components, lhs_nodes) |lhs_rl, lhs_node| {
3598 switch (node_tags[lhs_node]) {
3599 .local_var_decl, .simple_var_decl, .aligned_var_decl => {},
3600 else => continue, // We were mutating an existing lvalue - nothing to do
3601 }
3602 const full = tree.fullVarDecl(lhs_node).?;
3603 const raw_ptr = switch (lhs_rl) {
3604 .discard => unreachable,
3605 .typed_ptr => |typed_ptr| typed_ptr.inst,
3606 .inferred_ptr => |ptr_inst| ptr_inst,
3607 };
3608 // If the alloc was inferred, resolve it.
3609 if (full.ast.type_node == 0) {
3610 _ = try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, lhs_node);
3611 }
3612 const is_const = switch (token_tags[full.ast.mut_token]) {
3613 .keyword_var => false,
3614 .keyword_const => true,
3615 else => unreachable,
3616 };
3617 // If the alloc was const, make it const.
3618 const var_ptr = if (is_const and full.ast.type_node != 0) make_const: {
3619 // Note that we don't do this if type_node == 0 since `resolve_inferred_alloc`
3620 // handles it for us.
3621 break :make_const try gz.addUnNode(.make_ptr_const, raw_ptr, node);
3622 } else raw_ptr;
3623 const name_token = full.ast.mut_token + 1;
3624 const ident_name_raw = tree.tokenSlice(name_token);
3625 const ident_name = try astgen.identAsString(name_token);
3626 try astgen.detectLocalShadowing(
3627 cur_scope,
3628 ident_name,
3629 name_token,
3630 ident_name_raw,
3631 if (is_const) .@"local constant" else .@"local variable",
3632 );
3633 try gz.addDbgVar(.dbg_var_ptr, ident_name, var_ptr);
3634 // Finally, create the scope.
3635 const sub_scope = try block_arena.create(Scope.LocalPtr);
3636 sub_scope.* = .{
3637 .parent = cur_scope,
3638 .gen_zir = gz,
3639 .name = ident_name,
3640 .ptr = var_ptr,
3641 .token_src = name_token,
3642 .maybe_comptime = is_const or is_comptime,
3643 .id_cat = if (is_const) .@"local constant" else .@"local variable",
3644 };
3645 cur_scope = &sub_scope.base;
3646 }
3647
3648 return cur_scope;
3649}
3650
3651fn assignOp(
3652 gz: *GenZir,
3653 scope: *Scope,
3654 infix_node: Ast.Node.Index,
3655 op_inst_tag: Zir.Inst.Tag,
3656) InnerError!void {
3657 try emitDbgNode(gz, infix_node);
3658 const astgen = gz.astgen;
3659 const tree = astgen.tree;
3660 const node_datas = tree.nodes.items(.data);
3661
3662 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
3663
3664 const cursor = switch (op_inst_tag) {
3665 .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, infix_node),
3666 else => undefined,
3667 };
3668 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
3669 const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
3670 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = lhs_type } }, node_datas[infix_node].rhs);
3671
3672 switch (op_inst_tag) {
3673 .add, .sub, .mul, .div, .mod_rem => {
3674 try emitDbgStmt(gz, cursor);
3675 },
3676 else => {},
3677 }
3678 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
3679 .lhs = lhs,
3680 .rhs = rhs,
3681 });
3682 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
3683 .lhs = lhs_ptr,
3684 .rhs = result,
3685 });
3686}
3687
3688fn assignShift(
3689 gz: *GenZir,
3690 scope: *Scope,
3691 infix_node: Ast.Node.Index,
3692 op_inst_tag: Zir.Inst.Tag,
3693) InnerError!void {
3694 try emitDbgNode(gz, infix_node);
3695 const astgen = gz.astgen;
3696 const tree = astgen.tree;
3697 const node_datas = tree.nodes.items(.data);
3698
3699 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
3700 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
3701 const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node);
3702 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_type } }, node_datas[infix_node].rhs);
3703
3704 const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
3705 .lhs = lhs,
3706 .rhs = rhs,
3707 });
3708 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
3709 .lhs = lhs_ptr,
3710 .rhs = result,
3711 });
3712}
3713
3714fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
3715 try emitDbgNode(gz, infix_node);
3716 const astgen = gz.astgen;
3717 const tree = astgen.tree;
3718 const node_datas = tree.nodes.items(.data);
3719
3720 const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
3721 const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
3722 // Saturating shift-left allows any integer type for both the LHS and RHS.
3723 const rhs = try expr(gz, scope, .{ .rl = .none }, node_datas[infix_node].rhs);
3724
3725 const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{
3726 .lhs = lhs,
3727 .rhs = rhs,
3728 });
3729 _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
3730 .lhs = lhs_ptr,
3731 .rhs = result,
3732 });
3733}
3734
3735fn ptrType(
3736 gz: *GenZir,
3737 scope: *Scope,
3738 ri: ResultInfo,
3739 node: Ast.Node.Index,
3740 ptr_info: Ast.full.PtrType,
3741) InnerError!Zir.Inst.Ref {
3742 if (ptr_info.size == .C and ptr_info.allowzero_token != null) {
3743 return gz.astgen.failTok(ptr_info.allowzero_token.?, "C pointers always allow address zero", .{});
3744 }
3745
3746 const source_offset = gz.astgen.source_offset;
3747 const source_line = gz.astgen.source_line;
3748 const source_column = gz.astgen.source_column;
3749 const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
3750
3751 var sentinel_ref: Zir.Inst.Ref = .none;
3752 var align_ref: Zir.Inst.Ref = .none;
3753 var addrspace_ref: Zir.Inst.Ref = .none;
3754 var bit_start_ref: Zir.Inst.Ref = .none;
3755 var bit_end_ref: Zir.Inst.Ref = .none;
3756 var trailing_count: u32 = 0;
3757
3758 if (ptr_info.ast.sentinel != 0) {
3759 // These attributes can appear in any order and they all come before the
3760 // element type so we need to reset the source cursor before generating them.
3761 gz.astgen.source_offset = source_offset;
3762 gz.astgen.source_line = source_line;
3763 gz.astgen.source_column = source_column;
3764
3765 sentinel_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, ptr_info.ast.sentinel);
3766 trailing_count += 1;
3767 }
3768 if (ptr_info.ast.addrspace_node != 0) {
3769 gz.astgen.source_offset = source_offset;
3770 gz.astgen.source_line = source_line;
3771 gz.astgen.source_column = source_column;
3772
3773 addrspace_ref = try expr(gz, scope, coerced_addrspace_ri, ptr_info.ast.addrspace_node);
3774 trailing_count += 1;
3775 }
3776 if (ptr_info.ast.align_node != 0) {
3777 gz.astgen.source_offset = source_offset;
3778 gz.astgen.source_line = source_line;
3779 gz.astgen.source_column = source_column;
3780
3781 align_ref = try expr(gz, scope, coerced_align_ri, ptr_info.ast.align_node);
3782 trailing_count += 1;
3783 }
3784 if (ptr_info.ast.bit_range_start != 0) {
3785 assert(ptr_info.ast.bit_range_end != 0);
3786 bit_start_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_start);
3787 bit_end_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_end);
3788 trailing_count += 2;
3789 }
3790
3791 const gpa = gz.astgen.gpa;
3792 try gz.instructions.ensureUnusedCapacity(gpa, 1);
3793 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
3794 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).Struct.fields.len +
3795 trailing_count);
3796
3797 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{
3798 .elem_type = elem_type,
3799 .src_node = gz.nodeIndexToRelative(node),
3800 });
3801 if (sentinel_ref != .none) {
3802 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(sentinel_ref));
3803 }
3804 if (align_ref != .none) {
3805 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(align_ref));
3806 }
3807 if (addrspace_ref != .none) {
3808 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(addrspace_ref));
3809 }
3810 if (bit_start_ref != .none) {
3811 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_start_ref));
3812 gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_end_ref));
3813 }
3814
3815 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
3816 const result = new_index.toRef();
3817 gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
3818 .ptr_type = .{
3819 .flags = .{
3820 .is_allowzero = ptr_info.allowzero_token != null,
3821 .is_mutable = ptr_info.const_token == null,
3822 .is_volatile = ptr_info.volatile_token != null,
3823 .has_sentinel = sentinel_ref != .none,
3824 .has_align = align_ref != .none,
3825 .has_addrspace = addrspace_ref != .none,
3826 .has_bit_range = bit_start_ref != .none,
3827 },
3828 .size = ptr_info.size,
3829 .payload_index = payload_index,
3830 },
3831 } });
3832 gz.instructions.appendAssumeCapacity(new_index);
3833
3834 return rvalue(gz, ri, result, node);
3835}
3836
3837fn arrayType(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref {
3838 const astgen = gz.astgen;
3839 const tree = astgen.tree;
3840 const node_datas = tree.nodes.items(.data);
3841 const node_tags = tree.nodes.items(.tag);
3842 const main_tokens = tree.nodes.items(.main_token);
3843
3844 const len_node = node_datas[node].lhs;
3845 if (node_tags[len_node] == .identifier and
3846 mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
3847 {
3848 return astgen.failNode(len_node, "unable to infer array size", .{});
3849 }
3850 const len = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node);
3851 const elem_type = try typeExpr(gz, scope, node_datas[node].rhs);
3852
3853 const result = try gz.addPlNode(.array_type, node, Zir.Inst.Bin{
3854 .lhs = len,
3855 .rhs = elem_type,
3856 });
3857 return rvalue(gz, ri, result, node);
3858}
3859
3860fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref {
3861 const astgen = gz.astgen;
3862 const tree = astgen.tree;
3863 const node_datas = tree.nodes.items(.data);
3864 const node_tags = tree.nodes.items(.tag);
3865 const main_tokens = tree.nodes.items(.main_token);
3866 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel);
3867
3868 const len_node = node_datas[node].lhs;
3869 if (node_tags[len_node] == .identifier and
3870 mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
3871 {
3872 return astgen.failNode(len_node, "unable to infer array size", .{});
3873 }
3874 const len = try reachableExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node);
3875 const elem_type = try typeExpr(gz, scope, extra.elem_type);
3876 const sentinel = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = elem_type } }, extra.sentinel, node, true);
3877
3878 const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{
3879 .len = len,
3880 .elem_type = elem_type,
3881 .sentinel = sentinel,
3882 });
3883 return rvalue(gz, ri, result, node);
3884}
3885
3886const WipMembers = struct {
3887 payload: *ArrayListUnmanaged(u32),
3888 payload_top: usize,
3889 field_bits_start: u32,
3890 fields_start: u32,
3891 fields_end: u32,
3892 decl_index: u32 = 0,
3893 field_index: u32 = 0,
3894
3895 const Self = @This();
3896
3897 fn init(gpa: Allocator, payload: *ArrayListUnmanaged(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self {
3898 const payload_top: u32 = @intCast(payload.items.len);
3899 const field_bits_start = payload_top + decl_count;
3900 const fields_start = field_bits_start + if (bits_per_field > 0) blk: {
3901 const fields_per_u32 = 32 / bits_per_field;
3902 break :blk (field_count + fields_per_u32 - 1) / fields_per_u32;
3903 } else 0;
3904 const payload_end = fields_start + field_count * max_field_size;
3905 try payload.resize(gpa, payload_end);
3906 return .{
3907 .payload = payload,
3908 .payload_top = payload_top,
3909 .field_bits_start = field_bits_start,
3910 .fields_start = fields_start,
3911 .fields_end = fields_start,
3912 };
3913 }
3914
3915 fn nextDecl(self: *Self, decl_inst: Zir.Inst.Index) void {
3916 self.payload.items[self.payload_top + self.decl_index] = @intFromEnum(decl_inst);
3917 self.decl_index += 1;
3918 }
3919
3920 fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void {
3921 const fields_per_u32 = 32 / bits_per_field;
3922 const index = self.field_bits_start + self.field_index / fields_per_u32;
3923 assert(index < self.fields_start);
3924 var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload.items[index];
3925 bit_bag >>= bits_per_field;
3926 comptime var i = 0;
3927 inline while (i < bits_per_field) : (i += 1) {
3928 bit_bag |= @as(u32, @intFromBool(bits[i])) << (32 - bits_per_field + i);
3929 }
3930 self.payload.items[index] = bit_bag;
3931 self.field_index += 1;
3932 }
3933
3934 fn appendToField(self: *Self, data: u32) void {
3935 assert(self.fields_end < self.payload.items.len);
3936 self.payload.items[self.fields_end] = data;
3937 self.fields_end += 1;
3938 }
3939
3940 fn finishBits(self: *Self, comptime bits_per_field: u32) void {
3941 if (bits_per_field > 0) {
3942 const fields_per_u32 = 32 / bits_per_field;
3943 const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32);
3944 if (self.field_index > 0 and empty_field_slots < fields_per_u32) {
3945 const index = self.field_bits_start + self.field_index / fields_per_u32;
3946 self.payload.items[index] >>= @intCast(empty_field_slots * bits_per_field);
3947 }
3948 }
3949 }
3950
3951 fn declsSlice(self: *Self) []u32 {
3952 return self.payload.items[self.payload_top..][0..self.decl_index];
3953 }
3954
3955 fn fieldsSlice(self: *Self) []u32 {
3956 return self.payload.items[self.field_bits_start..self.fields_end];
3957 }
3958
3959 fn deinit(self: *Self) void {
3960 self.payload.items.len = self.payload_top;
3961 }
3962};
3963
3964fn fnDecl(
3965 astgen: *AstGen,
3966 gz: *GenZir,
3967 scope: *Scope,
3968 wip_members: *WipMembers,
3969 decl_node: Ast.Node.Index,
3970 body_node: Ast.Node.Index,
3971 fn_proto: Ast.full.FnProto,
3972) InnerError!void {
3973 const tree = astgen.tree;
3974 const token_tags = tree.tokens.items(.tag);
3975
3976 // missing function name already happened in scanDecls()
3977 const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;
3978
3979 // We insert this at the beginning so that its instruction index marks the
3980 // start of the top level declaration.
3981 const decl_inst = try gz.makeBlockInst(.declaration, fn_proto.ast.proto_node);
3982 astgen.advanceSourceCursorToNode(decl_node);
3983
3984 var decl_gz: GenZir = .{
3985 .is_comptime = true,
3986 .decl_node_index = fn_proto.ast.proto_node,
3987 .decl_line = astgen.source_line,
3988 .parent = scope,
3989 .astgen = astgen,
3990 .instructions = gz.instructions,
3991 .instructions_top = gz.instructions.items.len,
3992 };
3993 defer decl_gz.unstack();
3994
3995 var fn_gz: GenZir = .{
3996 .is_comptime = false,
3997 .decl_node_index = fn_proto.ast.proto_node,
3998 .decl_line = decl_gz.decl_line,
3999 .parent = &decl_gz.base,
4000 .astgen = astgen,
4001 .instructions = gz.instructions,
4002 .instructions_top = GenZir.unstacked_top,
4003 };
4004 defer fn_gz.unstack();
4005
4006 const is_pub = fn_proto.visib_token != null;
4007 const is_export = blk: {
4008 const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;
4009 break :blk token_tags[maybe_export_token] == .keyword_export;
4010 };
4011 const is_extern = blk: {
4012 const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
4013 break :blk token_tags[maybe_extern_token] == .keyword_extern;
4014 };
4015 const has_inline_keyword = blk: {
4016 const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
4017 break :blk token_tags[maybe_inline_token] == .keyword_inline;
4018 };
4019 const is_noinline = blk: {
4020 const maybe_noinline_token = fn_proto.extern_export_inline_token orelse break :blk false;
4021 break :blk token_tags[maybe_noinline_token] == .keyword_noinline;
4022 };
4023
4024 const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken());
4025
4026 wip_members.nextDecl(decl_inst);
4027
4028 var noalias_bits: u32 = 0;
4029 var params_scope = &fn_gz.base;
4030 const is_var_args = is_var_args: {
4031 var param_type_i: usize = 0;
4032 var it = fn_proto.iterate(tree);
4033 while (it.next()) |param| : (param_type_i += 1) {
4034 const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
4035 .keyword_noalias => is_comptime: {
4036 noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
4037 return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
4038 break :is_comptime false;
4039 },
4040 .keyword_comptime => true,
4041 else => false,
4042 } else false;
4043
4044 const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
4045 switch (token_tags[token]) {
4046 .keyword_anytype => break :blk true,
4047 .ellipsis3 => break :is_var_args true,
4048 else => unreachable,
4049 }
4050 } else false;
4051
4052 const param_name: Zir.NullTerminatedString = if (param.name_token) |name_token| blk: {
4053 const name_bytes = tree.tokenSlice(name_token);
4054 if (mem.eql(u8, "_", name_bytes))
4055 break :blk .empty;
4056
4057 const param_name = try astgen.identAsString(name_token);
4058 if (!is_extern) {
4059 try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter");
4060 }
4061 break :blk param_name;
4062 } else if (!is_extern) {
4063 if (param.anytype_ellipsis3) |tok| {
4064 return astgen.failTok(tok, "missing parameter name", .{});
4065 } else {
4066 ambiguous: {
4067 if (tree.nodes.items(.tag)[param.type_expr] != .identifier) break :ambiguous;
4068 const main_token = tree.nodes.items(.main_token)[param.type_expr];
4069 const identifier_str = tree.tokenSlice(main_token);
4070 if (isPrimitive(identifier_str)) break :ambiguous;
4071 return astgen.failNodeNotes(
4072 param.type_expr,
4073 "missing parameter name or type",
4074 .{},
4075 &[_]u32{
4076 try astgen.errNoteNode(
4077 param.type_expr,
4078 "if this is a name, annotate its type '{s}: T'",
4079 .{identifier_str},
4080 ),
4081 try astgen.errNoteNode(
4082 param.type_expr,
4083 "if this is a type, give it a name '<name>: {s}'",
4084 .{identifier_str},
4085 ),
4086 },
4087 );
4088 }
4089 return astgen.failNode(param.type_expr, "missing parameter name", .{});
4090 }
4091 } else .empty;
4092
4093 const param_inst = if (is_anytype) param: {
4094 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
4095 const tag: Zir.Inst.Tag = if (is_comptime)
4096 .param_anytype_comptime
4097 else
4098 .param_anytype;
4099 break :param try decl_gz.addStrTok(tag, param_name, name_token);
4100 } else param: {
4101 const param_type_node = param.type_expr;
4102 assert(param_type_node != 0);
4103 var param_gz = decl_gz.makeSubBlock(scope);
4104 defer param_gz.unstack();
4105 const param_type = try expr(&param_gz, params_scope, coerced_type_ri, param_type_node);
4106 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
4107 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
4108
4109 const main_tokens = tree.nodes.items(.main_token);
4110 const name_token = param.name_token orelse main_tokens[param_type_node];
4111 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
4112 const param_inst = try decl_gz.addParam(&param_gz, tag, name_token, param_name, param.first_doc_comment);
4113 assert(param_inst_expected == param_inst);
4114 break :param param_inst.toRef();
4115 };
4116
4117 if (param_name == .empty or is_extern) continue;
4118
4119 const sub_scope = try astgen.arena.create(Scope.LocalVal);
4120 sub_scope.* = .{
4121 .parent = params_scope,
4122 .gen_zir = &decl_gz,
4123 .name = param_name,
4124 .inst = param_inst,
4125 .token_src = param.name_token.?,
4126 .id_cat = .@"function parameter",
4127 };
4128 params_scope = &sub_scope.base;
4129 }
4130 break :is_var_args false;
4131 };
4132
4133 const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: {
4134 const lib_name_str = try astgen.strLitAsString(lib_name_token);
4135 const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
4136 if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
4137 return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
4138 } else if (lib_name_str.len == 0) {
4139 return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
4140 }
4141 break :blk lib_name_str.index;
4142 } else .empty;
4143
4144 const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
4145 const is_inferred_error = token_tags[maybe_bang] == .bang;
4146
4147 // After creating the function ZIR instruction, it will need to update the break
4148 // instructions inside the expression blocks for align, addrspace, cc, and ret_ty
4149 // to use the function instruction as the "block" to break from.
4150
4151 var align_gz = decl_gz.makeSubBlock(params_scope);
4152 defer align_gz.unstack();
4153 const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
4154 const inst = try expr(&decl_gz, params_scope, coerced_align_ri, fn_proto.ast.align_expr);
4155 if (align_gz.instructionsSlice().len == 0) {
4156 // In this case we will send a len=0 body which can be encoded more efficiently.
4157 break :inst inst;
4158 }
4159 _ = try align_gz.addBreak(.break_inline, @enumFromInt(0), inst);
4160 break :inst inst;
4161 };
4162
4163 var addrspace_gz = decl_gz.makeSubBlock(params_scope);
4164 defer addrspace_gz.unstack();
4165 const addrspace_ref: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: {
4166 const inst = try expr(&decl_gz, params_scope, coerced_addrspace_ri, fn_proto.ast.addrspace_expr);
4167 if (addrspace_gz.instructionsSlice().len == 0) {
4168 // In this case we will send a len=0 body which can be encoded more efficiently.
4169 break :inst inst;
4170 }
4171 _ = try addrspace_gz.addBreak(.break_inline, @enumFromInt(0), inst);
4172 break :inst inst;
4173 };
4174
4175 var section_gz = decl_gz.makeSubBlock(params_scope);
4176 defer section_gz.unstack();
4177 const section_ref: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
4178 const inst = try expr(&decl_gz, params_scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, fn_proto.ast.section_expr);
4179 if (section_gz.instructionsSlice().len == 0) {
4180 // In this case we will send a len=0 body which can be encoded more efficiently.
4181 break :inst inst;
4182 }
4183 _ = try section_gz.addBreak(.break_inline, @enumFromInt(0), inst);
4184 break :inst inst;
4185 };
4186
4187 var cc_gz = decl_gz.makeSubBlock(params_scope);
4188 defer cc_gz.unstack();
4189 const cc_ref: Zir.Inst.Ref = blk: {
4190 if (fn_proto.ast.callconv_expr != 0) {
4191 if (has_inline_keyword) {
4192 return astgen.failNode(
4193 fn_proto.ast.callconv_expr,
4194 "explicit callconv incompatible with inline keyword",
4195 .{},
4196 );
4197 }
4198 const inst = try expr(
4199 &decl_gz,
4200 params_scope,
4201 .{ .rl = .{ .coerced_ty = .calling_convention_type } },
4202 fn_proto.ast.callconv_expr,
4203 );
4204 if (cc_gz.instructionsSlice().len == 0) {
4205 // In this case we will send a len=0 body which can be encoded more efficiently.
4206 break :blk inst;
4207 }
4208 _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
4209 break :blk inst;
4210 } else if (is_extern) {
4211 // note: https://github.com/ziglang/zig/issues/5269
4212 break :blk .calling_convention_c;
4213 } else if (has_inline_keyword) {
4214 break :blk .calling_convention_inline;
4215 } else {
4216 break :blk .none;
4217 }
4218 };
4219
4220 var ret_gz = decl_gz.makeSubBlock(params_scope);
4221 defer ret_gz.unstack();
4222 const ret_ref: Zir.Inst.Ref = inst: {
4223 const inst = try expr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type);
4224 if (ret_gz.instructionsSlice().len == 0) {
4225 // In this case we will send a len=0 body which can be encoded more efficiently.
4226 break :inst inst;
4227 }
4228 _ = try ret_gz.addBreak(.break_inline, @enumFromInt(0), inst);
4229 break :inst inst;
4230 };
4231
4232 const func_inst: Zir.Inst.Ref = if (body_node == 0) func: {
4233 if (!is_extern) {
4234 return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{});
4235 }
4236 if (is_inferred_error) {
4237 return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
4238 }
4239 break :func try decl_gz.addFunc(.{
4240 .src_node = decl_node,
4241 .cc_ref = cc_ref,
4242 .cc_gz = &cc_gz,
4243 .align_ref = align_ref,
4244 .align_gz = &align_gz,
4245 .ret_ref = ret_ref,
4246 .ret_gz = &ret_gz,
4247 .section_ref = section_ref,
4248 .section_gz = &section_gz,
4249 .addrspace_ref = addrspace_ref,
4250 .addrspace_gz = &addrspace_gz,
4251 .param_block = decl_inst,
4252 .body_gz = null,
4253 .lib_name = lib_name,
4254 .is_var_args = is_var_args,
4255 .is_inferred_error = false,
4256 .is_test = false,
4257 .is_extern = true,
4258 .is_noinline = is_noinline,
4259 .noalias_bits = noalias_bits,
4260 });
4261 } else func: {
4262 // as a scope, fn_gz encloses ret_gz, but for instruction list, fn_gz stacks on ret_gz
4263 fn_gz.instructions_top = ret_gz.instructions.items.len;
4264
4265 const prev_fn_block = astgen.fn_block;
4266 const prev_fn_ret_ty = astgen.fn_ret_ty;
4267 astgen.fn_block = &fn_gz;
4268 astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: {
4269 // We're essentially guaranteed to need the return type at some point,
4270 // since the return type is likely not `void` or `noreturn` so there
4271 // will probably be an explicit return requiring RLS. Fetch this
4272 // return type now so the rest of the function can use it.
4273 break :r try fn_gz.addNode(.ret_type, decl_node);
4274 } else ret_ref;
4275 defer {
4276 astgen.fn_block = prev_fn_block;
4277 astgen.fn_ret_ty = prev_fn_ret_ty;
4278 }
4279
4280 const prev_var_args = astgen.fn_var_args;
4281 astgen.fn_var_args = is_var_args;
4282 defer astgen.fn_var_args = prev_var_args;
4283
4284 astgen.advanceSourceCursorToNode(body_node);
4285 const lbrace_line = astgen.source_line - decl_gz.decl_line;
4286 const lbrace_column = astgen.source_column;
4287
4288 _ = try expr(&fn_gz, params_scope, .{ .rl = .none }, body_node);
4289 try checkUsed(gz, &fn_gz.base, params_scope);
4290
4291 if (!fn_gz.endsWithNoReturn()) {
4292 // As our last action before the return, "pop" the error trace if needed
4293 _ = try fn_gz.addRestoreErrRetIndex(.ret, .always, decl_node);
4294
4295 // Add implicit return at end of function.
4296 _ = try fn_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
4297 }
4298
4299 break :func try decl_gz.addFunc(.{
4300 .src_node = decl_node,
4301 .cc_ref = cc_ref,
4302 .cc_gz = &cc_gz,
4303 .align_ref = align_ref,
4304 .align_gz = &align_gz,
4305 .ret_ref = ret_ref,
4306 .ret_gz = &ret_gz,
4307 .section_ref = section_ref,
4308 .section_gz = &section_gz,
4309 .addrspace_ref = addrspace_ref,
4310 .addrspace_gz = &addrspace_gz,
4311 .lbrace_line = lbrace_line,
4312 .lbrace_column = lbrace_column,
4313 .param_block = decl_inst,
4314 .body_gz = &fn_gz,
4315 .lib_name = lib_name,
4316 .is_var_args = is_var_args,
4317 .is_inferred_error = is_inferred_error,
4318 .is_test = false,
4319 .is_extern = false,
4320 .is_noinline = is_noinline,
4321 .noalias_bits = noalias_bits,
4322 });
4323 };
4324
4325 // We add this at the end so that its instruction index marks the end range
4326 // of the top level declaration. addFunc already unstacked fn_gz and ret_gz.
4327 _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst);
4328
4329 try setDeclaration(
4330 decl_inst,
4331 std.zig.hashSrc(tree.getNodeSource(decl_node)),
4332 .{ .named = fn_name_token },
4333 decl_gz.decl_line - gz.decl_line,
4334 is_pub,
4335 is_export,
4336 doc_comment_index,
4337 &decl_gz,
4338 // align, linksection, and addrspace are passed in the func instruction in this case.
4339 // TODO: move them from the function instruction to the declaration instruction?
4340 null,
4341 );
4342}
4343
4344fn globalVarDecl(
4345 astgen: *AstGen,
4346 gz: *GenZir,
4347 scope: *Scope,
4348 wip_members: *WipMembers,
4349 node: Ast.Node.Index,
4350 var_decl: Ast.full.VarDecl,
4351) InnerError!void {
4352 const tree = astgen.tree;
4353 const token_tags = tree.tokens.items(.tag);
4354
4355 const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
4356 // We do this at the beginning so that the instruction index marks the range start
4357 // of the top level declaration.
4358 const decl_inst = try gz.makeBlockInst(.declaration, node);
4359
4360 const name_token = var_decl.ast.mut_token + 1;
4361 astgen.advanceSourceCursorToNode(node);
4362
4363 var block_scope: GenZir = .{
4364 .parent = scope,
4365 .decl_node_index = node,
4366 .decl_line = astgen.source_line,
4367 .astgen = astgen,
4368 .is_comptime = true,
4369 .anon_name_strategy = .parent,
4370 .instructions = gz.instructions,
4371 .instructions_top = gz.instructions.items.len,
4372 };
4373 defer block_scope.unstack();
4374
4375 const is_pub = var_decl.visib_token != null;
4376 const is_export = blk: {
4377 const maybe_export_token = var_decl.extern_export_token orelse break :blk false;
4378 break :blk token_tags[maybe_export_token] == .keyword_export;
4379 };
4380 const is_extern = blk: {
4381 const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
4382 break :blk token_tags[maybe_extern_token] == .keyword_extern;
4383 };
4384 wip_members.nextDecl(decl_inst);
4385
4386 const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
4387 if (!is_mutable) {
4388 return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});
4389 }
4390 break :blk true;
4391 } else false;
4392
4393 const lib_name = if (var_decl.lib_name) |lib_name_token| blk: {
4394 const lib_name_str = try astgen.strLitAsString(lib_name_token);
4395 const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
4396 if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
4397 return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
4398 } else if (lib_name_str.len == 0) {
4399 return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
4400 }
4401 break :blk lib_name_str.index;
4402 } else .empty;
4403
4404 const doc_comment_index = try astgen.docCommentAsString(var_decl.firstToken());
4405
4406 assert(var_decl.comptime_token == null); // handled by parser
4407
4408 const var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: {
4409 if (is_extern) {
4410 return astgen.failNode(
4411 var_decl.ast.init_node,
4412 "extern variables have no initializers",
4413 .{},
4414 );
4415 }
4416
4417 const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node != 0)
4418 try expr(
4419 &block_scope,
4420 &block_scope.base,
4421 coerced_type_ri,
4422 var_decl.ast.type_node,
4423 )
4424 else
4425 .none;
4426
4427 const init_inst = try expr(
4428 &block_scope,
4429 &block_scope.base,
4430 if (type_inst != .none) .{ .rl = .{ .ty = type_inst } } else .{ .rl = .none },
4431 var_decl.ast.init_node,
4432 );
4433
4434 if (is_mutable) {
4435 const var_inst = try block_scope.addVar(.{
4436 .var_type = type_inst,
4437 .lib_name = .empty,
4438 .align_inst = .none, // passed via the decls data
4439 .init = init_inst,
4440 .is_extern = false,
4441 .is_const = !is_mutable,
4442 .is_threadlocal = is_threadlocal,
4443 });
4444 break :vi var_inst;
4445 } else {
4446 break :vi init_inst;
4447 }
4448 } else if (!is_extern) {
4449 return astgen.failNode(node, "variables must be initialized", .{});
4450 } else if (var_decl.ast.type_node != 0) vi: {
4451 // Extern variable which has an explicit type.
4452 const type_inst = try typeExpr(&block_scope, &block_scope.base, var_decl.ast.type_node);
4453
4454 const var_inst = try block_scope.addVar(.{
4455 .var_type = type_inst,
4456 .lib_name = lib_name,
4457 .align_inst = .none, // passed via the decls data
4458 .init = .none,
4459 .is_extern = true,
4460 .is_const = !is_mutable,
4461 .is_threadlocal = is_threadlocal,
4462 });
4463 break :vi var_inst;
4464 } else {
4465 return astgen.failNode(node, "unable to infer variable type", .{});
4466 };
4467
4468 // We do this at the end so that the instruction index marks the end
4469 // range of a top level declaration.
4470 _ = try block_scope.addBreakWithSrcNode(.break_inline, decl_inst, var_inst, node);
4471
4472 var align_gz = block_scope.makeSubBlock(scope);
4473 if (var_decl.ast.align_node != 0) {
4474 const align_inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node);
4475 _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node);
4476 }
4477
4478 var linksection_gz = align_gz.makeSubBlock(scope);
4479 if (var_decl.ast.section_node != 0) {
4480 const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node);
4481 _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node);
4482 }
4483
4484 var addrspace_gz = linksection_gz.makeSubBlock(scope);
4485 if (var_decl.ast.addrspace_node != 0) {
4486 const addrspace_inst = try expr(&addrspace_gz, &addrspace_gz.base, coerced_addrspace_ri, var_decl.ast.addrspace_node);
4487 _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node);
4488 }
4489
4490 try setDeclaration(
4491 decl_inst,
4492 std.zig.hashSrc(tree.getNodeSource(node)),
4493 .{ .named = name_token },
4494 block_scope.decl_line - gz.decl_line,
4495 is_pub,
4496 is_export,
4497 doc_comment_index,
4498 &block_scope,
4499 .{
4500 .align_gz = &align_gz,
4501 .linksection_gz = &linksection_gz,
4502 .addrspace_gz = &addrspace_gz,
4503 },
4504 );
4505}
4506
4507fn comptimeDecl(
4508 astgen: *AstGen,
4509 gz: *GenZir,
4510 scope: *Scope,
4511 wip_members: *WipMembers,
4512 node: Ast.Node.Index,
4513) InnerError!void {
4514 const tree = astgen.tree;
4515 const node_datas = tree.nodes.items(.data);
4516 const body_node = node_datas[node].lhs;
4517
4518 // Up top so the ZIR instruction index marks the start range of this
4519 // top-level declaration.
4520 const decl_inst = try gz.makeBlockInst(.declaration, node);
4521 wip_members.nextDecl(decl_inst);
4522 astgen.advanceSourceCursorToNode(node);
4523
4524 var decl_block: GenZir = .{
4525 .is_comptime = true,
4526 .decl_node_index = node,
4527 .decl_line = astgen.source_line,
4528 .parent = scope,
4529 .astgen = astgen,
4530 .instructions = gz.instructions,
4531 .instructions_top = gz.instructions.items.len,
4532 };
4533 defer decl_block.unstack();
4534
4535 const block_result = try expr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node);
4536 if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) {
4537 _ = try decl_block.addBreak(.break_inline, decl_inst, .void_value);
4538 }
4539
4540 try setDeclaration(
4541 decl_inst,
4542 std.zig.hashSrc(tree.getNodeSource(node)),
4543 .@"comptime",
4544 decl_block.decl_line - gz.decl_line,
4545 false,
4546 false,
4547 .empty,
4548 &decl_block,
4549 null,
4550 );
4551}
4552
4553fn usingnamespaceDecl(
4554 astgen: *AstGen,
4555 gz: *GenZir,
4556 scope: *Scope,
4557 wip_members: *WipMembers,
4558 node: Ast.Node.Index,
4559) InnerError!void {
4560 const tree = astgen.tree;
4561 const node_datas = tree.nodes.items(.data);
4562
4563 const type_expr = node_datas[node].lhs;
4564 const is_pub = blk: {
4565 const main_tokens = tree.nodes.items(.main_token);
4566 const token_tags = tree.tokens.items(.tag);
4567 const main_token = main_tokens[node];
4568 break :blk (main_token > 0 and token_tags[main_token - 1] == .keyword_pub);
4569 };
4570 // Up top so the ZIR instruction index marks the start range of this
4571 // top-level declaration.
4572 const decl_inst = try gz.makeBlockInst(.declaration, node);
4573 wip_members.nextDecl(decl_inst);
4574 astgen.advanceSourceCursorToNode(node);
4575
4576 var decl_block: GenZir = .{
4577 .is_comptime = true,
4578 .decl_node_index = node,
4579 .decl_line = astgen.source_line,
4580 .parent = scope,
4581 .astgen = astgen,
4582 .instructions = gz.instructions,
4583 .instructions_top = gz.instructions.items.len,
4584 };
4585 defer decl_block.unstack();
4586
4587 const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr);
4588 _ = try decl_block.addBreak(.break_inline, decl_inst, namespace_inst);
4589
4590 try setDeclaration(
4591 decl_inst,
4592 std.zig.hashSrc(tree.getNodeSource(node)),
4593 .@"usingnamespace",
4594 decl_block.decl_line - gz.decl_line,
4595 is_pub,
4596 false,
4597 .empty,
4598 &decl_block,
4599 null,
4600 );
4601}
4602
4603fn testDecl(
4604 astgen: *AstGen,
4605 gz: *GenZir,
4606 scope: *Scope,
4607 wip_members: *WipMembers,
4608 node: Ast.Node.Index,
4609) InnerError!void {
4610 const tree = astgen.tree;
4611 const node_datas = tree.nodes.items(.data);
4612 const body_node = node_datas[node].rhs;
4613
4614 // Up top so the ZIR instruction index marks the start range of this
4615 // top-level declaration.
4616 const decl_inst = try gz.makeBlockInst(.declaration, node);
4617
4618 wip_members.nextDecl(decl_inst);
4619 astgen.advanceSourceCursorToNode(node);
4620
4621 var decl_block: GenZir = .{
4622 .is_comptime = true,
4623 .decl_node_index = node,
4624 .decl_line = astgen.source_line,
4625 .parent = scope,
4626 .astgen = astgen,
4627 .instructions = gz.instructions,
4628 .instructions_top = gz.instructions.items.len,
4629 };
4630 defer decl_block.unstack();
4631
4632 const main_tokens = tree.nodes.items(.main_token);
4633 const token_tags = tree.tokens.items(.tag);
4634 const test_token = main_tokens[node];
4635 const test_name_token = test_token + 1;
4636 const test_name: DeclarationName = switch (token_tags[test_name_token]) {
4637 else => .unnamed_test,
4638 .string_literal => .{ .named_test = test_name_token },
4639 .identifier => blk: {
4640 const ident_name_raw = tree.tokenSlice(test_name_token);
4641
4642 if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{});
4643
4644 // if not @"" syntax, just use raw token slice
4645 if (ident_name_raw[0] != '@') {
4646 if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{});
4647 }
4648
4649 // Local variables, including function parameters.
4650 const name_str_index = try astgen.identAsString(test_name_token);
4651 var s = scope;
4652 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
4653 var num_namespaces_out: u32 = 0;
4654 var capturing_namespace: ?*Scope.Namespace = null;
4655 while (true) switch (s.tag) {
4656 .local_val => {
4657 const local_val = s.cast(Scope.LocalVal).?;
4658 if (local_val.name == name_str_index) {
4659 local_val.used = test_name_token;
4660 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
4661 @tagName(local_val.id_cat),
4662 }, &[_]u32{
4663 try astgen.errNoteTok(local_val.token_src, "{s} declared here", .{
4664 @tagName(local_val.id_cat),
4665 }),
4666 });
4667 }
4668 s = local_val.parent;
4669 },
4670 .local_ptr => {
4671 const local_ptr = s.cast(Scope.LocalPtr).?;
4672 if (local_ptr.name == name_str_index) {
4673 local_ptr.used = test_name_token;
4674 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
4675 @tagName(local_ptr.id_cat),
4676 }, &[_]u32{
4677 try astgen.errNoteTok(local_ptr.token_src, "{s} declared here", .{
4678 @tagName(local_ptr.id_cat),
4679 }),
4680 });
4681 }
4682 s = local_ptr.parent;
4683 },
4684 .gen_zir => s = s.cast(GenZir).?.parent,
4685 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
4686 .namespace, .enum_namespace => {
4687 const ns = s.cast(Scope.Namespace).?;
4688 if (ns.decls.get(name_str_index)) |i| {
4689 if (found_already) |f| {
4690 return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{
4691 try astgen.errNoteNode(f, "declared here", .{}),
4692 try astgen.errNoteNode(i, "also declared here", .{}),
4693 });
4694 }
4695 // We found a match but must continue looking for ambiguous references to decls.
4696 found_already = i;
4697 }
4698 num_namespaces_out += 1;
4699 capturing_namespace = ns;
4700 s = ns.parent;
4701 },
4702 .top => break,
4703 };
4704 if (found_already == null) {
4705 const ident_name = try astgen.identifierTokenString(test_name_token);
4706 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
4707 }
4708
4709 break :blk .{ .decltest = name_str_index };
4710 },
4711 };
4712
4713 var fn_block: GenZir = .{
4714 .is_comptime = false,
4715 .decl_node_index = node,
4716 .decl_line = decl_block.decl_line,
4717 .parent = &decl_block.base,
4718 .astgen = astgen,
4719 .instructions = decl_block.instructions,
4720 .instructions_top = decl_block.instructions.items.len,
4721 };
4722 defer fn_block.unstack();
4723
4724 const prev_fn_block = astgen.fn_block;
4725 const prev_fn_ret_ty = astgen.fn_ret_ty;
4726 astgen.fn_block = &fn_block;
4727 astgen.fn_ret_ty = .anyerror_void_error_union_type;
4728 defer {
4729 astgen.fn_block = prev_fn_block;
4730 astgen.fn_ret_ty = prev_fn_ret_ty;
4731 }
4732
4733 astgen.advanceSourceCursorToNode(body_node);
4734 const lbrace_line = astgen.source_line - decl_block.decl_line;
4735 const lbrace_column = astgen.source_column;
4736
4737 const block_result = try expr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node);
4738 if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) {
4739
4740 // As our last action before the return, "pop" the error trace if needed
4741 _ = try fn_block.addRestoreErrRetIndex(.ret, .always, node);
4742
4743 // Add implicit return at end of function.
4744 _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
4745 }
4746
4747 const func_inst = try decl_block.addFunc(.{
4748 .src_node = node,
4749
4750 .cc_ref = .none,
4751 .cc_gz = null,
4752 .align_ref = .none,
4753 .align_gz = null,
4754 .ret_ref = .anyerror_void_error_union_type,
4755 .ret_gz = null,
4756 .section_ref = .none,
4757 .section_gz = null,
4758 .addrspace_ref = .none,
4759 .addrspace_gz = null,
4760
4761 .lbrace_line = lbrace_line,
4762 .lbrace_column = lbrace_column,
4763 .param_block = decl_inst,
4764 .body_gz = &fn_block,
4765 .lib_name = .empty,
4766 .is_var_args = false,
4767 .is_inferred_error = false,
4768 .is_test = true,
4769 .is_extern = false,
4770 .is_noinline = false,
4771 .noalias_bits = 0,
4772 });
4773
4774 _ = try decl_block.addBreak(.break_inline, decl_inst, func_inst);
4775
4776 try setDeclaration(
4777 decl_inst,
4778 std.zig.hashSrc(tree.getNodeSource(node)),
4779 test_name,
4780 decl_block.decl_line - gz.decl_line,
4781 false,
4782 false,
4783 .empty,
4784 &decl_block,
4785 null,
4786 );
4787}
4788
4789fn structDeclInner(
4790 gz: *GenZir,
4791 scope: *Scope,
4792 node: Ast.Node.Index,
4793 container_decl: Ast.full.ContainerDecl,
4794 layout: std.builtin.Type.ContainerLayout,
4795 backing_int_node: Ast.Node.Index,
4796) InnerError!Zir.Inst.Ref {
4797 const decl_inst = try gz.reserveInstructionIndex();
4798
4799 if (container_decl.ast.members.len == 0 and backing_int_node == 0) {
4800 try gz.setStruct(decl_inst, .{
4801 .src_node = node,
4802 .layout = layout,
4803 .fields_len = 0,
4804 .decls_len = 0,
4805 .backing_int_ref = .none,
4806 .backing_int_body_len = 0,
4807 .known_non_opv = false,
4808 .known_comptime_only = false,
4809 .is_tuple = false,
4810 .any_comptime_fields = false,
4811 .any_default_inits = false,
4812 .any_aligned_fields = false,
4813 .fields_hash = std.zig.hashSrc(@tagName(layout)),
4814 });
4815 return decl_inst.toRef();
4816 }
4817
4818 const astgen = gz.astgen;
4819 const gpa = astgen.gpa;
4820 const tree = astgen.tree;
4821
4822 var namespace: Scope.Namespace = .{
4823 .parent = scope,
4824 .node = node,
4825 .inst = decl_inst,
4826 .declaring_gz = gz,
4827 };
4828 defer namespace.deinit(gpa);
4829
4830 // The struct_decl instruction introduces a scope in which the decls of the struct
4831 // are in scope, so that field types, alignments, and default value expressions
4832 // can refer to decls within the struct itself.
4833 astgen.advanceSourceCursorToNode(node);
4834 var block_scope: GenZir = .{
4835 .parent = &namespace.base,
4836 .decl_node_index = node,
4837 .decl_line = gz.decl_line,
4838 .astgen = astgen,
4839 .is_comptime = true,
4840 .instructions = gz.instructions,
4841 .instructions_top = gz.instructions.items.len,
4842 };
4843 defer block_scope.unstack();
4844
4845 const scratch_top = astgen.scratch.items.len;
4846 defer astgen.scratch.items.len = scratch_top;
4847
4848 var backing_int_body_len: usize = 0;
4849 const backing_int_ref: Zir.Inst.Ref = blk: {
4850 if (backing_int_node != 0) {
4851 if (layout != .Packed) {
4852 return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{});
4853 } else {
4854 const backing_int_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node);
4855 if (!block_scope.isEmpty()) {
4856 if (!block_scope.endsWithNoReturn()) {
4857 _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref);
4858 }
4859
4860 const body = block_scope.instructionsSlice();
4861 const old_scratch_len = astgen.scratch.items.len;
4862 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
4863 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
4864 backing_int_body_len = astgen.scratch.items.len - old_scratch_len;
4865 block_scope.instructions.items.len = block_scope.instructions_top;
4866 }
4867 break :blk backing_int_ref;
4868 }
4869 } else {
4870 break :blk .none;
4871 }
4872 };
4873
4874 const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
4875 const field_count: u32 = @intCast(container_decl.ast.members.len - decl_count);
4876
4877 const bits_per_field = 4;
4878 const max_field_size = 5;
4879 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
4880 defer wip_members.deinit();
4881
4882 // We will use the scratch buffer, starting here, for the bodies:
4883 // bodies: { // for every fields_len
4884 // field_type_body_inst: Inst, // for each field_type_body_len
4885 // align_body_inst: Inst, // for each align_body_len
4886 // init_body_inst: Inst, // for each init_body_len
4887 // }
4888 // Note that the scratch buffer is simultaneously being used by WipMembers, however
4889 // it will not access any elements beyond this point in the ArrayList. It also
4890 // accesses via the ArrayList items field so it can handle the scratch buffer being
4891 // reallocated.
4892 // No defer needed here because it is handled by `wip_members.deinit()` above.
4893 const bodies_start = astgen.scratch.items.len;
4894
4895 const node_tags = tree.nodes.items(.tag);
4896 const is_tuple = for (container_decl.ast.members) |member_node| {
4897 const container_field = tree.fullContainerField(member_node) orelse continue;
4898 if (container_field.ast.tuple_like) break true;
4899 } else false;
4900
4901 if (is_tuple) switch (layout) {
4902 .Auto => {},
4903 .Extern => return astgen.failNode(node, "extern tuples are not supported", .{}),
4904 .Packed => return astgen.failNode(node, "packed tuples are not supported", .{}),
4905 };
4906
4907 if (is_tuple) for (container_decl.ast.members) |member_node| {
4908 switch (node_tags[member_node]) {
4909 .container_field_init,
4910 .container_field_align,
4911 .container_field,
4912 .@"comptime",
4913 .test_decl,
4914 => continue,
4915 else => {
4916 const tuple_member = for (container_decl.ast.members) |maybe_tuple| switch (node_tags[maybe_tuple]) {
4917 .container_field_init,
4918 .container_field_align,
4919 .container_field,
4920 => break maybe_tuple,
4921 else => {},
4922 } else unreachable;
4923 return astgen.failNodeNotes(
4924 member_node,
4925 "tuple declarations cannot contain declarations",
4926 .{},
4927 &[_]u32{
4928 try astgen.errNoteNode(tuple_member, "tuple field here", .{}),
4929 },
4930 );
4931 },
4932 }
4933 };
4934
4935 var fields_hasher = std.zig.SrcHasher.init(.{});
4936 fields_hasher.update(@tagName(layout));
4937 if (backing_int_node != 0) {
4938 fields_hasher.update(tree.getNodeSource(backing_int_node));
4939 }
4940
4941 var sfba = std.heap.stackFallback(256, astgen.arena);
4942 const sfba_allocator = sfba.get();
4943
4944 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
4945 try duplicate_names.ensureTotalCapacity(field_count);
4946
4947 // When there aren't errors, use this to avoid a second iteration.
4948 var any_duplicate = false;
4949
4950 var known_non_opv = false;
4951 var known_comptime_only = false;
4952 var any_comptime_fields = false;
4953 var any_aligned_fields = false;
4954 var any_default_inits = false;
4955 for (container_decl.ast.members) |member_node| {
4956 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
4957 .decl => continue,
4958 .field => |field| field,
4959 };
4960
4961 fields_hasher.update(tree.getNodeSource(member_node));
4962
4963 if (!is_tuple) {
4964 const field_name = try astgen.identAsString(member.ast.main_token);
4965
4966 member.convertToNonTupleLike(astgen.tree.nodes);
4967 assert(!member.ast.tuple_like);
4968
4969 wip_members.appendToField(@intFromEnum(field_name));
4970
4971 const gop = try duplicate_names.getOrPut(field_name);
4972
4973 if (gop.found_existing) {
4974 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
4975 any_duplicate = true;
4976 } else {
4977 gop.value_ptr.* = .{};
4978 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
4979 }
4980 } else if (!member.ast.tuple_like) {
4981 return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
4982 }
4983
4984 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
4985 wip_members.appendToField(@intFromEnum(doc_comment_index));
4986
4987 if (member.ast.type_expr == 0) {
4988 return astgen.failTok(member.ast.main_token, "struct field missing type", .{});
4989 }
4990
4991 const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
4992 const have_type_body = !block_scope.isEmpty();
4993 const have_align = member.ast.align_expr != 0;
4994 const have_value = member.ast.value_expr != 0;
4995 const is_comptime = member.comptime_token != null;
4996
4997 if (is_comptime) {
4998 switch (layout) {
4999 .Packed => return astgen.failTok(member.comptime_token.?, "packed struct fields cannot be marked comptime", .{}),
5000 .Extern => return astgen.failTok(member.comptime_token.?, "extern struct fields cannot be marked comptime", .{}),
5001 .Auto => any_comptime_fields = true,
5002 }
5003 } else {
5004 known_non_opv = known_non_opv or
5005 nodeImpliesMoreThanOnePossibleValue(tree, member.ast.type_expr);
5006 known_comptime_only = known_comptime_only or
5007 nodeImpliesComptimeOnly(tree, member.ast.type_expr);
5008 }
5009 wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, have_type_body });
5010
5011 if (have_type_body) {
5012 if (!block_scope.endsWithNoReturn()) {
5013 _ = try block_scope.addBreak(.break_inline, decl_inst, field_type);
5014 }
5015 const body = block_scope.instructionsSlice();
5016 const old_scratch_len = astgen.scratch.items.len;
5017 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
5018 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
5019 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
5020 block_scope.instructions.items.len = block_scope.instructions_top;
5021 } else {
5022 wip_members.appendToField(@intFromEnum(field_type));
5023 }
5024
5025 if (have_align) {
5026 if (layout == .Packed) {
5027 try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
5028 }
5029 any_aligned_fields = true;
5030 const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr);
5031 if (!block_scope.endsWithNoReturn()) {
5032 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref);
5033 }
5034 const body = block_scope.instructionsSlice();
5035 const old_scratch_len = astgen.scratch.items.len;
5036 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
5037 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
5038 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
5039 block_scope.instructions.items.len = block_scope.instructions_top;
5040 }
5041
5042 if (have_value) {
5043 any_default_inits = true;
5044
5045 // The decl_inst is used as here so that we can easily reconstruct a mapping
5046 // between it and the field type when the fields inits are analzyed.
5047 const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = decl_inst.toRef() } };
5048
5049 const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr);
5050 if (!block_scope.endsWithNoReturn()) {
5051 _ = try block_scope.addBreak(.break_inline, decl_inst, default_inst);
5052 }
5053 const body = block_scope.instructionsSlice();
5054 const old_scratch_len = astgen.scratch.items.len;
5055 try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
5056 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
5057 wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
5058 block_scope.instructions.items.len = block_scope.instructions_top;
5059 } else if (member.comptime_token) |comptime_token| {
5060 return astgen.failTok(comptime_token, "comptime field without default initialization value", .{});
5061 }
5062 }
5063
5064 if (any_duplicate) {
5065 var it = duplicate_names.iterator();
5066
5067 while (it.next()) |entry| {
5068 const record = entry.value_ptr.*;
5069 if (record.items.len > 1) {
5070 var error_notes = std.ArrayList(u32).init(astgen.arena);
5071
5072 for (record.items[1..]) |duplicate| {
5073 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate field here", .{}));
5074 }
5075
5076 try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));
5077
5078 try astgen.appendErrorTokNotes(
5079 record.items[0],
5080 "duplicate struct field name",
5081 .{},
5082 error_notes.items,
5083 );
5084 }
5085 }
5086
5087 return error.AnalysisFail;
5088 }
5089
5090 var fields_hash: std.zig.SrcHash = undefined;
5091 fields_hasher.final(&fields_hash);
5092
5093 try gz.setStruct(decl_inst, .{
5094 .src_node = node,
5095 .layout = layout,
5096 .fields_len = field_count,
5097 .decls_len = decl_count,
5098 .backing_int_ref = backing_int_ref,
5099 .backing_int_body_len = @intCast(backing_int_body_len),
5100 .known_non_opv = known_non_opv,
5101 .known_comptime_only = known_comptime_only,
5102 .is_tuple = is_tuple,
5103 .any_comptime_fields = any_comptime_fields,
5104 .any_default_inits = any_default_inits,
5105 .any_aligned_fields = any_aligned_fields,
5106 .fields_hash = fields_hash,
5107 });
5108
5109 wip_members.finishBits(bits_per_field);
5110 const decls_slice = wip_members.declsSlice();
5111 const fields_slice = wip_members.fieldsSlice();
5112 const bodies_slice = astgen.scratch.items[bodies_start..];
5113 try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len +
5114 decls_slice.len + fields_slice.len + bodies_slice.len);
5115 astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]);
5116 astgen.extra.appendSliceAssumeCapacity(decls_slice);
5117 astgen.extra.appendSliceAssumeCapacity(fields_slice);
5118 astgen.extra.appendSliceAssumeCapacity(bodies_slice);
5119
5120 block_scope.unstack();
5121 try gz.addNamespaceCaptures(&namespace);
5122 return decl_inst.toRef();
5123}
5124
5125fn unionDeclInner(
5126 gz: *GenZir,
5127 scope: *Scope,
5128 node: Ast.Node.Index,
5129 members: []const Ast.Node.Index,
5130 layout: std.builtin.Type.ContainerLayout,
5131 arg_node: Ast.Node.Index,
5132 auto_enum_tok: ?Ast.TokenIndex,
5133) InnerError!Zir.Inst.Ref {
5134 const decl_inst = try gz.reserveInstructionIndex();
5135
5136 const astgen = gz.astgen;
5137 const gpa = astgen.gpa;
5138
5139 var namespace: Scope.Namespace = .{
5140 .parent = scope,
5141 .node = node,
5142 .inst = decl_inst,
5143 .declaring_gz = gz,
5144 };
5145 defer namespace.deinit(gpa);
5146
5147 // The union_decl instruction introduces a scope in which the decls of the union
5148 // are in scope, so that field types, alignments, and default value expressions
5149 // can refer to decls within the union itself.
5150 astgen.advanceSourceCursorToNode(node);
5151 var block_scope: GenZir = .{
5152 .parent = &namespace.base,
5153 .decl_node_index = node,
5154 .decl_line = gz.decl_line,
5155 .astgen = astgen,
5156 .is_comptime = true,
5157 .instructions = gz.instructions,
5158 .instructions_top = gz.instructions.items.len,
5159 };
5160 defer block_scope.unstack();
5161
5162 const decl_count = try astgen.scanDecls(&namespace, members);
5163 const field_count: u32 = @intCast(members.len - decl_count);
5164
5165 if (layout != .Auto and (auto_enum_tok != null or arg_node != 0)) {
5166 const layout_str = if (layout == .Extern) "extern" else "packed";
5167 if (arg_node != 0) {
5168 return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{layout_str});
5169 } else {
5170 return astgen.failTok(auto_enum_tok.?, "{s} union does not support enum tag type", .{layout_str});
5171 }
5172 }
5173
5174 const arg_inst: Zir.Inst.Ref = if (arg_node != 0)
5175 try typeExpr(&block_scope, &namespace.base, arg_node)
5176 else
5177 .none;
5178
5179 const bits_per_field = 4;
5180 const max_field_size = 5;
5181 var any_aligned_fields = false;
5182 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
5183 defer wip_members.deinit();
5184
5185 var fields_hasher = std.zig.SrcHasher.init(.{});
5186 fields_hasher.update(@tagName(layout));
5187 fields_hasher.update(&.{@intFromBool(auto_enum_tok != null)});
5188 if (arg_node != 0) {
5189 fields_hasher.update(astgen.tree.getNodeSource(arg_node));
5190 }
5191
5192 var sfba = std.heap.stackFallback(256, astgen.arena);
5193 const sfba_allocator = sfba.get();
5194
5195 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
5196 try duplicate_names.ensureTotalCapacity(field_count);
5197
5198 // When there aren't errors, use this to avoid a second iteration.
5199 var any_duplicate = false;
5200
5201 for (members) |member_node| {
5202 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
5203 .decl => continue,
5204 .field => |field| field,
5205 };
5206 fields_hasher.update(astgen.tree.getNodeSource(member_node));
5207 member.convertToNonTupleLike(astgen.tree.nodes);
5208 if (member.ast.tuple_like) {
5209 return astgen.failTok(member.ast.main_token, "union field missing name", .{});
5210 }
5211 if (member.comptime_token) |comptime_token| {
5212 return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});
5213 }
5214
5215 const field_name = try astgen.identAsString(member.ast.main_token);
5216 wip_members.appendToField(@intFromEnum(field_name));
5217
5218 const gop = try duplicate_names.getOrPut(field_name);
5219
5220 if (gop.found_existing) {
5221 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5222 any_duplicate = true;
5223 } else {
5224 gop.value_ptr.* = .{};
5225 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5226 }
5227
5228 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
5229 wip_members.appendToField(@intFromEnum(doc_comment_index));
5230
5231 const have_type = member.ast.type_expr != 0;
5232 const have_align = member.ast.align_expr != 0;
5233 const have_value = member.ast.value_expr != 0;
5234 const unused = false;
5235 wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused });
5236
5237 if (have_type) {
5238 const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
5239 wip_members.appendToField(@intFromEnum(field_type));
5240 } else if (arg_inst == .none and auto_enum_tok == null) {
5241 return astgen.failNode(member_node, "union field missing type", .{});
5242 }
5243 if (have_align) {
5244 const align_inst = try expr(&block_scope, &block_scope.base, coerced_align_ri, member.ast.align_expr);
5245 wip_members.appendToField(@intFromEnum(align_inst));
5246 any_aligned_fields = true;
5247 }
5248 if (have_value) {
5249 if (arg_inst == .none) {
5250 return astgen.failNodeNotes(
5251 node,
5252 "explicitly valued tagged union missing integer tag type",
5253 .{},
5254 &[_]u32{
5255 try astgen.errNoteNode(
5256 member.ast.value_expr,
5257 "tag value specified here",
5258 .{},
5259 ),
5260 },
5261 );
5262 }
5263 if (auto_enum_tok == null) {
5264 return astgen.failNodeNotes(
5265 node,
5266 "explicitly valued tagged union requires inferred enum tag type",
5267 .{},
5268 &[_]u32{
5269 try astgen.errNoteNode(
5270 member.ast.value_expr,
5271 "tag value specified here",
5272 .{},
5273 ),
5274 },
5275 );
5276 }
5277 const tag_value = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr);
5278 wip_members.appendToField(@intFromEnum(tag_value));
5279 }
5280 }
5281
5282 if (any_duplicate) {
5283 var it = duplicate_names.iterator();
5284
5285 while (it.next()) |entry| {
5286 const record = entry.value_ptr.*;
5287 if (record.items.len > 1) {
5288 var error_notes = std.ArrayList(u32).init(astgen.arena);
5289
5290 for (record.items[1..]) |duplicate| {
5291 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate field here", .{}));
5292 }
5293
5294 try error_notes.append(try astgen.errNoteNode(node, "union declared here", .{}));
5295
5296 try astgen.appendErrorTokNotes(
5297 record.items[0],
5298 "duplicate union field name",
5299 .{},
5300 error_notes.items,
5301 );
5302 }
5303 }
5304
5305 return error.AnalysisFail;
5306 }
5307
5308 var fields_hash: std.zig.SrcHash = undefined;
5309 fields_hasher.final(&fields_hash);
5310
5311 if (!block_scope.isEmpty()) {
5312 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
5313 }
5314
5315 const body = block_scope.instructionsSlice();
5316 const body_len = astgen.countBodyLenAfterFixups(body);
5317
5318 try gz.setUnion(decl_inst, .{
5319 .src_node = node,
5320 .layout = layout,
5321 .tag_type = arg_inst,
5322 .body_len = body_len,
5323 .fields_len = field_count,
5324 .decls_len = decl_count,
5325 .auto_enum_tag = auto_enum_tok != null,
5326 .any_aligned_fields = any_aligned_fields,
5327 .fields_hash = fields_hash,
5328 });
5329
5330 wip_members.finishBits(bits_per_field);
5331 const decls_slice = wip_members.declsSlice();
5332 const fields_slice = wip_members.fieldsSlice();
5333 try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len);
5334 astgen.extra.appendSliceAssumeCapacity(decls_slice);
5335 astgen.appendBodyWithFixups(body);
5336 astgen.extra.appendSliceAssumeCapacity(fields_slice);
5337
5338 block_scope.unstack();
5339 try gz.addNamespaceCaptures(&namespace);
5340 return decl_inst.toRef();
5341}
5342
5343fn containerDecl(
5344 gz: *GenZir,
5345 scope: *Scope,
5346 ri: ResultInfo,
5347 node: Ast.Node.Index,
5348 container_decl: Ast.full.ContainerDecl,
5349) InnerError!Zir.Inst.Ref {
5350 const astgen = gz.astgen;
5351 const gpa = astgen.gpa;
5352 const tree = astgen.tree;
5353 const token_tags = tree.tokens.items(.tag);
5354
5355 const prev_fn_block = astgen.fn_block;
5356 astgen.fn_block = null;
5357 defer astgen.fn_block = prev_fn_block;
5358
5359 // We must not create any types until Sema. Here the goal is only to generate
5360 // ZIR for all the field types, alignments, and default value expressions.
5361
5362 switch (token_tags[container_decl.ast.main_token]) {
5363 .keyword_struct => {
5364 const layout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
5365 .keyword_packed => std.builtin.Type.ContainerLayout.Packed,
5366 .keyword_extern => std.builtin.Type.ContainerLayout.Extern,
5367 else => unreachable,
5368 } else std.builtin.Type.ContainerLayout.Auto;
5369
5370 const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg);
5371 return rvalue(gz, ri, result, node);
5372 },
5373 .keyword_union => {
5374 const layout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
5375 .keyword_packed => std.builtin.Type.ContainerLayout.Packed,
5376 .keyword_extern => std.builtin.Type.ContainerLayout.Extern,
5377 else => unreachable,
5378 } else std.builtin.Type.ContainerLayout.Auto;
5379
5380 const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token);
5381 return rvalue(gz, ri, result, node);
5382 },
5383 .keyword_enum => {
5384 if (container_decl.layout_token) |t| {
5385 return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{});
5386 }
5387 // Count total fields as well as how many have explicitly provided tag values.
5388 const counts = blk: {
5389 var values: usize = 0;
5390 var total_fields: usize = 0;
5391 var decls: usize = 0;
5392 var nonexhaustive_node: Ast.Node.Index = 0;
5393 var nonfinal_nonexhaustive = false;
5394 for (container_decl.ast.members) |member_node| {
5395 var member = tree.fullContainerField(member_node) orelse {
5396 decls += 1;
5397 continue;
5398 };
5399 member.convertToNonTupleLike(astgen.tree.nodes);
5400 if (member.ast.tuple_like) {
5401 return astgen.failTok(member.ast.main_token, "enum field missing name", .{});
5402 }
5403 if (member.comptime_token) |comptime_token| {
5404 return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});
5405 }
5406 if (member.ast.type_expr != 0) {
5407 return astgen.failNodeNotes(
5408 member.ast.type_expr,
5409 "enum fields do not have types",
5410 .{},
5411 &[_]u32{
5412 try astgen.errNoteNode(
5413 node,
5414 "consider 'union(enum)' here to make it a tagged union",
5415 .{},
5416 ),
5417 },
5418 );
5419 }
5420 if (member.ast.align_expr != 0) {
5421 return astgen.failNode(member.ast.align_expr, "enum fields cannot be aligned", .{});
5422 }
5423
5424 const name_token = member.ast.main_token;
5425 if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
5426 if (nonexhaustive_node != 0) {
5427 return astgen.failNodeNotes(
5428 member_node,
5429 "redundant non-exhaustive enum mark",
5430 .{},
5431 &[_]u32{
5432 try astgen.errNoteNode(
5433 nonexhaustive_node,
5434 "other mark here",
5435 .{},
5436 ),
5437 },
5438 );
5439 }
5440 nonexhaustive_node = member_node;
5441 if (member.ast.value_expr != 0) {
5442 return astgen.failNode(member.ast.value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{});
5443 }
5444 continue;
5445 } else if (nonexhaustive_node != 0) {
5446 nonfinal_nonexhaustive = true;
5447 }
5448 total_fields += 1;
5449 if (member.ast.value_expr != 0) {
5450 if (container_decl.ast.arg == 0) {
5451 return astgen.failNode(member.ast.value_expr, "value assigned to enum tag with inferred tag type", .{});
5452 }
5453 values += 1;
5454 }
5455 }
5456 if (nonfinal_nonexhaustive) {
5457 return astgen.failNode(nonexhaustive_node, "'_' field of non-exhaustive enum must be last", .{});
5458 }
5459 break :blk .{
5460 .total_fields = total_fields,
5461 .values = values,
5462 .decls = decls,
5463 .nonexhaustive_node = nonexhaustive_node,
5464 };
5465 };
5466 if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) {
5467 try astgen.appendErrorNodeNotes(
5468 node,
5469 "non-exhaustive enum missing integer tag type",
5470 .{},
5471 &[_]u32{
5472 try astgen.errNoteNode(
5473 counts.nonexhaustive_node,
5474 "marked non-exhaustive here",
5475 .{},
5476 ),
5477 },
5478 );
5479 }
5480 // In this case we must generate ZIR code for the tag values, similar to
5481 // how structs are handled above.
5482 const nonexhaustive = counts.nonexhaustive_node != 0;
5483
5484 const decl_inst = try gz.reserveInstructionIndex();
5485
5486 var namespace: Scope.Namespace = .{
5487 .parent = scope,
5488 .node = node,
5489 .inst = decl_inst,
5490 .declaring_gz = gz,
5491 };
5492 defer namespace.deinit(gpa);
5493
5494 // The enum_decl instruction introduces a scope in which the decls of the enum
5495 // are in scope, so that tag values can refer to decls within the enum itself.
5496 astgen.advanceSourceCursorToNode(node);
5497 var block_scope: GenZir = .{
5498 .parent = &namespace.base,
5499 .decl_node_index = node,
5500 .decl_line = gz.decl_line,
5501 .astgen = astgen,
5502 .is_comptime = true,
5503 .instructions = gz.instructions,
5504 .instructions_top = gz.instructions.items.len,
5505 };
5506 defer block_scope.unstack();
5507
5508 _ = try astgen.scanDecls(&namespace, container_decl.ast.members);
5509 namespace.base.tag = .enum_namespace;
5510
5511 const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0)
5512 try comptimeExpr(&block_scope, &namespace.base, coerced_type_ri, container_decl.ast.arg)
5513 else
5514 .none;
5515
5516 const bits_per_field = 1;
5517 const max_field_size = 3;
5518 var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size);
5519 defer wip_members.deinit();
5520
5521 var fields_hasher = std.zig.SrcHasher.init(.{});
5522 if (container_decl.ast.arg != 0) {
5523 fields_hasher.update(tree.getNodeSource(container_decl.ast.arg));
5524 }
5525 fields_hasher.update(&.{@intFromBool(nonexhaustive)});
5526
5527 var sfba = std.heap.stackFallback(256, astgen.arena);
5528 const sfba_allocator = sfba.get();
5529
5530 var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, std.ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
5531 try duplicate_names.ensureTotalCapacity(counts.total_fields);
5532
5533 // When there aren't errors, use this to avoid a second iteration.
5534 var any_duplicate = false;
5535
5536 for (container_decl.ast.members) |member_node| {
5537 if (member_node == counts.nonexhaustive_node)
5538 continue;
5539 fields_hasher.update(tree.getNodeSource(member_node));
5540 namespace.base.tag = .namespace;
5541 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
5542 .decl => continue,
5543 .field => |field| field,
5544 };
5545 member.convertToNonTupleLike(astgen.tree.nodes);
5546 assert(member.comptime_token == null);
5547 assert(member.ast.type_expr == 0);
5548 assert(member.ast.align_expr == 0);
5549
5550 const field_name = try astgen.identAsString(member.ast.main_token);
5551 wip_members.appendToField(@intFromEnum(field_name));
5552
5553 const gop = try duplicate_names.getOrPut(field_name);
5554
5555 if (gop.found_existing) {
5556 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5557 any_duplicate = true;
5558 } else {
5559 gop.value_ptr.* = .{};
5560 try gop.value_ptr.append(sfba_allocator, member.ast.main_token);
5561 }
5562
5563 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
5564 wip_members.appendToField(@intFromEnum(doc_comment_index));
5565
5566 const have_value = member.ast.value_expr != 0;
5567 wip_members.nextField(bits_per_field, .{have_value});
5568
5569 if (have_value) {
5570 if (arg_inst == .none) {
5571 return astgen.failNodeNotes(
5572 node,
5573 "explicitly valued enum missing integer tag type",
5574 .{},
5575 &[_]u32{
5576 try astgen.errNoteNode(
5577 member.ast.value_expr,
5578 "tag value specified here",
5579 .{},
5580 ),
5581 },
5582 );
5583 }
5584 namespace.base.tag = .enum_namespace;
5585 const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr);
5586 wip_members.appendToField(@intFromEnum(tag_value_inst));
5587 }
5588 }
5589
5590 if (any_duplicate) {
5591 var it = duplicate_names.iterator();
5592
5593 while (it.next()) |entry| {
5594 const record = entry.value_ptr.*;
5595 if (record.items.len > 1) {
5596 var error_notes = std.ArrayList(u32).init(astgen.arena);
5597
5598 for (record.items[1..]) |duplicate| {
5599 try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate field here", .{}));
5600 }
5601
5602 try error_notes.append(try astgen.errNoteNode(node, "enum declared here", .{}));
5603
5604 try astgen.appendErrorTokNotes(
5605 record.items[0],
5606 "duplicate enum field name",
5607 .{},
5608 error_notes.items,
5609 );
5610 }
5611 }
5612
5613 return error.AnalysisFail;
5614 }
5615
5616 if (!block_scope.isEmpty()) {
5617 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
5618 }
5619
5620 var fields_hash: std.zig.SrcHash = undefined;
5621 fields_hasher.final(&fields_hash);
5622
5623 const body = block_scope.instructionsSlice();
5624 const body_len = astgen.countBodyLenAfterFixups(body);
5625
5626 try gz.setEnum(decl_inst, .{
5627 .src_node = node,
5628 .nonexhaustive = nonexhaustive,
5629 .tag_type = arg_inst,
5630 .body_len = body_len,
5631 .fields_len = @intCast(counts.total_fields),
5632 .decls_len = @intCast(counts.decls),
5633 .fields_hash = fields_hash,
5634 });
5635
5636 wip_members.finishBits(bits_per_field);
5637 const decls_slice = wip_members.declsSlice();
5638 const fields_slice = wip_members.fieldsSlice();
5639 try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len);
5640 astgen.extra.appendSliceAssumeCapacity(decls_slice);
5641 astgen.appendBodyWithFixups(body);
5642 astgen.extra.appendSliceAssumeCapacity(fields_slice);
5643
5644 block_scope.unstack();
5645 try gz.addNamespaceCaptures(&namespace);
5646 return rvalue(gz, ri, decl_inst.toRef(), node);
5647 },
5648 .keyword_opaque => {
5649 assert(container_decl.ast.arg == 0);
5650
5651 const decl_inst = try gz.reserveInstructionIndex();
5652
5653 var namespace: Scope.Namespace = .{
5654 .parent = scope,
5655 .node = node,
5656 .inst = decl_inst,
5657 .declaring_gz = gz,
5658 };
5659 defer namespace.deinit(gpa);
5660
5661 astgen.advanceSourceCursorToNode(node);
5662 var block_scope: GenZir = .{
5663 .parent = &namespace.base,
5664 .decl_node_index = node,
5665 .decl_line = gz.decl_line,
5666 .astgen = astgen,
5667 .is_comptime = true,
5668 .instructions = gz.instructions,
5669 .instructions_top = gz.instructions.items.len,
5670 };
5671 defer block_scope.unstack();
5672
5673 const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
5674
5675 var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0);
5676 defer wip_members.deinit();
5677
5678 for (container_decl.ast.members) |member_node| {
5679 const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node);
5680 if (res == .field) {
5681 return astgen.failNode(member_node, "opaque types cannot have fields", .{});
5682 }
5683 }
5684
5685 try gz.setOpaque(decl_inst, .{
5686 .src_node = node,
5687 .decls_len = decl_count,
5688 });
5689
5690 wip_members.finishBits(0);
5691 const decls_slice = wip_members.declsSlice();
5692 try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len);
5693 astgen.extra.appendSliceAssumeCapacity(decls_slice);
5694
5695 block_scope.unstack();
5696 try gz.addNamespaceCaptures(&namespace);
5697 return rvalue(gz, ri, decl_inst.toRef(), node);
5698 },
5699 else => unreachable,
5700 }
5701}
5702
5703const ContainerMemberResult = union(enum) { decl, field: Ast.full.ContainerField };
5704
5705fn containerMember(
5706 gz: *GenZir,
5707 scope: *Scope,
5708 wip_members: *WipMembers,
5709 member_node: Ast.Node.Index,
5710) InnerError!ContainerMemberResult {
5711 const astgen = gz.astgen;
5712 const tree = astgen.tree;
5713 const node_tags = tree.nodes.items(.tag);
5714 const node_datas = tree.nodes.items(.data);
5715 switch (node_tags[member_node]) {
5716 .container_field_init,
5717 .container_field_align,
5718 .container_field,
5719 => return ContainerMemberResult{ .field = tree.fullContainerField(member_node).? },
5720
5721 .fn_proto,
5722 .fn_proto_multi,
5723 .fn_proto_one,
5724 .fn_proto_simple,
5725 .fn_decl,
5726 => {
5727 var buf: [1]Ast.Node.Index = undefined;
5728 const full = tree.fullFnProto(&buf, member_node).?;
5729 const body = if (node_tags[member_node] == .fn_decl) node_datas[member_node].rhs else 0;
5730
5731 astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) {
5732 error.OutOfMemory => return error.OutOfMemory,
5733 error.AnalysisFail => {},
5734 };
5735 },
5736
5737 .global_var_decl,
5738 .local_var_decl,
5739 .simple_var_decl,
5740 .aligned_var_decl,
5741 => {
5742 astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.fullVarDecl(member_node).?) catch |err| switch (err) {
5743 error.OutOfMemory => return error.OutOfMemory,
5744 error.AnalysisFail => {},
5745 };
5746 },
5747
5748 .@"comptime" => {
5749 astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
5750 error.OutOfMemory => return error.OutOfMemory,
5751 error.AnalysisFail => {},
5752 };
5753 },
5754 .@"usingnamespace" => {
5755 astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
5756 error.OutOfMemory => return error.OutOfMemory,
5757 error.AnalysisFail => {},
5758 };
5759 },
5760 .test_decl => {
5761 astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
5762 error.OutOfMemory => return error.OutOfMemory,
5763 error.AnalysisFail => {},
5764 };
5765 },
5766 else => unreachable,
5767 }
5768 return .decl;
5769}
5770
5771fn errorSetDecl(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
5772 const astgen = gz.astgen;
5773 const gpa = astgen.gpa;
5774 const tree = astgen.tree;
5775 const main_tokens = tree.nodes.items(.main_token);
5776 const token_tags = tree.tokens.items(.tag);
5777
5778 const payload_index = try reserveExtra(astgen, @typeInfo(Zir.Inst.ErrorSetDecl).Struct.fields.len);
5779 var fields_len: usize = 0;
5780 {
5781 var idents: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .{};
5782 defer idents.deinit(gpa);
5783
5784 const error_token = main_tokens[node];
5785 var tok_i = error_token + 2;
5786 while (true) : (tok_i += 1) {
5787 switch (token_tags[tok_i]) {
5788 .doc_comment, .comma => {},
5789 .identifier => {
5790 const str_index = try astgen.identAsString(tok_i);
5791 const gop = try idents.getOrPut(gpa, str_index);
5792 if (gop.found_existing) {
5793 const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(str_index)));
5794 defer gpa.free(name);
5795 return astgen.failTokNotes(
5796 tok_i,
5797 "duplicate error set field '{s}'",
5798 .{name},
5799 &[_]u32{
5800 try astgen.errNoteTok(
5801 gop.value_ptr.*,
5802 "previous declaration here",
5803 .{},
5804 ),
5805 },
5806 );
5807 }
5808 gop.value_ptr.* = tok_i;
5809
5810 try astgen.extra.ensureUnusedCapacity(gpa, 2);
5811 astgen.extra.appendAssumeCapacity(@intFromEnum(str_index));
5812 const doc_comment_index = try astgen.docCommentAsString(tok_i);
5813 astgen.extra.appendAssumeCapacity(@intFromEnum(doc_comment_index));
5814 fields_len += 1;
5815 },
5816 .r_brace => break,
5817 else => unreachable,
5818 }
5819 }
5820 }
5821
5822 setExtra(astgen, payload_index, Zir.Inst.ErrorSetDecl{
5823 .fields_len = @intCast(fields_len),
5824 });
5825 const result = try gz.addPlNodePayloadIndex(.error_set_decl, node, payload_index);
5826 return rvalue(gz, ri, result, node);
5827}
5828
5829fn tryExpr(
5830 parent_gz: *GenZir,
5831 scope: *Scope,
5832 ri: ResultInfo,
5833 node: Ast.Node.Index,
5834 operand_node: Ast.Node.Index,
5835) InnerError!Zir.Inst.Ref {
5836 const astgen = parent_gz.astgen;
5837
5838 const fn_block = astgen.fn_block orelse {
5839 return astgen.failNode(node, "'try' outside function scope", .{});
5840 };
5841
5842 if (parent_gz.any_defer_node != 0) {
5843 return astgen.failNodeNotes(node, "'try' not allowed inside defer expression", .{}, &.{
5844 try astgen.errNoteNode(
5845 parent_gz.any_defer_node,
5846 "defer expression here",
5847 .{},
5848 ),
5849 });
5850 }
5851
5852 // Ensure debug line/column information is emitted for this try expression.
5853 // Then we will save the line/column so that we can emit another one that goes
5854 // "backwards" because we want to evaluate the operand, but then put the debug
5855 // info back at the try keyword for error return tracing.
5856 if (!parent_gz.is_comptime) {
5857 try emitDbgNode(parent_gz, node);
5858 }
5859 const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
5860
5861 const operand_ri: ResultInfo = switch (ri.rl) {
5862 .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = .error_handling_expr },
5863 else => .{ .rl = .none, .ctx = .error_handling_expr },
5864 };
5865 // This could be a pointer or value depending on the `ri` parameter.
5866 const operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, node);
5867 const block_tag: Zir.Inst.Tag = if (operand_ri.rl == .ref) .try_ptr else .@"try";
5868 const try_inst = try parent_gz.makeBlockInst(block_tag, node);
5869 try parent_gz.instructions.append(astgen.gpa, try_inst);
5870
5871 var else_scope = parent_gz.makeSubBlock(scope);
5872 defer else_scope.unstack();
5873
5874 const err_tag = switch (ri.rl) {
5875 .ref, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr,
5876 else => Zir.Inst.Tag.err_union_code,
5877 };
5878 const err_code = try else_scope.addUnNode(err_tag, operand, node);
5879 try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
5880 try emitDbgStmt(&else_scope, try_lc);
5881 _ = try else_scope.addUnNode(.ret_node, err_code, node);
5882
5883 try else_scope.setTryBody(try_inst, operand);
5884 const result = try_inst.toRef();
5885 switch (ri.rl) {
5886 .ref, .ref_coerced_ty => return result,
5887 else => return rvalue(parent_gz, ri, result, node),
5888 }
5889}
5890
5891fn orelseCatchExpr(
5892 parent_gz: *GenZir,
5893 scope: *Scope,
5894 ri: ResultInfo,
5895 node: Ast.Node.Index,
5896 lhs: Ast.Node.Index,
5897 cond_op: Zir.Inst.Tag,
5898 unwrap_op: Zir.Inst.Tag,
5899 unwrap_code_op: Zir.Inst.Tag,
5900 rhs: Ast.Node.Index,
5901 payload_token: ?Ast.TokenIndex,
5902) InnerError!Zir.Inst.Ref {
5903 const astgen = parent_gz.astgen;
5904 const tree = astgen.tree;
5905
5906 const need_rl = astgen.nodes_need_rl.contains(node);
5907 const block_ri: ResultInfo = if (need_rl) ri else .{
5908 .rl = switch (ri.rl) {
5909 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
5910 .inferred_ptr => .none,
5911 else => ri.rl,
5912 },
5913 .ctx = ri.ctx,
5914 };
5915 // We need to call `rvalue` to write through to the pointer only if we had a
5916 // result pointer and aren't forwarding it.
5917 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
5918 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
5919
5920 const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr);
5921
5922 var block_scope = parent_gz.makeSubBlock(scope);
5923 block_scope.setBreakResultInfo(block_ri);
5924 defer block_scope.unstack();
5925
5926 const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) {
5927 .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none },
5928 else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none },
5929 };
5930 // This could be a pointer or value depending on the `operand_ri` parameter.
5931 // We cannot use `block_scope.break_result_info` because that has the bare
5932 // type, whereas this expression has the optional type. Later we make
5933 // up for this fact by calling rvalue on the else branch.
5934 const operand = try reachableExpr(&block_scope, &block_scope.base, operand_ri, lhs, rhs);
5935 const cond = try block_scope.addUnNode(cond_op, operand, node);
5936 const condbr = try block_scope.addCondBr(.condbr, node);
5937
5938 const block = try parent_gz.makeBlockInst(.block, node);
5939 try block_scope.setBlockBody(block);
5940 // block_scope unstacked now, can add new instructions to parent_gz
5941 try parent_gz.instructions.append(astgen.gpa, block);
5942
5943 var then_scope = block_scope.makeSubBlock(scope);
5944 defer then_scope.unstack();
5945
5946 // This could be a pointer or value depending on `unwrap_op`.
5947 const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
5948 const then_result = switch (ri.rl) {
5949 .ref, .ref_coerced_ty => unwrapped_payload,
5950 else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node),
5951 };
5952 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node);
5953
5954 var else_scope = block_scope.makeSubBlock(scope);
5955 defer else_scope.unstack();
5956
5957 // We know that the operand (almost certainly) modified the error return trace,
5958 // so signal to Sema that it should save the new index for restoring later.
5959 if (do_err_trace and nodeMayAppendToErrorTrace(tree, lhs))
5960 _ = try else_scope.addSaveErrRetIndex(.always);
5961
5962 var err_val_scope: Scope.LocalVal = undefined;
5963 const else_sub_scope = blk: {
5964 const payload = payload_token orelse break :blk &else_scope.base;
5965 const err_str = tree.tokenSlice(payload);
5966 if (mem.eql(u8, err_str, "_")) {
5967 return astgen.failTok(payload, "discard of error capture; omit it instead", .{});
5968 }
5969 const err_name = try astgen.identAsString(payload);
5970
5971 try astgen.detectLocalShadowing(scope, err_name, payload, err_str, .capture);
5972
5973 err_val_scope = .{
5974 .parent = &else_scope.base,
5975 .gen_zir = &else_scope,
5976 .name = err_name,
5977 .inst = try else_scope.addUnNode(unwrap_code_op, operand, node),
5978 .token_src = payload,
5979 .id_cat = .capture,
5980 };
5981 break :blk &err_val_scope.base;
5982 };
5983
5984 const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs);
5985 if (!else_scope.endsWithNoReturn()) {
5986 // As our last action before the break, "pop" the error trace if needed
5987 if (do_err_trace)
5988 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, rhs, else_result);
5989
5990 _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, rhs);
5991 }
5992 try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
5993
5994 try setCondBrPayload(condbr, cond, &then_scope, &else_scope);
5995
5996 if (need_result_rvalue) {
5997 return rvalue(parent_gz, ri, block.toRef(), node);
5998 } else {
5999 return block.toRef();
6000 }
6001}
6002
6003/// Return whether the identifier names of two tokens are equal. Resolves @""
6004/// tokens without allocating.
6005/// OK in theory it could do it without allocating. This implementation
6006/// allocates when the @"" form is used.
6007fn tokenIdentEql(astgen: *AstGen, token1: Ast.TokenIndex, token2: Ast.TokenIndex) !bool {
6008 const ident_name_1 = try astgen.identifierTokenString(token1);
6009 const ident_name_2 = try astgen.identifierTokenString(token2);
6010 return mem.eql(u8, ident_name_1, ident_name_2);
6011}
6012
6013fn fieldAccess(
6014 gz: *GenZir,
6015 scope: *Scope,
6016 ri: ResultInfo,
6017 node: Ast.Node.Index,
6018) InnerError!Zir.Inst.Ref {
6019 switch (ri.rl) {
6020 .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node),
6021 else => {
6022 const access = try addFieldAccess(.field_val, gz, scope, .{ .rl = .none }, node);
6023 return rvalue(gz, ri, access, node);
6024 },
6025 }
6026}
6027
6028fn addFieldAccess(
6029 tag: Zir.Inst.Tag,
6030 gz: *GenZir,
6031 scope: *Scope,
6032 lhs_ri: ResultInfo,
6033 node: Ast.Node.Index,
6034) InnerError!Zir.Inst.Ref {
6035 const astgen = gz.astgen;
6036 const tree = astgen.tree;
6037 const main_tokens = tree.nodes.items(.main_token);
6038 const node_datas = tree.nodes.items(.data);
6039
6040 const object_node = node_datas[node].lhs;
6041 const dot_token = main_tokens[node];
6042 const field_ident = dot_token + 1;
6043 const str_index = try astgen.identAsString(field_ident);
6044 const lhs = try expr(gz, scope, lhs_ri, object_node);
6045
6046 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
6047 try emitDbgStmt(gz, cursor);
6048
6049 return gz.addPlNode(tag, node, Zir.Inst.Field{
6050 .lhs = lhs,
6051 .field_name_start = str_index,
6052 });
6053}
6054
6055fn arrayAccess(
6056 gz: *GenZir,
6057 scope: *Scope,
6058 ri: ResultInfo,
6059 node: Ast.Node.Index,
6060) InnerError!Zir.Inst.Ref {
6061 const tree = gz.astgen.tree;
6062 const node_datas = tree.nodes.items(.data);
6063 switch (ri.rl) {
6064 .ref, .ref_coerced_ty => {
6065 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
6066
6067 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
6068
6069 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
6070 try emitDbgStmt(gz, cursor);
6071
6072 return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
6073 },
6074 else => {
6075 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
6076
6077 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
6078
6079 const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
6080 try emitDbgStmt(gz, cursor);
6081
6082 return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node);
6083 },
6084 }
6085}
6086
6087fn simpleBinOp(
6088 gz: *GenZir,
6089 scope: *Scope,
6090 ri: ResultInfo,
6091 node: Ast.Node.Index,
6092 op_inst_tag: Zir.Inst.Tag,
6093) InnerError!Zir.Inst.Ref {
6094 const astgen = gz.astgen;
6095 const tree = astgen.tree;
6096 const node_datas = tree.nodes.items(.data);
6097
6098 if (op_inst_tag == .cmp_neq or op_inst_tag == .cmp_eq) {
6099 const node_tags = tree.nodes.items(.tag);
6100 const str = if (op_inst_tag == .cmp_eq) "==" else "!=";
6101 if (node_tags[node_datas[node].lhs] == .string_literal or
6102 node_tags[node_datas[node].rhs] == .string_literal)
6103 return astgen.failNode(node, "cannot compare strings with {s}", .{str});
6104 }
6105
6106 const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node);
6107 const cursor = switch (op_inst_tag) {
6108 .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, node),
6109 else => undefined,
6110 };
6111 const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].rhs, node);
6112
6113 switch (op_inst_tag) {
6114 .add, .sub, .mul, .div, .mod_rem => {
6115 try emitDbgStmt(gz, cursor);
6116 },
6117 else => {},
6118 }
6119 const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
6120 return rvalue(gz, ri, result, node);
6121}
6122
6123fn simpleStrTok(
6124 gz: *GenZir,
6125 ri: ResultInfo,
6126 ident_token: Ast.TokenIndex,
6127 node: Ast.Node.Index,
6128 op_inst_tag: Zir.Inst.Tag,
6129) InnerError!Zir.Inst.Ref {
6130 const astgen = gz.astgen;
6131 const str_index = try astgen.identAsString(ident_token);
6132 const result = try gz.addStrTok(op_inst_tag, str_index, ident_token);
6133 return rvalue(gz, ri, result, node);
6134}
6135
6136fn boolBinOp(
6137 gz: *GenZir,
6138 scope: *Scope,
6139 ri: ResultInfo,
6140 node: Ast.Node.Index,
6141 zir_tag: Zir.Inst.Tag,
6142) InnerError!Zir.Inst.Ref {
6143 const astgen = gz.astgen;
6144 const tree = astgen.tree;
6145 const node_datas = tree.nodes.items(.data);
6146
6147 const lhs = try expr(gz, scope, coerced_bool_ri, node_datas[node].lhs);
6148 const bool_br = (try gz.addPlNodePayloadIndex(zir_tag, node, undefined)).toIndex().?;
6149
6150 var rhs_scope = gz.makeSubBlock(scope);
6151 defer rhs_scope.unstack();
6152 const rhs = try expr(&rhs_scope, &rhs_scope.base, coerced_bool_ri, node_datas[node].rhs);
6153 if (!gz.refIsNoReturn(rhs)) {
6154 _ = try rhs_scope.addBreakWithSrcNode(.break_inline, bool_br, rhs, node_datas[node].rhs);
6155 }
6156 try rhs_scope.setBoolBrBody(bool_br, lhs);
6157
6158 const block_ref = bool_br.toRef();
6159 return rvalue(gz, ri, block_ref, node);
6160}
6161
6162fn ifExpr(
6163 parent_gz: *GenZir,
6164 scope: *Scope,
6165 ri: ResultInfo,
6166 node: Ast.Node.Index,
6167 if_full: Ast.full.If,
6168) InnerError!Zir.Inst.Ref {
6169 const astgen = parent_gz.astgen;
6170 const tree = astgen.tree;
6171 const token_tags = tree.tokens.items(.tag);
6172
6173 const do_err_trace = astgen.fn_block != null and if_full.error_token != null;
6174
6175 const need_rl = astgen.nodes_need_rl.contains(node);
6176 const block_ri: ResultInfo = if (need_rl) ri else .{
6177 .rl = switch (ri.rl) {
6178 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
6179 .inferred_ptr => .none,
6180 else => ri.rl,
6181 },
6182 .ctx = ri.ctx,
6183 };
6184 // We need to call `rvalue` to write through to the pointer only if we had a
6185 // result pointer and aren't forwarding it.
6186 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
6187 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
6188
6189 var block_scope = parent_gz.makeSubBlock(scope);
6190 block_scope.setBreakResultInfo(block_ri);
6191 defer block_scope.unstack();
6192
6193 const payload_is_ref = if (if_full.payload_token) |payload_token|
6194 token_tags[payload_token] == .asterisk
6195 else
6196 false;
6197
6198 try emitDbgNode(parent_gz, if_full.ast.cond_expr);
6199 const cond: struct {
6200 inst: Zir.Inst.Ref,
6201 bool_bit: Zir.Inst.Ref,
6202 } = c: {
6203 if (if_full.error_token) |_| {
6204 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none, .ctx = .error_handling_expr };
6205 const err_union = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
6206 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
6207 break :c .{
6208 .inst = err_union,
6209 .bool_bit = try block_scope.addUnNode(tag, err_union, if_full.ast.cond_expr),
6210 };
6211 } else if (if_full.payload_token) |_| {
6212 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
6213 const optional = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
6214 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
6215 break :c .{
6216 .inst = optional,
6217 .bool_bit = try block_scope.addUnNode(tag, optional, if_full.ast.cond_expr),
6218 };
6219 } else {
6220 const cond = try expr(&block_scope, &block_scope.base, coerced_bool_ri, if_full.ast.cond_expr);
6221 break :c .{
6222 .inst = cond,
6223 .bool_bit = cond,
6224 };
6225 }
6226 };
6227
6228 const condbr = try block_scope.addCondBr(.condbr, node);
6229
6230 const block = try parent_gz.makeBlockInst(.block, node);
6231 try block_scope.setBlockBody(block);
6232 // block_scope unstacked now, can add new instructions to parent_gz
6233 try parent_gz.instructions.append(astgen.gpa, block);
6234
6235 var then_scope = parent_gz.makeSubBlock(scope);
6236 defer then_scope.unstack();
6237
6238 var payload_val_scope: Scope.LocalVal = undefined;
6239
6240 const then_node = if_full.ast.then_expr;
6241 const then_sub_scope = s: {
6242 if (if_full.error_token != null) {
6243 if (if_full.payload_token) |payload_token| {
6244 const tag: Zir.Inst.Tag = if (payload_is_ref)
6245 .err_union_payload_unsafe_ptr
6246 else
6247 .err_union_payload_unsafe;
6248 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node);
6249 const token_name_index = payload_token + @intFromBool(payload_is_ref);
6250 const ident_name = try astgen.identAsString(token_name_index);
6251 const token_name_str = tree.tokenSlice(token_name_index);
6252 if (mem.eql(u8, "_", token_name_str))
6253 break :s &then_scope.base;
6254 try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str, .capture);
6255 payload_val_scope = .{
6256 .parent = &then_scope.base,
6257 .gen_zir = &then_scope,
6258 .name = ident_name,
6259 .inst = payload_inst,
6260 .token_src = token_name_index,
6261 .id_cat = .capture,
6262 };
6263 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
6264 break :s &payload_val_scope.base;
6265 } else {
6266 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
6267 break :s &then_scope.base;
6268 }
6269 } else if (if_full.payload_token) |payload_token| {
6270 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
6271 const tag: Zir.Inst.Tag = if (payload_is_ref)
6272 .optional_payload_unsafe_ptr
6273 else
6274 .optional_payload_unsafe;
6275 const ident_bytes = tree.tokenSlice(ident_token);
6276 if (mem.eql(u8, "_", ident_bytes))
6277 break :s &then_scope.base;
6278 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node);
6279 const ident_name = try astgen.identAsString(ident_token);
6280 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
6281 payload_val_scope = .{
6282 .parent = &then_scope.base,
6283 .gen_zir = &then_scope,
6284 .name = ident_name,
6285 .inst = payload_inst,
6286 .token_src = ident_token,
6287 .id_cat = .capture,
6288 };
6289 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
6290 break :s &payload_val_scope.base;
6291 } else {
6292 break :s &then_scope.base;
6293 }
6294 };
6295
6296 const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node);
6297 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
6298 if (!then_scope.endsWithNoReturn()) {
6299 _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node);
6300 }
6301
6302 var else_scope = parent_gz.makeSubBlock(scope);
6303 defer else_scope.unstack();
6304
6305 // We know that the operand (almost certainly) modified the error return trace,
6306 // so signal to Sema that it should save the new index for restoring later.
6307 if (do_err_trace and nodeMayAppendToErrorTrace(tree, if_full.ast.cond_expr))
6308 _ = try else_scope.addSaveErrRetIndex(.always);
6309
6310 const else_node = if_full.ast.else_expr;
6311 if (else_node != 0) {
6312 const sub_scope = s: {
6313 if (if_full.error_token) |error_token| {
6314 const tag: Zir.Inst.Tag = if (payload_is_ref)
6315 .err_union_code_ptr
6316 else
6317 .err_union_code;
6318 const payload_inst = try else_scope.addUnNode(tag, cond.inst, if_full.ast.cond_expr);
6319 const ident_name = try astgen.identAsString(error_token);
6320 const error_token_str = tree.tokenSlice(error_token);
6321 if (mem.eql(u8, "_", error_token_str))
6322 break :s &else_scope.base;
6323 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str, .capture);
6324 payload_val_scope = .{
6325 .parent = &else_scope.base,
6326 .gen_zir = &else_scope,
6327 .name = ident_name,
6328 .inst = payload_inst,
6329 .token_src = error_token,
6330 .id_cat = .capture,
6331 };
6332 try else_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
6333 break :s &payload_val_scope.base;
6334 } else {
6335 break :s &else_scope.base;
6336 }
6337 };
6338 const else_result = try expr(&else_scope, sub_scope, block_scope.break_result_info, else_node);
6339 if (!else_scope.endsWithNoReturn()) {
6340 // As our last action before the break, "pop" the error trace if needed
6341 if (do_err_trace)
6342 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, else_node, else_result);
6343 _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, else_node);
6344 }
6345 try checkUsed(parent_gz, &else_scope.base, sub_scope);
6346 } else {
6347 const result = try rvalue(&else_scope, ri, .void_value, node);
6348 _ = try else_scope.addBreak(.@"break", block, result);
6349 }
6350
6351 try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
6352
6353 if (need_result_rvalue) {
6354 return rvalue(parent_gz, ri, block.toRef(), node);
6355 } else {
6356 return block.toRef();
6357 }
6358}
6359
6360/// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
6361fn setCondBrPayload(
6362 condbr: Zir.Inst.Index,
6363 cond: Zir.Inst.Ref,
6364 then_scope: *GenZir,
6365 else_scope: *GenZir,
6366) !void {
6367 defer then_scope.unstack();
6368 defer else_scope.unstack();
6369 const astgen = then_scope.astgen;
6370 const then_body = then_scope.instructionsSliceUpto(else_scope);
6371 const else_body = else_scope.instructionsSlice();
6372 const then_body_len = astgen.countBodyLenAfterFixups(then_body);
6373 const else_body_len = astgen.countBodyLenAfterFixups(else_body);
6374 try astgen.extra.ensureUnusedCapacity(
6375 astgen.gpa,
6376 @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len,
6377 );
6378
6379 const zir_datas = astgen.instructions.items(.data);
6380 zir_datas[@intFromEnum(condbr)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
6381 .condition = cond,
6382 .then_body_len = then_body_len,
6383 .else_body_len = else_body_len,
6384 });
6385 astgen.appendBodyWithFixups(then_body);
6386 astgen.appendBodyWithFixups(else_body);
6387}
6388
6389fn whileExpr(
6390 parent_gz: *GenZir,
6391 scope: *Scope,
6392 ri: ResultInfo,
6393 node: Ast.Node.Index,
6394 while_full: Ast.full.While,
6395 is_statement: bool,
6396) InnerError!Zir.Inst.Ref {
6397 const astgen = parent_gz.astgen;
6398 const tree = astgen.tree;
6399 const token_tags = tree.tokens.items(.tag);
6400
6401 const need_rl = astgen.nodes_need_rl.contains(node);
6402 const block_ri: ResultInfo = if (need_rl) ri else .{
6403 .rl = switch (ri.rl) {
6404 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
6405 .inferred_ptr => .none,
6406 else => ri.rl,
6407 },
6408 .ctx = ri.ctx,
6409 };
6410 // We need to call `rvalue` to write through to the pointer only if we had a
6411 // result pointer and aren't forwarding it.
6412 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
6413 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
6414
6415 if (while_full.label_token) |label_token| {
6416 try astgen.checkLabelRedefinition(scope, label_token);
6417 }
6418
6419 const is_inline = while_full.inline_token != null;
6420 if (parent_gz.is_comptime and is_inline) {
6421 return astgen.failTok(while_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
6422 }
6423 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
6424 const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
6425 try parent_gz.instructions.append(astgen.gpa, loop_block);
6426
6427 var loop_scope = parent_gz.makeSubBlock(scope);
6428 loop_scope.is_inline = is_inline;
6429 loop_scope.setBreakResultInfo(block_ri);
6430 defer loop_scope.unstack();
6431
6432 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
6433 defer cond_scope.unstack();
6434
6435 const payload_is_ref = if (while_full.payload_token) |payload_token|
6436 token_tags[payload_token] == .asterisk
6437 else
6438 false;
6439
6440 try emitDbgNode(parent_gz, while_full.ast.cond_expr);
6441 const cond: struct {
6442 inst: Zir.Inst.Ref,
6443 bool_bit: Zir.Inst.Ref,
6444 } = c: {
6445 if (while_full.error_token) |_| {
6446 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
6447 const err_union = try expr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr);
6448 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
6449 break :c .{
6450 .inst = err_union,
6451 .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.cond_expr),
6452 };
6453 } else if (while_full.payload_token) |_| {
6454 const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
6455 const optional = try expr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr);
6456 const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
6457 break :c .{
6458 .inst = optional,
6459 .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.cond_expr),
6460 };
6461 } else {
6462 const cond = try expr(&cond_scope, &cond_scope.base, coerced_bool_ri, while_full.ast.cond_expr);
6463 break :c .{
6464 .inst = cond,
6465 .bool_bit = cond,
6466 };
6467 }
6468 };
6469
6470 const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
6471 const condbr = try cond_scope.addCondBr(condbr_tag, node);
6472 const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
6473 const cond_block = try loop_scope.makeBlockInst(block_tag, node);
6474 try cond_scope.setBlockBody(cond_block);
6475 // cond_scope unstacked now, can add new instructions to loop_scope
6476 try loop_scope.instructions.append(astgen.gpa, cond_block);
6477
6478 // make scope now but don't stack on parent_gz until loop_scope
6479 // gets unstacked after cont_expr is emitted and added below
6480 var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
6481 then_scope.instructions_top = GenZir.unstacked_top;
6482 defer then_scope.unstack();
6483
6484 var dbg_var_name: Zir.NullTerminatedString = .empty;
6485 var dbg_var_inst: Zir.Inst.Ref = undefined;
6486 var opt_payload_inst: Zir.Inst.OptionalIndex = .none;
6487 var payload_val_scope: Scope.LocalVal = undefined;
6488 const then_sub_scope = s: {
6489 if (while_full.error_token != null) {
6490 if (while_full.payload_token) |payload_token| {
6491 const tag: Zir.Inst.Tag = if (payload_is_ref)
6492 .err_union_payload_unsafe_ptr
6493 else
6494 .err_union_payload_unsafe;
6495 // will add this instruction to then_scope.instructions below
6496 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
6497 opt_payload_inst = payload_inst.toOptional();
6498 const ident_token = payload_token + @intFromBool(payload_is_ref);
6499 const ident_bytes = tree.tokenSlice(ident_token);
6500 if (mem.eql(u8, "_", ident_bytes))
6501 break :s &then_scope.base;
6502 const ident_name = try astgen.identAsString(ident_token);
6503 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
6504 payload_val_scope = .{
6505 .parent = &then_scope.base,
6506 .gen_zir = &then_scope,
6507 .name = ident_name,
6508 .inst = payload_inst.toRef(),
6509 .token_src = ident_token,
6510 .id_cat = .capture,
6511 };
6512 dbg_var_name = ident_name;
6513 dbg_var_inst = payload_inst.toRef();
6514 break :s &payload_val_scope.base;
6515 } else {
6516 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
6517 break :s &then_scope.base;
6518 }
6519 } else if (while_full.payload_token) |payload_token| {
6520 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
6521 const tag: Zir.Inst.Tag = if (payload_is_ref)
6522 .optional_payload_unsafe_ptr
6523 else
6524 .optional_payload_unsafe;
6525 // will add this instruction to then_scope.instructions below
6526 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
6527 opt_payload_inst = payload_inst.toOptional();
6528 const ident_name = try astgen.identAsString(ident_token);
6529 const ident_bytes = tree.tokenSlice(ident_token);
6530 if (mem.eql(u8, "_", ident_bytes))
6531 break :s &then_scope.base;
6532 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
6533 payload_val_scope = .{
6534 .parent = &then_scope.base,
6535 .gen_zir = &then_scope,
6536 .name = ident_name,
6537 .inst = payload_inst.toRef(),
6538 .token_src = ident_token,
6539 .id_cat = .capture,
6540 };
6541 dbg_var_name = ident_name;
6542 dbg_var_inst = payload_inst.toRef();
6543 break :s &payload_val_scope.base;
6544 } else {
6545 break :s &then_scope.base;
6546 }
6547 };
6548
6549 var continue_scope = parent_gz.makeSubBlock(then_sub_scope);
6550 continue_scope.instructions_top = GenZir.unstacked_top;
6551 defer continue_scope.unstack();
6552 const continue_block = try then_scope.makeBlockInst(block_tag, node);
6553
6554 const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
6555 _ = try loop_scope.addNode(repeat_tag, node);
6556
6557 try loop_scope.setBlockBody(loop_block);
6558 loop_scope.break_block = loop_block.toOptional();
6559 loop_scope.continue_block = continue_block.toOptional();
6560 if (while_full.label_token) |label_token| {
6561 loop_scope.label = .{
6562 .token = label_token,
6563 .block_inst = loop_block,
6564 };
6565 }
6566
6567 // done adding instructions to loop_scope, can now stack then_scope
6568 then_scope.instructions_top = then_scope.instructions.items.len;
6569
6570 const then_node = while_full.ast.then_expr;
6571 if (opt_payload_inst.unwrap()) |payload_inst| {
6572 try then_scope.instructions.append(astgen.gpa, payload_inst);
6573 }
6574 if (dbg_var_name != .empty) try then_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
6575 try then_scope.instructions.append(astgen.gpa, continue_block);
6576 // This code could be improved to avoid emitting the continue expr when there
6577 // are no jumps to it. This happens when the last statement of a while body is noreturn
6578 // and there are no `continue` statements.
6579 // Tracking issue: https://github.com/ziglang/zig/issues/9185
6580 if (while_full.ast.cont_expr != 0) {
6581 _ = try unusedResultExpr(&then_scope, then_sub_scope, while_full.ast.cont_expr);
6582 }
6583
6584 continue_scope.instructions_top = continue_scope.instructions.items.len;
6585 _ = try unusedResultExpr(&continue_scope, &continue_scope.base, then_node);
6586 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
6587 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
6588 if (!continue_scope.endsWithNoReturn()) {
6589 _ = try continue_scope.addBreak(break_tag, continue_block, .void_value);
6590 }
6591 try continue_scope.setBlockBody(continue_block);
6592 _ = try then_scope.addBreak(break_tag, cond_block, .void_value);
6593
6594 var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
6595 defer else_scope.unstack();
6596
6597 const else_node = while_full.ast.else_expr;
6598 if (else_node != 0) {
6599 const sub_scope = s: {
6600 if (while_full.error_token) |error_token| {
6601 const tag: Zir.Inst.Tag = if (payload_is_ref)
6602 .err_union_code_ptr
6603 else
6604 .err_union_code;
6605 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, while_full.ast.cond_expr);
6606 const ident_name = try astgen.identAsString(error_token);
6607 const ident_bytes = tree.tokenSlice(error_token);
6608 if (mem.eql(u8, ident_bytes, "_"))
6609 break :s &else_scope.base;
6610 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes, .capture);
6611 payload_val_scope = .{
6612 .parent = &else_scope.base,
6613 .gen_zir = &else_scope,
6614 .name = ident_name,
6615 .inst = else_payload_inst,
6616 .token_src = error_token,
6617 .id_cat = .capture,
6618 };
6619 try else_scope.addDbgVar(.dbg_var_val, ident_name, else_payload_inst);
6620 break :s &payload_val_scope.base;
6621 } else {
6622 break :s &else_scope.base;
6623 }
6624 };
6625 // Remove the continue block and break block so that `continue` and `break`
6626 // control flow apply to outer loops; not this one.
6627 loop_scope.continue_block = .none;
6628 loop_scope.break_block = .none;
6629 const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
6630 if (is_statement) {
6631 _ = try addEnsureResult(&else_scope, else_result, else_node);
6632 }
6633
6634 try checkUsed(parent_gz, &else_scope.base, sub_scope);
6635 if (!else_scope.endsWithNoReturn()) {
6636 _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node);
6637 }
6638 } else {
6639 const result = try rvalue(&else_scope, ri, .void_value, node);
6640 _ = try else_scope.addBreak(break_tag, loop_block, result);
6641 }
6642
6643 if (loop_scope.label) |some| {
6644 if (!some.used) {
6645 try astgen.appendErrorTok(some.token, "unused while loop label", .{});
6646 }
6647 }
6648
6649 try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
6650
6651 const result = if (need_result_rvalue)
6652 try rvalue(parent_gz, ri, loop_block.toRef(), node)
6653 else
6654 loop_block.toRef();
6655
6656 if (is_statement) {
6657 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
6658 }
6659
6660 return result;
6661}
6662
6663fn forExpr(
6664 parent_gz: *GenZir,
6665 scope: *Scope,
6666 ri: ResultInfo,
6667 node: Ast.Node.Index,
6668 for_full: Ast.full.For,
6669 is_statement: bool,
6670) InnerError!Zir.Inst.Ref {
6671 const astgen = parent_gz.astgen;
6672
6673 if (for_full.label_token) |label_token| {
6674 try astgen.checkLabelRedefinition(scope, label_token);
6675 }
6676
6677 const need_rl = astgen.nodes_need_rl.contains(node);
6678 const block_ri: ResultInfo = if (need_rl) ri else .{
6679 .rl = switch (ri.rl) {
6680 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
6681 .inferred_ptr => .none,
6682 else => ri.rl,
6683 },
6684 .ctx = ri.ctx,
6685 };
6686 // We need to call `rvalue` to write through to the pointer only if we had a
6687 // result pointer and aren't forwarding it.
6688 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
6689 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
6690
6691 const is_inline = for_full.inline_token != null;
6692 if (parent_gz.is_comptime and is_inline) {
6693 return astgen.failTok(for_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
6694 }
6695 const tree = astgen.tree;
6696 const token_tags = tree.tokens.items(.tag);
6697 const node_tags = tree.nodes.items(.tag);
6698 const node_data = tree.nodes.items(.data);
6699 const gpa = astgen.gpa;
6700
6701 // For counters, this is the start value; for indexables, this is the base
6702 // pointer that can be used with elem_ptr and similar instructions.
6703 // Special value `none` means that this is a counter and its start value is
6704 // zero, indicating that the main index counter can be used directly.
6705 const indexables = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len);
6706 defer gpa.free(indexables);
6707 // elements of this array can be `none`, indicating no length check.
6708 const lens = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len);
6709 defer gpa.free(lens);
6710
6711 // We will use a single zero-based counter no matter how many indexables there are.
6712 const index_ptr = blk: {
6713 const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc;
6714 const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);
6715 // initialize to zero
6716 _ = try parent_gz.addPlNode(.store_node, node, Zir.Inst.Bin{
6717 .lhs = index_ptr,
6718 .rhs = .zero_usize,
6719 });
6720 break :blk index_ptr;
6721 };
6722
6723 var any_len_checks = false;
6724
6725 {
6726 var capture_token = for_full.payload_token;
6727 for (for_full.ast.inputs, indexables, lens) |input, *indexable_ref, *len_ref| {
6728 const capture_is_ref = token_tags[capture_token] == .asterisk;
6729 const ident_tok = capture_token + @intFromBool(capture_is_ref);
6730 const is_discard = mem.eql(u8, tree.tokenSlice(ident_tok), "_");
6731
6732 if (is_discard and capture_is_ref) {
6733 return astgen.failTok(capture_token, "pointer modifier invalid on discard", .{});
6734 }
6735 // Skip over the comma, and on to the next capture (or the ending pipe character).
6736 capture_token = ident_tok + 2;
6737
6738 try emitDbgNode(parent_gz, input);
6739 if (node_tags[input] == .for_range) {
6740 if (capture_is_ref) {
6741 return astgen.failTok(ident_tok, "cannot capture reference to range", .{});
6742 }
6743 const start_node = node_data[input].lhs;
6744 const start_val = try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, start_node);
6745
6746 const end_node = node_data[input].rhs;
6747 const end_val = if (end_node != 0)
6748 try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_data[input].rhs)
6749 else
6750 .none;
6751
6752 if (end_val == .none and is_discard) {
6753 return astgen.failTok(ident_tok, "discard of unbounded counter", .{});
6754 }
6755
6756 const start_is_zero = nodeIsTriviallyZero(tree, start_node);
6757 const range_len = if (end_val == .none or start_is_zero)
6758 end_val
6759 else
6760 try parent_gz.addPlNode(.sub, input, Zir.Inst.Bin{
6761 .lhs = end_val,
6762 .rhs = start_val,
6763 });
6764
6765 any_len_checks = any_len_checks or range_len != .none;
6766 indexable_ref.* = if (start_is_zero) .none else start_val;
6767 len_ref.* = range_len;
6768 } else {
6769 const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input);
6770
6771 any_len_checks = true;
6772 indexable_ref.* = indexable;
6773 len_ref.* = indexable;
6774 }
6775 }
6776 }
6777
6778 if (!any_len_checks) {
6779 return astgen.failNode(node, "unbounded for loop", .{});
6780 }
6781
6782 // We use a dedicated ZIR instruction to assert the lengths to assist with
6783 // nicer error reporting as well as fewer ZIR bytes emitted.
6784 const len: Zir.Inst.Ref = len: {
6785 const lens_len: u32 = @intCast(lens.len);
6786 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).Struct.fields.len + lens_len);
6787 const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{
6788 .operands_len = lens_len,
6789 });
6790 appendRefsAssumeCapacity(astgen, lens);
6791 break :len len;
6792 };
6793
6794 const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
6795 const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
6796 try parent_gz.instructions.append(gpa, loop_block);
6797
6798 var loop_scope = parent_gz.makeSubBlock(scope);
6799 loop_scope.is_inline = is_inline;
6800 loop_scope.setBreakResultInfo(block_ri);
6801 defer loop_scope.unstack();
6802
6803 // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the
6804 // load must be removed from instructions in the meantime or it appears to be part of parent_gz.
6805 const index = try loop_scope.addUnNode(.load, index_ptr, node);
6806 _ = loop_scope.instructions.pop();
6807
6808 var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
6809 defer cond_scope.unstack();
6810
6811 // Check the condition.
6812 const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{
6813 .lhs = index,
6814 .rhs = len,
6815 });
6816
6817 const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
6818 const condbr = try cond_scope.addCondBr(condbr_tag, node);
6819 const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
6820 const cond_block = try loop_scope.makeBlockInst(block_tag, node);
6821 try cond_scope.setBlockBody(cond_block);
6822
6823 loop_scope.break_block = loop_block.toOptional();
6824 loop_scope.continue_block = cond_block.toOptional();
6825 if (for_full.label_token) |label_token| {
6826 loop_scope.label = .{
6827 .token = label_token,
6828 .block_inst = loop_block,
6829 };
6830 }
6831
6832 const then_node = for_full.ast.then_expr;
6833 var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
6834 defer then_scope.unstack();
6835
6836 const capture_scopes = try gpa.alloc(Scope.LocalVal, for_full.ast.inputs.len);
6837 defer gpa.free(capture_scopes);
6838
6839 const then_sub_scope = blk: {
6840 var capture_token = for_full.payload_token;
6841 var capture_sub_scope: *Scope = &then_scope.base;
6842 for (for_full.ast.inputs, indexables, capture_scopes) |input, indexable_ref, *capture_scope| {
6843 const capture_is_ref = token_tags[capture_token] == .asterisk;
6844 const ident_tok = capture_token + @intFromBool(capture_is_ref);
6845 const capture_name = tree.tokenSlice(ident_tok);
6846 // Skip over the comma, and on to the next capture (or the ending pipe character).
6847 capture_token = ident_tok + 2;
6848
6849 if (mem.eql(u8, capture_name, "_")) continue;
6850
6851 const name_str_index = try astgen.identAsString(ident_tok);
6852 try astgen.detectLocalShadowing(capture_sub_scope, name_str_index, ident_tok, capture_name, .capture);
6853
6854 const capture_inst = inst: {
6855 const is_counter = node_tags[input] == .for_range;
6856
6857 if (indexable_ref == .none) {
6858 // Special case: the main index can be used directly.
6859 assert(is_counter);
6860 assert(!capture_is_ref);
6861 break :inst index;
6862 }
6863
6864 // For counters, we add the index variable to the start value; for
6865 // indexables, we use it as an element index. This is so similar
6866 // that they can share the same code paths, branching only on the
6867 // ZIR tag.
6868 const switch_cond = (@as(u2, @intFromBool(capture_is_ref)) << 1) | @intFromBool(is_counter);
6869 const tag: Zir.Inst.Tag = switch (switch_cond) {
6870 0b00 => .elem_val,
6871 0b01 => .add,
6872 0b10 => .elem_ptr,
6873 0b11 => unreachable, // compile error emitted already
6874 };
6875 break :inst try then_scope.addPlNode(tag, input, Zir.Inst.Bin{
6876 .lhs = indexable_ref,
6877 .rhs = index,
6878 });
6879 };
6880
6881 capture_scope.* = .{
6882 .parent = capture_sub_scope,
6883 .gen_zir = &then_scope,
6884 .name = name_str_index,
6885 .inst = capture_inst,
6886 .token_src = ident_tok,
6887 .id_cat = .capture,
6888 };
6889
6890 try then_scope.addDbgVar(.dbg_var_val, name_str_index, capture_inst);
6891 capture_sub_scope = &capture_scope.base;
6892 }
6893
6894 break :blk capture_sub_scope;
6895 };
6896
6897 const then_result = try expr(&then_scope, then_sub_scope, .{ .rl = .none }, then_node);
6898 _ = try addEnsureResult(&then_scope, then_result, then_node);
6899
6900 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
6901
6902 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
6903
6904 _ = try then_scope.addBreak(break_tag, cond_block, .void_value);
6905
6906 var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
6907 defer else_scope.unstack();
6908
6909 const else_node = for_full.ast.else_expr;
6910 if (else_node != 0) {
6911 const sub_scope = &else_scope.base;
6912 // Remove the continue block and break block so that `continue` and `break`
6913 // control flow apply to outer loops; not this one.
6914 loop_scope.continue_block = .none;
6915 loop_scope.break_block = .none;
6916 const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
6917 if (is_statement) {
6918 _ = try addEnsureResult(&else_scope, else_result, else_node);
6919 }
6920 if (!else_scope.endsWithNoReturn()) {
6921 _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node);
6922 }
6923 } else {
6924 const result = try rvalue(&else_scope, ri, .void_value, node);
6925 _ = try else_scope.addBreak(break_tag, loop_block, result);
6926 }
6927
6928 if (loop_scope.label) |some| {
6929 if (!some.used) {
6930 try astgen.appendErrorTok(some.token, "unused for loop label", .{});
6931 }
6932 }
6933
6934 try setCondBrPayload(condbr, cond, &then_scope, &else_scope);
6935
6936 // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it
6937 {
6938 loop_scope.instructions_top = loop_scope.instructions.items.len;
6939 try loop_scope.instructions.appendSlice(gpa, &.{ index.toIndex().?, cond_block });
6940
6941 // Increment the index variable.
6942 const index_plus_one = try loop_scope.addPlNode(.add_unsafe, node, Zir.Inst.Bin{
6943 .lhs = index,
6944 .rhs = .one_usize,
6945 });
6946 _ = try loop_scope.addPlNode(.store_node, node, Zir.Inst.Bin{
6947 .lhs = index_ptr,
6948 .rhs = index_plus_one,
6949 });
6950 const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
6951 _ = try loop_scope.addNode(repeat_tag, node);
6952
6953 try loop_scope.setBlockBody(loop_block);
6954 }
6955
6956 const result = if (need_result_rvalue)
6957 try rvalue(parent_gz, ri, loop_block.toRef(), node)
6958 else
6959 loop_block.toRef();
6960
6961 if (is_statement) {
6962 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
6963 }
6964 return result;
6965}
6966
6967fn switchExprErrUnion(
6968 parent_gz: *GenZir,
6969 scope: *Scope,
6970 ri: ResultInfo,
6971 catch_or_if_node: Ast.Node.Index,
6972 node_ty: enum { @"catch", @"if" },
6973) InnerError!Zir.Inst.Ref {
6974 const astgen = parent_gz.astgen;
6975 const gpa = astgen.gpa;
6976 const tree = astgen.tree;
6977 const node_datas = tree.nodes.items(.data);
6978 const node_tags = tree.nodes.items(.tag);
6979 const main_tokens = tree.nodes.items(.main_token);
6980 const token_tags = tree.tokens.items(.tag);
6981
6982 const if_full = switch (node_ty) {
6983 .@"catch" => undefined,
6984 .@"if" => tree.fullIf(catch_or_if_node).?,
6985 };
6986
6987 const switch_node, const operand_node, const error_payload = switch (node_ty) {
6988 .@"catch" => .{
6989 node_datas[catch_or_if_node].rhs,
6990 node_datas[catch_or_if_node].lhs,
6991 main_tokens[catch_or_if_node] + 2,
6992 },
6993 .@"if" => .{
6994 if_full.ast.else_expr,
6995 if_full.ast.cond_expr,
6996 if_full.error_token.?,
6997 },
6998 };
6999 assert(node_tags[switch_node] == .@"switch" or node_tags[switch_node] == .switch_comma);
7000
7001 const do_err_trace = astgen.fn_block != null;
7002
7003 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
7004 const case_nodes = tree.extra_data[extra.start..extra.end];
7005
7006 const need_rl = astgen.nodes_need_rl.contains(catch_or_if_node);
7007 const block_ri: ResultInfo = if (need_rl) ri else .{
7008 .rl = switch (ri.rl) {
7009 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, catch_or_if_node)).? },
7010 .inferred_ptr => .none,
7011 else => ri.rl,
7012 },
7013 .ctx = ri.ctx,
7014 };
7015
7016 const payload_is_ref = node_ty == .@"if" and
7017 if_full.payload_token != null and token_tags[if_full.payload_token.?] == .asterisk;
7018
7019 // We need to call `rvalue` to write through to the pointer only if we had a
7020 // result pointer and aren't forwarding it.
7021 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
7022 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
7023 var scalar_cases_len: u32 = 0;
7024 var multi_cases_len: u32 = 0;
7025 var inline_cases_len: u32 = 0;
7026 var has_else = false;
7027 var else_node: Ast.Node.Index = 0;
7028 var else_src: ?Ast.TokenIndex = null;
7029 for (case_nodes) |case_node| {
7030 const case = tree.fullSwitchCase(case_node).?;
7031
7032 if (case.ast.values.len == 0) {
7033 const case_src = case.ast.arrow_token - 1;
7034 if (else_src) |src| {
7035 return astgen.failTokNotes(
7036 case_src,
7037 "multiple else prongs in switch expression",
7038 .{},
7039 &[_]u32{
7040 try astgen.errNoteTok(
7041 src,
7042 "previous else prong here",
7043 .{},
7044 ),
7045 },
7046 );
7047 }
7048 has_else = true;
7049 else_node = case_node;
7050 else_src = case_src;
7051 continue;
7052 } else if (case.ast.values.len == 1 and
7053 node_tags[case.ast.values[0]] == .identifier and
7054 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
7055 {
7056 const case_src = case.ast.arrow_token - 1;
7057 return astgen.failTokNotes(
7058 case_src,
7059 "'_' prong is not allowed when switching on errors",
7060 .{},
7061 &[_]u32{
7062 try astgen.errNoteTok(
7063 case_src,
7064 "consider using 'else'",
7065 .{},
7066 ),
7067 },
7068 );
7069 }
7070
7071 for (case.ast.values) |val| {
7072 if (node_tags[val] == .string_literal)
7073 return astgen.failNode(val, "cannot switch on strings", .{});
7074 }
7075
7076 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) {
7077 scalar_cases_len += 1;
7078 } else {
7079 multi_cases_len += 1;
7080 }
7081 if (case.inline_token != null) {
7082 inline_cases_len += 1;
7083 }
7084 }
7085
7086 const operand_ri: ResultInfo = .{
7087 .rl = if (payload_is_ref) .ref else .none,
7088 .ctx = .error_handling_expr,
7089 };
7090
7091 astgen.advanceSourceCursorToNode(operand_node);
7092 const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
7093
7094 const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node);
7095 const item_ri: ResultInfo = .{ .rl = .none };
7096
7097 // This contains the data that goes into the `extra` array for the SwitchBlockErrUnion, except
7098 // the first cases_nodes.len slots are a table that indexes payloads later in the array,
7099 // with the non-error and else case indices coming first, then scalar_cases_len indexes, then
7100 // multi_cases_len indexes
7101 const payloads = &astgen.scratch;
7102 const scratch_top = astgen.scratch.items.len;
7103 const case_table_start = scratch_top;
7104 const scalar_case_table = case_table_start + 1 + @intFromBool(has_else);
7105 const multi_case_table = scalar_case_table + scalar_cases_len;
7106 const case_table_end = multi_case_table + multi_cases_len;
7107
7108 try astgen.scratch.resize(gpa, case_table_end);
7109 defer astgen.scratch.items.len = scratch_top;
7110
7111 var block_scope = parent_gz.makeSubBlock(scope);
7112 // block_scope not used for collecting instructions
7113 block_scope.instructions_top = GenZir.unstacked_top;
7114 block_scope.setBreakResultInfo(block_ri);
7115
7116 // Sema expects a dbg_stmt immediately before switch_block_err_union
7117 try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
7118 // This gets added to the parent block later, after the item expressions.
7119 const switch_block = try parent_gz.makeBlockInst(.switch_block_err_union, switch_node);
7120
7121 // We re-use this same scope for all cases, including the special prong, if any.
7122 var case_scope = parent_gz.makeSubBlock(&block_scope.base);
7123 case_scope.instructions_top = GenZir.unstacked_top;
7124
7125 {
7126 const body_len_index: u32 = @intCast(payloads.items.len);
7127 payloads.items[case_table_start] = body_len_index;
7128 try payloads.resize(gpa, body_len_index + 1); // body_len
7129
7130 case_scope.instructions_top = parent_gz.instructions.items.len;
7131 defer case_scope.unstack();
7132
7133 const unwrap_payload_tag: Zir.Inst.Tag = if (payload_is_ref)
7134 .err_union_payload_unsafe_ptr
7135 else
7136 .err_union_payload_unsafe;
7137
7138 const unwrapped_payload = try case_scope.addUnNode(
7139 unwrap_payload_tag,
7140 raw_operand,
7141 catch_or_if_node,
7142 );
7143
7144 switch (node_ty) {
7145 .@"catch" => {
7146 const case_result = switch (ri.rl) {
7147 .ref, .ref_coerced_ty => unwrapped_payload,
7148 else => try rvalue(
7149 &case_scope,
7150 block_scope.break_result_info,
7151 unwrapped_payload,
7152 catch_or_if_node,
7153 ),
7154 };
7155 _ = try case_scope.addBreakWithSrcNode(
7156 .@"break",
7157 switch_block,
7158 case_result,
7159 catch_or_if_node,
7160 );
7161 },
7162 .@"if" => {
7163 var payload_val_scope: Scope.LocalVal = undefined;
7164
7165 const then_node = if_full.ast.then_expr;
7166 const then_sub_scope = s: {
7167 assert(if_full.error_token != null);
7168 if (if_full.payload_token) |payload_token| {
7169 const token_name_index = payload_token + @intFromBool(payload_is_ref);
7170 const ident_name = try astgen.identAsString(token_name_index);
7171 const token_name_str = tree.tokenSlice(token_name_index);
7172 if (mem.eql(u8, "_", token_name_str))
7173 break :s &case_scope.base;
7174 try astgen.detectLocalShadowing(
7175 &case_scope.base,
7176 ident_name,
7177 token_name_index,
7178 token_name_str,
7179 .capture,
7180 );
7181 payload_val_scope = .{
7182 .parent = &case_scope.base,
7183 .gen_zir = &case_scope,
7184 .name = ident_name,
7185 .inst = unwrapped_payload,
7186 .token_src = token_name_index,
7187 .id_cat = .capture,
7188 };
7189 try case_scope.addDbgVar(.dbg_var_val, ident_name, unwrapped_payload);
7190 break :s &payload_val_scope.base;
7191 } else {
7192 _ = try case_scope.addUnNode(
7193 .ensure_err_union_payload_void,
7194 raw_operand,
7195 catch_or_if_node,
7196 );
7197 break :s &case_scope.base;
7198 }
7199 };
7200 const then_result = try expr(
7201 &case_scope,
7202 then_sub_scope,
7203 block_scope.break_result_info,
7204 then_node,
7205 );
7206 try checkUsed(parent_gz, &case_scope.base, then_sub_scope);
7207 if (!case_scope.endsWithNoReturn()) {
7208 _ = try case_scope.addBreakWithSrcNode(
7209 .@"break",
7210 switch_block,
7211 then_result,
7212 then_node,
7213 );
7214 }
7215 },
7216 }
7217
7218 const case_slice = case_scope.instructionsSlice();
7219 // Since we use the switch_block_err_union instruction itself to refer
7220 // to the capture, which will not be added to the child block, we need
7221 // to handle ref_table manually.
7222 const refs_len = refs: {
7223 var n: usize = 0;
7224 var check_inst = switch_block;
7225 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7226 n += 1;
7227 check_inst = ref_inst;
7228 }
7229 break :refs n;
7230 };
7231 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
7232 try payloads.ensureUnusedCapacity(gpa, body_len);
7233 const capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = switch (node_ty) {
7234 .@"catch" => .none,
7235 .@"if" => if (if_full.payload_token == null)
7236 .none
7237 else if (payload_is_ref)
7238 .by_ref
7239 else
7240 .by_val,
7241 };
7242 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
7243 .body_len = @intCast(body_len),
7244 .capture = capture,
7245 .is_inline = false,
7246 .has_tag_capture = false,
7247 });
7248 if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
7249 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7250 }
7251 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
7252 }
7253
7254 const err_name = blk: {
7255 const err_str = tree.tokenSlice(error_payload);
7256 if (mem.eql(u8, err_str, "_")) {
7257 return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{});
7258 }
7259 const err_name = try astgen.identAsString(error_payload);
7260 try astgen.detectLocalShadowing(scope, err_name, error_payload, err_str, .capture);
7261
7262 break :blk err_name;
7263 };
7264
7265 // allocate a shared dummy instruction for the error capture
7266 const err_inst = err_inst: {
7267 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
7268 try astgen.instructions.append(astgen.gpa, .{
7269 .tag = .extended,
7270 .data = .{ .extended = .{
7271 .opcode = .value_placeholder,
7272 .small = undefined,
7273 .operand = undefined,
7274 } },
7275 });
7276 break :err_inst inst;
7277 };
7278
7279 // In this pass we generate all the item and prong expressions for error cases.
7280 var multi_case_index: u32 = 0;
7281 var scalar_case_index: u32 = 0;
7282 var any_uses_err_capture = false;
7283 for (case_nodes) |case_node| {
7284 const case = tree.fullSwitchCase(case_node).?;
7285
7286 const is_multi_case = case.ast.values.len > 1 or
7287 (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
7288
7289 var dbg_var_name: Zir.NullTerminatedString = .empty;
7290 var dbg_var_inst: Zir.Inst.Ref = undefined;
7291 var err_scope: Scope.LocalVal = undefined;
7292 var capture_scope: Scope.LocalVal = undefined;
7293
7294 const sub_scope = blk: {
7295 err_scope = .{
7296 .parent = &case_scope.base,
7297 .gen_zir = &case_scope,
7298 .name = err_name,
7299 .inst = err_inst.toRef(),
7300 .token_src = error_payload,
7301 .id_cat = .capture,
7302 };
7303
7304 const capture_token = case.payload_token orelse break :blk &err_scope.base;
7305 if (token_tags[capture_token] != .identifier) {
7306 return astgen.failTok(capture_token + 1, "error set cannot be captured by reference", .{});
7307 }
7308
7309 const capture_slice = tree.tokenSlice(capture_token);
7310 if (mem.eql(u8, capture_slice, "_")) {
7311 return astgen.failTok(capture_token, "discard of error capture; omit it instead", .{});
7312 }
7313 const tag_name = try astgen.identAsString(capture_token);
7314 try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture);
7315
7316 capture_scope = .{
7317 .parent = &case_scope.base,
7318 .gen_zir = &case_scope,
7319 .name = tag_name,
7320 .inst = switch_block.toRef(),
7321 .token_src = capture_token,
7322 .id_cat = .capture,
7323 };
7324 dbg_var_name = tag_name;
7325 dbg_var_inst = switch_block.toRef();
7326
7327 err_scope.parent = &capture_scope.base;
7328
7329 break :blk &err_scope.base;
7330 };
7331
7332 const header_index: u32 = @intCast(payloads.items.len);
7333 const body_len_index = if (is_multi_case) blk: {
7334 payloads.items[multi_case_table + multi_case_index] = header_index;
7335 multi_case_index += 1;
7336 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
7337
7338 // items
7339 var items_len: u32 = 0;
7340 for (case.ast.values) |item_node| {
7341 if (node_tags[item_node] == .switch_range) continue;
7342 items_len += 1;
7343
7344 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
7345 try payloads.append(gpa, @intFromEnum(item_inst));
7346 }
7347
7348 // ranges
7349 var ranges_len: u32 = 0;
7350 for (case.ast.values) |range| {
7351 if (node_tags[range] != .switch_range) continue;
7352 ranges_len += 1;
7353
7354 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs);
7355 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs);
7356 try payloads.appendSlice(gpa, &[_]u32{
7357 @intFromEnum(first), @intFromEnum(last),
7358 });
7359 }
7360
7361 payloads.items[header_index] = items_len;
7362 payloads.items[header_index + 1] = ranges_len;
7363 break :blk header_index + 2;
7364 } else if (case_node == else_node) blk: {
7365 payloads.items[case_table_start + 1] = header_index;
7366 try payloads.resize(gpa, header_index + 1); // body_len
7367 break :blk header_index;
7368 } else blk: {
7369 payloads.items[scalar_case_table + scalar_case_index] = header_index;
7370 scalar_case_index += 1;
7371 try payloads.resize(gpa, header_index + 2); // item, body_len
7372 const item_node = case.ast.values[0];
7373 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
7374 payloads.items[header_index] = @intFromEnum(item_inst);
7375 break :blk header_index + 1;
7376 };
7377
7378 {
7379 // temporarily stack case_scope on parent_gz
7380 case_scope.instructions_top = parent_gz.instructions.items.len;
7381 defer case_scope.unstack();
7382
7383 if (do_err_trace and nodeMayAppendToErrorTrace(tree, operand_node))
7384 _ = try case_scope.addSaveErrRetIndex(.always);
7385
7386 if (dbg_var_name != .empty) {
7387 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
7388 }
7389
7390 const target_expr_node = case.ast.target_expr;
7391 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node);
7392 // check capture_scope, not err_scope to avoid false positive unused error capture
7393 try checkUsed(parent_gz, &case_scope.base, err_scope.parent);
7394 const uses_err = err_scope.used != 0 or err_scope.discarded != 0;
7395 if (uses_err) {
7396 try case_scope.addDbgVar(.dbg_var_val, err_name, err_inst.toRef());
7397 any_uses_err_capture = true;
7398 }
7399
7400 if (!parent_gz.refIsNoReturn(case_result)) {
7401 if (do_err_trace)
7402 try restoreErrRetIndex(
7403 &case_scope,
7404 .{ .block = switch_block },
7405 block_scope.break_result_info,
7406 target_expr_node,
7407 case_result,
7408 );
7409
7410 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
7411 }
7412
7413 const case_slice = case_scope.instructionsSlice();
7414 // Since we use the switch_block_err_union instruction itself to refer
7415 // to the capture, which will not be added to the child block, we need
7416 // to handle ref_table manually.
7417 const refs_len = refs: {
7418 var n: usize = 0;
7419 var check_inst = switch_block;
7420 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7421 n += 1;
7422 check_inst = ref_inst;
7423 }
7424 if (uses_err) {
7425 check_inst = err_inst;
7426 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7427 n += 1;
7428 check_inst = ref_inst;
7429 }
7430 }
7431 break :refs n;
7432 };
7433 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
7434 try payloads.ensureUnusedCapacity(gpa, body_len);
7435 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
7436 .body_len = @intCast(body_len),
7437 .capture = if (case.payload_token != null) .by_val else .none,
7438 .is_inline = case.inline_token != null,
7439 .has_tag_capture = false,
7440 });
7441 if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
7442 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7443 }
7444 if (uses_err) {
7445 if (astgen.ref_table.fetchRemove(err_inst)) |kv| {
7446 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7447 }
7448 }
7449 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
7450 }
7451 }
7452 // Now that the item expressions are generated we can add this.
7453 try parent_gz.instructions.append(gpa, switch_block);
7454
7455 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlockErrUnion).Struct.fields.len +
7456 @intFromBool(multi_cases_len != 0) +
7457 payloads.items.len - case_table_end +
7458 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).Struct.fields.len);
7459
7460 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlockErrUnion{
7461 .operand = raw_operand,
7462 .bits = Zir.Inst.SwitchBlockErrUnion.Bits{
7463 .has_multi_cases = multi_cases_len != 0,
7464 .has_else = has_else,
7465 .scalar_cases_len = @intCast(scalar_cases_len),
7466 .any_uses_err_capture = any_uses_err_capture,
7467 .payload_is_ref = payload_is_ref,
7468 },
7469 .main_src_node_offset = parent_gz.nodeIndexToRelative(catch_or_if_node),
7470 });
7471
7472 if (multi_cases_len != 0) {
7473 astgen.extra.appendAssumeCapacity(multi_cases_len);
7474 }
7475
7476 if (any_uses_err_capture) {
7477 astgen.extra.appendAssumeCapacity(@intFromEnum(err_inst));
7478 }
7479
7480 const zir_datas = astgen.instructions.items(.data);
7481 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
7482
7483 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
7484 var body_len_index = start_index;
7485 var end_index = start_index;
7486 const table_index = case_table_start + i;
7487 if (table_index < scalar_case_table) {
7488 end_index += 1;
7489 } else if (table_index < multi_case_table) {
7490 body_len_index += 1;
7491 end_index += 2;
7492 } else {
7493 body_len_index += 2;
7494 const items_len = payloads.items[start_index];
7495 const ranges_len = payloads.items[start_index + 1];
7496 end_index += 3 + items_len + 2 * ranges_len;
7497 }
7498 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
7499 end_index += prong_info.body_len;
7500 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
7501 }
7502
7503 if (need_result_rvalue) {
7504 return rvalue(parent_gz, ri, switch_block.toRef(), switch_node);
7505 } else {
7506 return switch_block.toRef();
7507 }
7508}
7509
7510fn switchExpr(
7511 parent_gz: *GenZir,
7512 scope: *Scope,
7513 ri: ResultInfo,
7514 switch_node: Ast.Node.Index,
7515) InnerError!Zir.Inst.Ref {
7516 const astgen = parent_gz.astgen;
7517 const gpa = astgen.gpa;
7518 const tree = astgen.tree;
7519 const node_datas = tree.nodes.items(.data);
7520 const node_tags = tree.nodes.items(.tag);
7521 const main_tokens = tree.nodes.items(.main_token);
7522 const token_tags = tree.tokens.items(.tag);
7523 const operand_node = node_datas[switch_node].lhs;
7524 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
7525 const case_nodes = tree.extra_data[extra.start..extra.end];
7526
7527 const need_rl = astgen.nodes_need_rl.contains(switch_node);
7528 const block_ri: ResultInfo = if (need_rl) ri else .{
7529 .rl = switch (ri.rl) {
7530 .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, switch_node)).? },
7531 .inferred_ptr => .none,
7532 else => ri.rl,
7533 },
7534 .ctx = ri.ctx,
7535 };
7536 // We need to call `rvalue` to write through to the pointer only if we had a
7537 // result pointer and aren't forwarding it.
7538 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
7539 const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
7540
7541 // We perform two passes over the AST. This first pass is to collect information
7542 // for the following variables, make note of the special prong AST node index,
7543 // and bail out with a compile error if there are multiple special prongs present.
7544 var any_payload_is_ref = false;
7545 var any_has_tag_capture = false;
7546 var scalar_cases_len: u32 = 0;
7547 var multi_cases_len: u32 = 0;
7548 var inline_cases_len: u32 = 0;
7549 var special_prong: Zir.SpecialProng = .none;
7550 var special_node: Ast.Node.Index = 0;
7551 var else_src: ?Ast.TokenIndex = null;
7552 var underscore_src: ?Ast.TokenIndex = null;
7553 for (case_nodes) |case_node| {
7554 const case = tree.fullSwitchCase(case_node).?;
7555 if (case.payload_token) |payload_token| {
7556 const ident = if (token_tags[payload_token] == .asterisk) blk: {
7557 any_payload_is_ref = true;
7558 break :blk payload_token + 1;
7559 } else payload_token;
7560 if (token_tags[ident + 1] == .comma) {
7561 any_has_tag_capture = true;
7562 }
7563 }
7564 // Check for else/`_` prong.
7565 if (case.ast.values.len == 0) {
7566 const case_src = case.ast.arrow_token - 1;
7567 if (else_src) |src| {
7568 return astgen.failTokNotes(
7569 case_src,
7570 "multiple else prongs in switch expression",
7571 .{},
7572 &[_]u32{
7573 try astgen.errNoteTok(
7574 src,
7575 "previous else prong here",
7576 .{},
7577 ),
7578 },
7579 );
7580 } else if (underscore_src) |some_underscore| {
7581 return astgen.failNodeNotes(
7582 switch_node,
7583 "else and '_' prong in switch expression",
7584 .{},
7585 &[_]u32{
7586 try astgen.errNoteTok(
7587 case_src,
7588 "else prong here",
7589 .{},
7590 ),
7591 try astgen.errNoteTok(
7592 some_underscore,
7593 "'_' prong here",
7594 .{},
7595 ),
7596 },
7597 );
7598 }
7599 special_node = case_node;
7600 special_prong = .@"else";
7601 else_src = case_src;
7602 continue;
7603 } else if (case.ast.values.len == 1 and
7604 node_tags[case.ast.values[0]] == .identifier and
7605 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
7606 {
7607 const case_src = case.ast.arrow_token - 1;
7608 if (underscore_src) |src| {
7609 return astgen.failTokNotes(
7610 case_src,
7611 "multiple '_' prongs in switch expression",
7612 .{},
7613 &[_]u32{
7614 try astgen.errNoteTok(
7615 src,
7616 "previous '_' prong here",
7617 .{},
7618 ),
7619 },
7620 );
7621 } else if (else_src) |some_else| {
7622 return astgen.failNodeNotes(
7623 switch_node,
7624 "else and '_' prong in switch expression",
7625 .{},
7626 &[_]u32{
7627 try astgen.errNoteTok(
7628 some_else,
7629 "else prong here",
7630 .{},
7631 ),
7632 try astgen.errNoteTok(
7633 case_src,
7634 "'_' prong here",
7635 .{},
7636 ),
7637 },
7638 );
7639 }
7640 if (case.inline_token != null) {
7641 return astgen.failTok(case_src, "cannot inline '_' prong", .{});
7642 }
7643 special_node = case_node;
7644 special_prong = .under;
7645 underscore_src = case_src;
7646 continue;
7647 }
7648
7649 for (case.ast.values) |val| {
7650 if (node_tags[val] == .string_literal)
7651 return astgen.failNode(val, "cannot switch on strings", .{});
7652 }
7653
7654 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) {
7655 scalar_cases_len += 1;
7656 } else {
7657 multi_cases_len += 1;
7658 }
7659 if (case.inline_token != null) {
7660 inline_cases_len += 1;
7661 }
7662 }
7663
7664 const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };
7665
7666 astgen.advanceSourceCursorToNode(operand_node);
7667 const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
7668
7669 const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
7670 const item_ri: ResultInfo = .{ .rl = .none };
7671
7672 // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
7673 // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
7674 // the special case index coming first, then scalar_case_len indexes, then multi_cases_len indexes
7675 const payloads = &astgen.scratch;
7676 const scratch_top = astgen.scratch.items.len;
7677 const case_table_start = scratch_top;
7678 const scalar_case_table = case_table_start + @intFromBool(special_prong != .none);
7679 const multi_case_table = scalar_case_table + scalar_cases_len;
7680 const case_table_end = multi_case_table + multi_cases_len;
7681 try astgen.scratch.resize(gpa, case_table_end);
7682 defer astgen.scratch.items.len = scratch_top;
7683
7684 var block_scope = parent_gz.makeSubBlock(scope);
7685 // block_scope not used for collecting instructions
7686 block_scope.instructions_top = GenZir.unstacked_top;
7687 block_scope.setBreakResultInfo(block_ri);
7688
7689 // Sema expects a dbg_stmt immediately before switch_block(_ref)
7690 try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
7691 // This gets added to the parent block later, after the item expressions.
7692 const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block;
7693 const switch_block = try parent_gz.makeBlockInst(switch_tag, switch_node);
7694
7695 // We re-use this same scope for all cases, including the special prong, if any.
7696 var case_scope = parent_gz.makeSubBlock(&block_scope.base);
7697 case_scope.instructions_top = GenZir.unstacked_top;
7698
7699 // If any prong has an inline tag capture, allocate a shared dummy instruction for it
7700 const tag_inst = if (any_has_tag_capture) tag_inst: {
7701 const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
7702 try astgen.instructions.append(astgen.gpa, .{
7703 .tag = .extended,
7704 .data = .{ .extended = .{
7705 .opcode = .value_placeholder,
7706 .small = undefined,
7707 .operand = undefined,
7708 } },
7709 });
7710 break :tag_inst inst;
7711 } else undefined;
7712
7713 // In this pass we generate all the item and prong expressions.
7714 var multi_case_index: u32 = 0;
7715 var scalar_case_index: u32 = 0;
7716 for (case_nodes) |case_node| {
7717 const case = tree.fullSwitchCase(case_node).?;
7718
7719 const is_multi_case = case.ast.values.len > 1 or
7720 (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
7721
7722 var dbg_var_name: Zir.NullTerminatedString = .empty;
7723 var dbg_var_inst: Zir.Inst.Ref = undefined;
7724 var dbg_var_tag_name: Zir.NullTerminatedString = .empty;
7725 var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
7726 var has_tag_capture = false;
7727 var capture_val_scope: Scope.LocalVal = undefined;
7728 var tag_scope: Scope.LocalVal = undefined;
7729
7730 var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none;
7731
7732 const sub_scope = blk: {
7733 const payload_token = case.payload_token orelse break :blk &case_scope.base;
7734 const ident = if (token_tags[payload_token] == .asterisk)
7735 payload_token + 1
7736 else
7737 payload_token;
7738
7739 const is_ptr = ident != payload_token;
7740 capture = if (is_ptr) .by_ref else .by_val;
7741
7742 const ident_slice = tree.tokenSlice(ident);
7743 var payload_sub_scope: *Scope = undefined;
7744 if (mem.eql(u8, ident_slice, "_")) {
7745 if (is_ptr) {
7746 return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
7747 }
7748 payload_sub_scope = &case_scope.base;
7749 } else {
7750 const capture_name = try astgen.identAsString(ident);
7751 try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture);
7752 capture_val_scope = .{
7753 .parent = &case_scope.base,
7754 .gen_zir = &case_scope,
7755 .name = capture_name,
7756 .inst = switch_block.toRef(),
7757 .token_src = ident,
7758 .id_cat = .capture,
7759 };
7760 dbg_var_name = capture_name;
7761 dbg_var_inst = switch_block.toRef();
7762 payload_sub_scope = &capture_val_scope.base;
7763 }
7764
7765 const tag_token = if (token_tags[ident + 1] == .comma)
7766 ident + 2
7767 else
7768 break :blk payload_sub_scope;
7769 const tag_slice = tree.tokenSlice(tag_token);
7770 if (mem.eql(u8, tag_slice, "_")) {
7771 return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{});
7772 } else if (case.inline_token == null) {
7773 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
7774 }
7775 const tag_name = try astgen.identAsString(tag_token);
7776 try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture");
7777
7778 assert(any_has_tag_capture);
7779 has_tag_capture = true;
7780
7781 tag_scope = .{
7782 .parent = payload_sub_scope,
7783 .gen_zir = &case_scope,
7784 .name = tag_name,
7785 .inst = tag_inst.toRef(),
7786 .token_src = tag_token,
7787 .id_cat = .@"switch tag capture",
7788 };
7789 dbg_var_tag_name = tag_name;
7790 dbg_var_tag_inst = tag_inst.toRef();
7791 break :blk &tag_scope.base;
7792 };
7793
7794 const header_index: u32 = @intCast(payloads.items.len);
7795 const body_len_index = if (is_multi_case) blk: {
7796 payloads.items[multi_case_table + multi_case_index] = header_index;
7797 multi_case_index += 1;
7798 try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
7799
7800 // items
7801 var items_len: u32 = 0;
7802 for (case.ast.values) |item_node| {
7803 if (node_tags[item_node] == .switch_range) continue;
7804 items_len += 1;
7805
7806 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
7807 try payloads.append(gpa, @intFromEnum(item_inst));
7808 }
7809
7810 // ranges
7811 var ranges_len: u32 = 0;
7812 for (case.ast.values) |range| {
7813 if (node_tags[range] != .switch_range) continue;
7814 ranges_len += 1;
7815
7816 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs);
7817 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs);
7818 try payloads.appendSlice(gpa, &[_]u32{
7819 @intFromEnum(first), @intFromEnum(last),
7820 });
7821 }
7822
7823 payloads.items[header_index] = items_len;
7824 payloads.items[header_index + 1] = ranges_len;
7825 break :blk header_index + 2;
7826 } else if (case_node == special_node) blk: {
7827 payloads.items[case_table_start] = header_index;
7828 try payloads.resize(gpa, header_index + 1); // body_len
7829 break :blk header_index;
7830 } else blk: {
7831 payloads.items[scalar_case_table + scalar_case_index] = header_index;
7832 scalar_case_index += 1;
7833 try payloads.resize(gpa, header_index + 2); // item, body_len
7834 const item_node = case.ast.values[0];
7835 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
7836 payloads.items[header_index] = @intFromEnum(item_inst);
7837 break :blk header_index + 1;
7838 };
7839
7840 {
7841 // temporarily stack case_scope on parent_gz
7842 case_scope.instructions_top = parent_gz.instructions.items.len;
7843 defer case_scope.unstack();
7844
7845 if (dbg_var_name != .empty) {
7846 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
7847 }
7848 if (dbg_var_tag_name != .empty) {
7849 try case_scope.addDbgVar(.dbg_var_val, dbg_var_tag_name, dbg_var_tag_inst);
7850 }
7851 const target_expr_node = case.ast.target_expr;
7852 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node);
7853 try checkUsed(parent_gz, &case_scope.base, sub_scope);
7854 if (!parent_gz.refIsNoReturn(case_result)) {
7855 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
7856 }
7857
7858 const case_slice = case_scope.instructionsSlice();
7859 // Since we use the switch_block instruction itself to refer to the
7860 // capture, which will not be added to the child block, we need to
7861 // handle ref_table manually, and the same for the inline tag
7862 // capture instruction.
7863 const refs_len = refs: {
7864 var n: usize = 0;
7865 var check_inst = switch_block;
7866 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7867 n += 1;
7868 check_inst = ref_inst;
7869 }
7870 if (has_tag_capture) {
7871 check_inst = tag_inst;
7872 while (astgen.ref_table.get(check_inst)) |ref_inst| {
7873 n += 1;
7874 check_inst = ref_inst;
7875 }
7876 }
7877 break :refs n;
7878 };
7879 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
7880 try payloads.ensureUnusedCapacity(gpa, body_len);
7881 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
7882 .body_len = @intCast(body_len),
7883 .capture = capture,
7884 .is_inline = case.inline_token != null,
7885 .has_tag_capture = has_tag_capture,
7886 });
7887 if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
7888 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7889 }
7890 if (has_tag_capture) {
7891 if (astgen.ref_table.fetchRemove(tag_inst)) |kv| {
7892 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
7893 }
7894 }
7895 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
7896 }
7897 }
7898 // Now that the item expressions are generated we can add this.
7899 try parent_gz.instructions.append(gpa, switch_block);
7900
7901 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
7902 @intFromBool(multi_cases_len != 0) +
7903 @intFromBool(any_has_tag_capture) +
7904 payloads.items.len - case_table_end +
7905 (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).Struct.fields.len);
7906
7907 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
7908 .operand = raw_operand,
7909 .bits = Zir.Inst.SwitchBlock.Bits{
7910 .has_multi_cases = multi_cases_len != 0,
7911 .has_else = special_prong == .@"else",
7912 .has_under = special_prong == .under,
7913 .any_has_tag_capture = any_has_tag_capture,
7914 .scalar_cases_len = @intCast(scalar_cases_len),
7915 },
7916 });
7917
7918 if (multi_cases_len != 0) {
7919 astgen.extra.appendAssumeCapacity(multi_cases_len);
7920 }
7921
7922 if (any_has_tag_capture) {
7923 astgen.extra.appendAssumeCapacity(@intFromEnum(tag_inst));
7924 }
7925
7926 const zir_datas = astgen.instructions.items(.data);
7927 zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
7928
7929 for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
7930 var body_len_index = start_index;
7931 var end_index = start_index;
7932 const table_index = case_table_start + i;
7933 if (table_index < scalar_case_table) {
7934 end_index += 1;
7935 } else if (table_index < multi_case_table) {
7936 body_len_index += 1;
7937 end_index += 2;
7938 } else {
7939 body_len_index += 2;
7940 const items_len = payloads.items[start_index];
7941 const ranges_len = payloads.items[start_index + 1];
7942 end_index += 3 + items_len + 2 * ranges_len;
7943 }
7944 const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
7945 end_index += prong_info.body_len;
7946 astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
7947 }
7948
7949 if (need_result_rvalue) {
7950 return rvalue(parent_gz, ri, switch_block.toRef(), switch_node);
7951 } else {
7952 return switch_block.toRef();
7953 }
7954}
7955
7956fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
7957 const astgen = gz.astgen;
7958 const tree = astgen.tree;
7959 const node_datas = tree.nodes.items(.data);
7960 const node_tags = tree.nodes.items(.tag);
7961
7962 if (astgen.fn_block == null) {
7963 return astgen.failNode(node, "'return' outside function scope", .{});
7964 }
7965
7966 if (gz.any_defer_node != 0) {
7967 return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{
7968 try astgen.errNoteNode(
7969 gz.any_defer_node,
7970 "defer expression here",
7971 .{},
7972 ),
7973 });
7974 }
7975
7976 // Ensure debug line/column information is emitted for this return expression.
7977 // Then we will save the line/column so that we can emit another one that goes
7978 // "backwards" because we want to evaluate the operand, but then put the debug
7979 // info back at the return keyword for error return tracing.
7980 if (!gz.is_comptime) {
7981 try emitDbgNode(gz, node);
7982 }
7983 const ret_lc = LineColumn{ astgen.source_line - gz.decl_line, astgen.source_column };
7984
7985 const defer_outer = &astgen.fn_block.?.base;
7986
7987 const operand_node = node_datas[node].lhs;
7988 if (operand_node == 0) {
7989 // Returning a void value; skip error defers.
7990 try genDefers(gz, defer_outer, scope, .normal_only);
7991
7992 // As our last action before the return, "pop" the error trace if needed
7993 _ = try gz.addRestoreErrRetIndex(.ret, .always, node);
7994
7995 _ = try gz.addUnNode(.ret_node, .void_value, node);
7996 return Zir.Inst.Ref.unreachable_value;
7997 }
7998
7999 if (node_tags[operand_node] == .error_value) {
8000 // Hot path for `return error.Foo`. This bypasses result location logic as well as logic
8001 // for detecting whether to add something to the function's inferred error set.
8002 const ident_token = node_datas[operand_node].rhs;
8003 const err_name_str_index = try astgen.identAsString(ident_token);
8004 const defer_counts = countDefers(defer_outer, scope);
8005 if (!defer_counts.need_err_code) {
8006 try genDefers(gz, defer_outer, scope, .both_sans_err);
8007 try emitDbgStmt(gz, ret_lc);
8008 _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
8009 return Zir.Inst.Ref.unreachable_value;
8010 }
8011 const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
8012 try genDefers(gz, defer_outer, scope, .{ .both = err_code });
8013 try emitDbgStmt(gz, ret_lc);
8014 _ = try gz.addUnNode(.ret_node, err_code, node);
8015 return Zir.Inst.Ref.unreachable_value;
8016 }
8017
8018 const ri: ResultInfo = if (astgen.nodes_need_rl.contains(node)) .{
8019 .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } },
8020 .ctx = .@"return",
8021 } else .{
8022 .rl = .{ .coerced_ty = astgen.fn_ret_ty },
8023 .ctx = .@"return",
8024 };
8025 const prev_anon_name_strategy = gz.anon_name_strategy;
8026 gz.anon_name_strategy = .func;
8027 const operand = try reachableExpr(gz, scope, ri, operand_node, node);
8028 gz.anon_name_strategy = prev_anon_name_strategy;
8029
8030 switch (nodeMayEvalToError(tree, operand_node)) {
8031 .never => {
8032 // Returning a value that cannot be an error; skip error defers.
8033 try genDefers(gz, defer_outer, scope, .normal_only);
8034
8035 // As our last action before the return, "pop" the error trace if needed
8036 _ = try gz.addRestoreErrRetIndex(.ret, .always, node);
8037
8038 try emitDbgStmt(gz, ret_lc);
8039 try gz.addRet(ri, operand, node);
8040 return Zir.Inst.Ref.unreachable_value;
8041 },
8042 .always => {
8043 // Value is always an error. Emit both error defers and regular defers.
8044 const err_code = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
8045 try genDefers(gz, defer_outer, scope, .{ .both = err_code });
8046 try emitDbgStmt(gz, ret_lc);
8047 try gz.addRet(ri, operand, node);
8048 return Zir.Inst.Ref.unreachable_value;
8049 },
8050 .maybe => {
8051 const defer_counts = countDefers(defer_outer, scope);
8052 if (!defer_counts.have_err) {
8053 // Only regular defers; no branch needed.
8054 try genDefers(gz, defer_outer, scope, .normal_only);
8055 try emitDbgStmt(gz, ret_lc);
8056
8057 // As our last action before the return, "pop" the error trace if needed
8058 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
8059 _ = try gz.addRestoreErrRetIndex(.ret, .{ .if_non_error = result }, node);
8060
8061 try gz.addRet(ri, operand, node);
8062 return Zir.Inst.Ref.unreachable_value;
8063 }
8064
8065 // Emit conditional branch for generating errdefers.
8066 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
8067 const is_non_err = try gz.addUnNode(.ret_is_non_err, result, node);
8068 const condbr = try gz.addCondBr(.condbr, node);
8069
8070 var then_scope = gz.makeSubBlock(scope);
8071 defer then_scope.unstack();
8072
8073 try genDefers(&then_scope, defer_outer, scope, .normal_only);
8074
8075 // As our last action before the return, "pop" the error trace if needed
8076 _ = try then_scope.addRestoreErrRetIndex(.ret, .always, node);
8077
8078 try emitDbgStmt(&then_scope, ret_lc);
8079 try then_scope.addRet(ri, operand, node);
8080
8081 var else_scope = gz.makeSubBlock(scope);
8082 defer else_scope.unstack();
8083
8084 const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
8085 .both = try else_scope.addUnNode(.err_union_code, result, node),
8086 };
8087 try genDefers(&else_scope, defer_outer, scope, which_ones);
8088 try emitDbgStmt(&else_scope, ret_lc);
8089 try else_scope.addRet(ri, operand, node);
8090
8091 try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope);
8092
8093 return Zir.Inst.Ref.unreachable_value;
8094 },
8095 }
8096}
8097
8098/// Parses the string `buf` as a base 10 integer of type `u16`.
8099///
8100/// Unlike std.fmt.parseInt, does not allow the '_' character in `buf`.
8101fn parseBitCount(buf: []const u8) std.fmt.ParseIntError!u16 {
8102 if (buf.len == 0) return error.InvalidCharacter;
8103
8104 var x: u16 = 0;
8105
8106 for (buf) |c| {
8107 const digit = switch (c) {
8108 '0'...'9' => c - '0',
8109 else => return error.InvalidCharacter,
8110 };
8111
8112 if (x != 0) x = try std.math.mul(u16, x, 10);
8113 x = try std.math.add(u16, x, digit);
8114 }
8115
8116 return x;
8117}
8118
8119fn identifier(
8120 gz: *GenZir,
8121 scope: *Scope,
8122 ri: ResultInfo,
8123 ident: Ast.Node.Index,
8124) InnerError!Zir.Inst.Ref {
8125 const astgen = gz.astgen;
8126 const tree = astgen.tree;
8127 const main_tokens = tree.nodes.items(.main_token);
8128
8129 const ident_token = main_tokens[ident];
8130 const ident_name_raw = tree.tokenSlice(ident_token);
8131 if (mem.eql(u8, ident_name_raw, "_")) {
8132 return astgen.failNode(ident, "'_' used as an identifier without @\"_\" syntax", .{});
8133 }
8134
8135 // if not @"" syntax, just use raw token slice
8136 if (ident_name_raw[0] != '@') {
8137 if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
8138 return rvalue(gz, ri, zir_const_ref, ident);
8139 }
8140
8141 if (ident_name_raw.len >= 2) integer: {
8142 const first_c = ident_name_raw[0];
8143 if (first_c == 'i' or first_c == 'u') {
8144 const signedness: std.builtin.Signedness = switch (first_c == 'i') {
8145 true => .signed,
8146 false => .unsigned,
8147 };
8148 if (ident_name_raw.len >= 3 and ident_name_raw[1] == '0') {
8149 return astgen.failNode(
8150 ident,
8151 "primitive integer type '{s}' has leading zero",
8152 .{ident_name_raw},
8153 );
8154 }
8155 const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
8156 error.Overflow => return astgen.failNode(
8157 ident,
8158 "primitive integer type '{s}' exceeds maximum bit width of 65535",
8159 .{ident_name_raw},
8160 ),
8161 error.InvalidCharacter => break :integer,
8162 };
8163 const result = try gz.add(.{
8164 .tag = .int_type,
8165 .data = .{ .int_type = .{
8166 .src_node = gz.nodeIndexToRelative(ident),
8167 .signedness = signedness,
8168 .bit_count = bit_count,
8169 } },
8170 });
8171 return rvalue(gz, ri, result, ident);
8172 }
8173 }
8174 }
8175
8176 // Local variables, including function parameters.
8177 return localVarRef(gz, scope, ri, ident, ident_token);
8178}
8179
8180fn localVarRef(
8181 gz: *GenZir,
8182 scope: *Scope,
8183 ri: ResultInfo,
8184 ident: Ast.Node.Index,
8185 ident_token: Ast.TokenIndex,
8186) InnerError!Zir.Inst.Ref {
8187 const astgen = gz.astgen;
8188 const gpa = astgen.gpa;
8189 const name_str_index = try astgen.identAsString(ident_token);
8190 var s = scope;
8191 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
8192 var num_namespaces_out: u32 = 0;
8193 var capturing_namespace: ?*Scope.Namespace = null;
8194 while (true) switch (s.tag) {
8195 .local_val => {
8196 const local_val = s.cast(Scope.LocalVal).?;
8197
8198 if (local_val.name == name_str_index) {
8199 // Locals cannot shadow anything, so we do not need to look for ambiguous
8200 // references in this case.
8201 if (ri.rl == .discard and ri.ctx == .assignment) {
8202 local_val.discarded = ident_token;
8203 } else {
8204 local_val.used = ident_token;
8205 }
8206
8207 const value_inst = try tunnelThroughClosure(
8208 gz,
8209 ident,
8210 num_namespaces_out,
8211 capturing_namespace,
8212 local_val.inst,
8213 local_val.token_src,
8214 gpa,
8215 );
8216
8217 return rvalueNoCoercePreRef(gz, ri, value_inst, ident);
8218 }
8219 s = local_val.parent;
8220 },
8221 .local_ptr => {
8222 const local_ptr = s.cast(Scope.LocalPtr).?;
8223 if (local_ptr.name == name_str_index) {
8224 if (ri.rl == .discard and ri.ctx == .assignment) {
8225 local_ptr.discarded = ident_token;
8226 } else {
8227 local_ptr.used = ident_token;
8228 }
8229
8230 // Can't close over a runtime variable
8231 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) {
8232 const ident_name = try astgen.identifierTokenString(ident_token);
8233 return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
8234 try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
8235 try astgen.errNoteNode(capturing_namespace.?.node, "crosses namespace boundary here", .{}),
8236 });
8237 }
8238
8239 const ptr_inst = try tunnelThroughClosure(
8240 gz,
8241 ident,
8242 num_namespaces_out,
8243 capturing_namespace,
8244 local_ptr.ptr,
8245 local_ptr.token_src,
8246 gpa,
8247 );
8248
8249 switch (ri.rl) {
8250 .ref, .ref_coerced_ty => {
8251 local_ptr.used_as_lvalue = true;
8252 return ptr_inst;
8253 },
8254 else => {
8255 const loaded = try gz.addUnNode(.load, ptr_inst, ident);
8256 return rvalueNoCoercePreRef(gz, ri, loaded, ident);
8257 },
8258 }
8259 }
8260 s = local_ptr.parent;
8261 },
8262 .gen_zir => s = s.cast(GenZir).?.parent,
8263 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
8264 .namespace, .enum_namespace => {
8265 const ns = s.cast(Scope.Namespace).?;
8266 if (ns.decls.get(name_str_index)) |i| {
8267 if (found_already) |f| {
8268 return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{
8269 try astgen.errNoteNode(f, "declared here", .{}),
8270 try astgen.errNoteNode(i, "also declared here", .{}),
8271 });
8272 }
8273 // We found a match but must continue looking for ambiguous references to decls.
8274 found_already = i;
8275 }
8276 if (s.tag == .namespace) num_namespaces_out += 1;
8277 capturing_namespace = ns;
8278 s = ns.parent;
8279 },
8280 .top => break,
8281 };
8282 if (found_already == null) {
8283 const ident_name = try astgen.identifierTokenString(ident_token);
8284 return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
8285 }
8286
8287 // Decl references happen by name rather than ZIR index so that when unrelated
8288 // decls are modified, ZIR code containing references to them can be unmodified.
8289 switch (ri.rl) {
8290 .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
8291 else => {
8292 const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);
8293 return rvalueNoCoercePreRef(gz, ri, result, ident);
8294 },
8295 }
8296}
8297
8298/// Adds a capture to a namespace, if needed.
8299/// Returns the index of the closure_capture instruction.
8300fn tunnelThroughClosure(
8301 gz: *GenZir,
8302 inner_ref_node: Ast.Node.Index,
8303 num_tunnels: u32,
8304 ns: ?*Scope.Namespace,
8305 value: Zir.Inst.Ref,
8306 token: Ast.TokenIndex,
8307 gpa: Allocator,
8308) !Zir.Inst.Ref {
8309 // For trivial values, we don't need a tunnel.
8310 // Just return the ref.
8311 if (num_tunnels == 0 or value.toIndex() == null) {
8312 return value;
8313 }
8314
8315 // Otherwise we need a tunnel. Check if this namespace
8316 // already has one for this value.
8317 const gop = try ns.?.captures.getOrPut(gpa, value.toIndex().?);
8318 if (!gop.found_existing) {
8319 // Make a new capture for this value but don't add it to the declaring_gz yet
8320 try gz.astgen.instructions.append(gz.astgen.gpa, .{
8321 .tag = .closure_capture,
8322 .data = .{ .un_tok = .{
8323 .operand = value,
8324 .src_tok = ns.?.declaring_gz.?.tokenIndexToRelative(token),
8325 } },
8326 });
8327 gop.value_ptr.* = @enumFromInt(gz.astgen.instructions.len - 1);
8328 }
8329
8330 // Add an instruction to get the value from the closure into
8331 // our current context
8332 return try gz.addInstNode(.closure_get, gop.value_ptr.*, inner_ref_node);
8333}
8334
8335fn stringLiteral(
8336 gz: *GenZir,
8337 ri: ResultInfo,
8338 node: Ast.Node.Index,
8339) InnerError!Zir.Inst.Ref {
8340 const astgen = gz.astgen;
8341 const tree = astgen.tree;
8342 const main_tokens = tree.nodes.items(.main_token);
8343 const str_lit_token = main_tokens[node];
8344 const str = try astgen.strLitAsString(str_lit_token);
8345 const result = try gz.add(.{
8346 .tag = .str,
8347 .data = .{ .str = .{
8348 .start = str.index,
8349 .len = str.len,
8350 } },
8351 });
8352 return rvalue(gz, ri, result, node);
8353}
8354
8355fn multilineStringLiteral(
8356 gz: *GenZir,
8357 ri: ResultInfo,
8358 node: Ast.Node.Index,
8359) InnerError!Zir.Inst.Ref {
8360 const astgen = gz.astgen;
8361 const str = try astgen.strLitNodeAsString(node);
8362 const result = try gz.add(.{
8363 .tag = .str,
8364 .data = .{ .str = .{
8365 .start = str.index,
8366 .len = str.len,
8367 } },
8368 });
8369 return rvalue(gz, ri, result, node);
8370}
8371
8372fn charLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
8373 const astgen = gz.astgen;
8374 const tree = astgen.tree;
8375 const main_tokens = tree.nodes.items(.main_token);
8376 const main_token = main_tokens[node];
8377 const slice = tree.tokenSlice(main_token);
8378
8379 switch (std.zig.parseCharLiteral(slice)) {
8380 .success => |codepoint| {
8381 const result = try gz.addInt(codepoint);
8382 return rvalue(gz, ri, result, node);
8383 },
8384 .failure => |err| return astgen.failWithStrLitError(err, main_token, slice, 0),
8385 }
8386}
8387
8388const Sign = enum { negative, positive };
8389
8390fn numberLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index, source_node: Ast.Node.Index, sign: Sign) InnerError!Zir.Inst.Ref {
8391 const astgen = gz.astgen;
8392 const tree = astgen.tree;
8393 const main_tokens = tree.nodes.items(.main_token);
8394 const num_token = main_tokens[node];
8395 const bytes = tree.tokenSlice(num_token);
8396
8397 const result: Zir.Inst.Ref = switch (std.zig.parseNumberLiteral(bytes)) {
8398 .int => |num| switch (num) {
8399 0 => if (sign == .positive) .zero else return astgen.failTokNotes(
8400 num_token,
8401 "integer literal '-0' is ambiguous",
8402 .{},
8403 &.{
8404 try astgen.errNoteTok(num_token, "use '0' for an integer zero", .{}),
8405 try astgen.errNoteTok(num_token, "use '-0.0' for a floating-point signed zero", .{}),
8406 },
8407 ),
8408 1 => .one,
8409 else => try gz.addInt(num),
8410 },
8411 .big_int => |base| big: {
8412 const gpa = astgen.gpa;
8413 var big_int = try std.math.big.int.Managed.init(gpa);
8414 defer big_int.deinit();
8415 const prefix_offset: usize = if (base == .decimal) 0 else 2;
8416 big_int.setString(@intFromEnum(base), bytes[prefix_offset..]) catch |err| switch (err) {
8417 error.InvalidCharacter => unreachable, // caught in `parseNumberLiteral`
8418 error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above
8419 error.OutOfMemory => return error.OutOfMemory,
8420 };
8421
8422 const limbs = big_int.limbs[0..big_int.len()];
8423 assert(big_int.isPositive());
8424 break :big try gz.addIntBig(limbs);
8425 },
8426 .float => {
8427 const unsigned_float_number = std.fmt.parseFloat(f128, bytes) catch |err| switch (err) {
8428 error.InvalidCharacter => unreachable, // validated by tokenizer
8429 };
8430 const float_number = switch (sign) {
8431 .negative => -unsigned_float_number,
8432 .positive => unsigned_float_number,
8433 };
8434 // If the value fits into a f64 without losing any precision, store it that way.
8435 @setFloatMode(.Strict);
8436 const smaller_float: f64 = @floatCast(float_number);
8437 const bigger_again: f128 = smaller_float;
8438 if (bigger_again == float_number) {
8439 const result = try gz.addFloat(smaller_float);
8440 return rvalue(gz, ri, result, source_node);
8441 }
8442 // We need to use 128 bits. Break the float into 4 u32 values so we can
8443 // put it into the `extra` array.
8444 const int_bits: u128 = @bitCast(float_number);
8445 const result = try gz.addPlNode(.float128, node, Zir.Inst.Float128{
8446 .piece0 = @truncate(int_bits),
8447 .piece1 = @truncate(int_bits >> 32),
8448 .piece2 = @truncate(int_bits >> 64),
8449 .piece3 = @truncate(int_bits >> 96),
8450 });
8451 return rvalue(gz, ri, result, source_node);
8452 },
8453 .failure => |err| return astgen.failWithNumberError(err, num_token, bytes),
8454 };
8455
8456 if (sign == .positive) {
8457 return rvalue(gz, ri, result, source_node);
8458 } else {
8459 const negated = try gz.addUnNode(.negate, result, source_node);
8460 return rvalue(gz, ri, negated, source_node);
8461 }
8462}
8463
8464fn failWithNumberError(astgen: *AstGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) InnerError {
8465 const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null;
8466 switch (err) {
8467 .leading_zero => if (is_float) {
8468 return astgen.failTok(token, "number '{s}' has leading zero", .{bytes});
8469 } else {
8470 return astgen.failTokNotes(token, "number '{s}' has leading zero", .{bytes}, &.{
8471 try astgen.errNoteTok(token, "use '0o' prefix for octal literals", .{}),
8472 });
8473 },
8474 .digit_after_base => return astgen.failTok(token, "expected a digit after base prefix", .{}),
8475 .upper_case_base => |i| return astgen.failOff(token, @intCast(i), "base prefix must be lowercase", .{}),
8476 .invalid_float_base => |i| return astgen.failOff(token, @intCast(i), "invalid base for float literal", .{}),
8477 .repeated_underscore => |i| return astgen.failOff(token, @intCast(i), "repeated digit separator", .{}),
8478 .invalid_underscore_after_special => |i| return astgen.failOff(token, @intCast(i), "expected digit before digit separator", .{}),
8479 .invalid_digit => |info| return astgen.failOff(token, @intCast(info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }),
8480 .invalid_digit_exponent => |i| return astgen.failOff(token, @intCast(i), "invalid digit '{c}' in exponent", .{bytes[i]}),
8481 .duplicate_exponent => |i| return astgen.failOff(token, @intCast(i), "duplicate exponent", .{}),
8482 .exponent_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before exponent", .{}),
8483 .special_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before '{c}'", .{bytes[i]}),
8484 .trailing_special => |i| return astgen.failOff(token, @intCast(i), "expected digit after '{c}'", .{bytes[i - 1]}),
8485 .trailing_underscore => |i| return astgen.failOff(token, @intCast(i), "trailing digit separator", .{}),
8486 .duplicate_period => unreachable, // Validated by tokenizer
8487 .invalid_character => unreachable, // Validated by tokenizer
8488 .invalid_exponent_sign => |i| {
8489 assert(bytes.len >= 2 and bytes[0] == '0' and bytes[1] == 'x'); // Validated by tokenizer
8490 return astgen.failOff(token, @intCast(i), "sign '{c}' cannot follow digit '{c}' in hex base", .{ bytes[i], bytes[i - 1] });
8491 },
8492 }
8493}
8494
8495fn asmExpr(
8496 gz: *GenZir,
8497 scope: *Scope,
8498 ri: ResultInfo,
8499 node: Ast.Node.Index,
8500 full: Ast.full.Asm,
8501) InnerError!Zir.Inst.Ref {
8502 const astgen = gz.astgen;
8503 const tree = astgen.tree;
8504 const main_tokens = tree.nodes.items(.main_token);
8505 const node_datas = tree.nodes.items(.data);
8506 const node_tags = tree.nodes.items(.tag);
8507 const token_tags = tree.tokens.items(.tag);
8508
8509 const TagAndTmpl = struct { tag: Zir.Inst.Extended, tmpl: Zir.NullTerminatedString };
8510 const tag_and_tmpl: TagAndTmpl = switch (node_tags[full.ast.template]) {
8511 .string_literal => .{
8512 .tag = .@"asm",
8513 .tmpl = (try astgen.strLitAsString(main_tokens[full.ast.template])).index,
8514 },
8515 .multiline_string_literal => .{
8516 .tag = .@"asm",
8517 .tmpl = (try astgen.strLitNodeAsString(full.ast.template)).index,
8518 },
8519 else => .{
8520 .tag = .asm_expr,
8521 .tmpl = @enumFromInt(@intFromEnum(try comptimeExpr(gz, scope, .{ .rl = .none }, full.ast.template))),
8522 },
8523 };
8524
8525 // See https://github.com/ziglang/zig/issues/215 and related issues discussing
8526 // possible inline assembly improvements. Until then here is status quo AstGen
8527 // for assembly syntax. It's used by std lib crypto aesni.zig.
8528 const is_container_asm = astgen.fn_block == null;
8529 if (is_container_asm) {
8530 if (full.volatile_token) |t|
8531 return astgen.failTok(t, "volatile is meaningless on global assembly", .{});
8532 if (full.outputs.len != 0 or full.inputs.len != 0 or full.first_clobber != null)
8533 return astgen.failNode(node, "global assembly cannot have inputs, outputs, or clobbers", .{});
8534 } else {
8535 if (full.outputs.len == 0 and full.volatile_token == null) {
8536 return astgen.failNode(node, "assembly expression with no output must be marked volatile", .{});
8537 }
8538 }
8539 if (full.outputs.len > 32) {
8540 return astgen.failNode(full.outputs[32], "too many asm outputs", .{});
8541 }
8542 var outputs_buffer: [32]Zir.Inst.Asm.Output = undefined;
8543 const outputs = outputs_buffer[0..full.outputs.len];
8544
8545 var output_type_bits: u32 = 0;
8546
8547 for (full.outputs, 0..) |output_node, i| {
8548 const symbolic_name = main_tokens[output_node];
8549 const name = try astgen.identAsString(symbolic_name);
8550 const constraint_token = symbolic_name + 2;
8551 const constraint = (try astgen.strLitAsString(constraint_token)).index;
8552 const has_arrow = token_tags[symbolic_name + 4] == .arrow;
8553 if (has_arrow) {
8554 if (output_type_bits != 0) {
8555 return astgen.failNode(output_node, "inline assembly allows up to one output value", .{});
8556 }
8557 output_type_bits |= @as(u32, 1) << @intCast(i);
8558 const out_type_node = node_datas[output_node].lhs;
8559 const out_type_inst = try typeExpr(gz, scope, out_type_node);
8560 outputs[i] = .{
8561 .name = name,
8562 .constraint = constraint,
8563 .operand = out_type_inst,
8564 };
8565 } else {
8566 const ident_token = symbolic_name + 4;
8567 // TODO have a look at #215 and related issues and decide how to
8568 // handle outputs. Do we want this to be identifiers?
8569 // Or maybe we want to force this to be expressions with a pointer type.
8570 outputs[i] = .{
8571 .name = name,
8572 .constraint = constraint,
8573 .operand = try localVarRef(gz, scope, .{ .rl = .ref }, node, ident_token),
8574 };
8575 }
8576 }
8577
8578 if (full.inputs.len > 32) {
8579 return astgen.failNode(full.inputs[32], "too many asm inputs", .{});
8580 }
8581 var inputs_buffer: [32]Zir.Inst.Asm.Input = undefined;
8582 const inputs = inputs_buffer[0..full.inputs.len];
8583
8584 for (full.inputs, 0..) |input_node, i| {
8585 const symbolic_name = main_tokens[input_node];
8586 const name = try astgen.identAsString(symbolic_name);
8587 const constraint_token = symbolic_name + 2;
8588 const constraint = (try astgen.strLitAsString(constraint_token)).index;
8589 const operand = try expr(gz, scope, .{ .rl = .none }, node_datas[input_node].lhs);
8590 inputs[i] = .{
8591 .name = name,
8592 .constraint = constraint,
8593 .operand = operand,
8594 };
8595 }
8596
8597 var clobbers_buffer: [32]u32 = undefined;
8598 var clobber_i: usize = 0;
8599 if (full.first_clobber) |first_clobber| clobbers: {
8600 // asm ("foo" ::: "a", "b")
8601 // asm ("foo" ::: "a", "b",)
8602 var tok_i = first_clobber;
8603 while (true) : (tok_i += 1) {
8604 if (clobber_i >= clobbers_buffer.len) {
8605 return astgen.failTok(tok_i, "too many asm clobbers", .{});
8606 }
8607 clobbers_buffer[clobber_i] = @intFromEnum((try astgen.strLitAsString(tok_i)).index);
8608 clobber_i += 1;
8609 tok_i += 1;
8610 switch (token_tags[tok_i]) {
8611 .r_paren => break :clobbers,
8612 .comma => {
8613 if (token_tags[tok_i + 1] == .r_paren) {
8614 break :clobbers;
8615 } else {
8616 continue;
8617 }
8618 },
8619 else => unreachable,
8620 }
8621 }
8622 }
8623
8624 const result = try gz.addAsm(.{
8625 .tag = tag_and_tmpl.tag,
8626 .node = node,
8627 .asm_source = tag_and_tmpl.tmpl,
8628 .is_volatile = full.volatile_token != null,
8629 .output_type_bits = output_type_bits,
8630 .outputs = outputs,
8631 .inputs = inputs,
8632 .clobbers = clobbers_buffer[0..clobber_i],
8633 });
8634 return rvalue(gz, ri, result, node);
8635}
8636
8637fn as(
8638 gz: *GenZir,
8639 scope: *Scope,
8640 ri: ResultInfo,
8641 node: Ast.Node.Index,
8642 lhs: Ast.Node.Index,
8643 rhs: Ast.Node.Index,
8644) InnerError!Zir.Inst.Ref {
8645 const dest_type = try typeExpr(gz, scope, lhs);
8646 const result = try reachableExpr(gz, scope, .{ .rl = .{ .ty = dest_type } }, rhs, node);
8647 return rvalue(gz, ri, result, node);
8648}
8649
8650fn unionInit(
8651 gz: *GenZir,
8652 scope: *Scope,
8653 ri: ResultInfo,
8654 node: Ast.Node.Index,
8655 params: []const Ast.Node.Index,
8656) InnerError!Zir.Inst.Ref {
8657 const union_type = try typeExpr(gz, scope, params[0]);
8658 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]);
8659 const field_type = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{
8660 .container_type = union_type,
8661 .field_name = field_name,
8662 });
8663 const init = try reachableExpr(gz, scope, .{ .rl = .{ .ty = field_type } }, params[2], node);
8664 const result = try gz.addPlNode(.union_init, node, Zir.Inst.UnionInit{
8665 .union_type = union_type,
8666 .init = init,
8667 .field_name = field_name,
8668 });
8669 return rvalue(gz, ri, result, node);
8670}
8671
8672fn bitCast(
8673 gz: *GenZir,
8674 scope: *Scope,
8675 ri: ResultInfo,
8676 node: Ast.Node.Index,
8677 operand_node: Ast.Node.Index,
8678) InnerError!Zir.Inst.Ref {
8679 const dest_type = try ri.rl.resultTypeForCast(gz, node, "@bitCast");
8680 const operand = try reachableExpr(gz, scope, .{ .rl = .none }, operand_node, node);
8681 const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{
8682 .lhs = dest_type,
8683 .rhs = operand,
8684 });
8685 return rvalue(gz, ri, result, node);
8686}
8687
8688/// Handle one or more nested pointer cast builtins:
8689/// * @ptrCast
8690/// * @alignCast
8691/// * @addrSpaceCast
8692/// * @constCast
8693/// * @volatileCast
8694/// Any sequence of such builtins is treated as a single operation. This allowed
8695/// for sequences like `@ptrCast(@alignCast(ptr))` to work correctly despite the
8696/// intermediate result type being unknown.
8697fn ptrCast(
8698 gz: *GenZir,
8699 scope: *Scope,
8700 ri: ResultInfo,
8701 root_node: Ast.Node.Index,
8702) InnerError!Zir.Inst.Ref {
8703 const astgen = gz.astgen;
8704 const tree = astgen.tree;
8705 const main_tokens = tree.nodes.items(.main_token);
8706 const node_datas = tree.nodes.items(.data);
8707 const node_tags = tree.nodes.items(.tag);
8708
8709 var flags: Zir.Inst.FullPtrCastFlags = .{};
8710
8711 // Note that all pointer cast builtins have one parameter, so we only need
8712 // to handle `builtin_call_two`.
8713 var node = root_node;
8714 while (true) {
8715 switch (node_tags[node]) {
8716 .builtin_call_two, .builtin_call_two_comma => {},
8717 .grouped_expression => {
8718 // Handle the chaining even with redundant parentheses
8719 node = node_datas[node].lhs;
8720 continue;
8721 },
8722 else => break,
8723 }
8724
8725 if (node_datas[node].lhs == 0) break; // 0 args
8726 if (node_datas[node].rhs != 0) break; // 2 args
8727
8728 const builtin_token = main_tokens[node];
8729 const builtin_name = tree.tokenSlice(builtin_token);
8730 const info = BuiltinFn.list.get(builtin_name) orelse break;
8731 if (info.param_count != 1) break;
8732
8733 switch (info.tag) {
8734 else => break,
8735 inline .ptr_cast,
8736 .align_cast,
8737 .addrspace_cast,
8738 .const_cast,
8739 .volatile_cast,
8740 => |tag| {
8741 if (@field(flags, @tagName(tag))) {
8742 return astgen.failNode(node, "redundant {s}", .{builtin_name});
8743 }
8744 @field(flags, @tagName(tag)) = true;
8745 },
8746 }
8747
8748 node = node_datas[node].lhs;
8749 }
8750
8751 const flags_i: u5 = @bitCast(flags);
8752 assert(flags_i != 0);
8753
8754 const ptr_only: Zir.Inst.FullPtrCastFlags = .{ .ptr_cast = true };
8755 if (flags_i == @as(u5, @bitCast(ptr_only))) {
8756 // Special case: simpler representation
8757 return typeCast(gz, scope, ri, root_node, node, .ptr_cast, "@ptrCast");
8758 }
8759
8760 const no_result_ty_flags: Zir.Inst.FullPtrCastFlags = .{
8761 .const_cast = true,
8762 .volatile_cast = true,
8763 };
8764 if ((flags_i & ~@as(u5, @bitCast(no_result_ty_flags))) == 0) {
8765 // Result type not needed
8766 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
8767 const operand = try expr(gz, scope, .{ .rl = .none }, node);
8768 try emitDbgStmt(gz, cursor);
8769 const result = try gz.addExtendedPayloadSmall(.ptr_cast_no_dest, flags_i, Zir.Inst.UnNode{
8770 .node = gz.nodeIndexToRelative(root_node),
8771 .operand = operand,
8772 });
8773 return rvalue(gz, ri, result, root_node);
8774 }
8775
8776 // Full cast including result type
8777
8778 const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
8779 const result_type = try ri.rl.resultTypeForCast(gz, root_node, flags.needResultTypeBuiltinName());
8780 const operand = try expr(gz, scope, .{ .rl = .none }, node);
8781 try emitDbgStmt(gz, cursor);
8782 const result = try gz.addExtendedPayloadSmall(.ptr_cast_full, flags_i, Zir.Inst.BinNode{
8783 .node = gz.nodeIndexToRelative(root_node),
8784 .lhs = result_type,
8785 .rhs = operand,
8786 });
8787 return rvalue(gz, ri, result, root_node);
8788}
8789
8790fn typeOf(
8791 gz: *GenZir,
8792 scope: *Scope,
8793 ri: ResultInfo,
8794 node: Ast.Node.Index,
8795 args: []const Ast.Node.Index,
8796) InnerError!Zir.Inst.Ref {
8797 const astgen = gz.astgen;
8798 if (args.len < 1) {
8799 return astgen.failNode(node, "expected at least 1 argument, found 0", .{});
8800 }
8801 const gpa = astgen.gpa;
8802 if (args.len == 1) {
8803 const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node);
8804
8805 var typeof_scope = gz.makeSubBlock(scope);
8806 typeof_scope.is_comptime = false;
8807 typeof_scope.is_typeof = true;
8808 typeof_scope.c_import = false;
8809 defer typeof_scope.unstack();
8810
8811 const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, args[0], node);
8812 if (!gz.refIsNoReturn(ty_expr)) {
8813 _ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr);
8814 }
8815 try typeof_scope.setBlockBody(typeof_inst);
8816
8817 // typeof_scope unstacked now, can add new instructions to gz
8818 try gz.instructions.append(gpa, typeof_inst);
8819 return rvalue(gz, ri, typeof_inst.toRef(), node);
8820 }
8821 const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len;
8822 const payload_index = try reserveExtra(astgen, payload_size + args.len);
8823 const args_index = payload_index + payload_size;
8824
8825 const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len);
8826
8827 var typeof_scope = gz.makeSubBlock(scope);
8828 typeof_scope.is_comptime = false;
8829
8830 for (args, 0..) |arg, i| {
8831 const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, arg, node);
8832 astgen.extra.items[args_index + i] = @intFromEnum(param_ref);
8833 }
8834 _ = try typeof_scope.addBreak(.break_inline, typeof_inst.toIndex().?, .void_value);
8835
8836 const body = typeof_scope.instructionsSlice();
8837 const body_len = astgen.countBodyLenAfterFixups(body);
8838 astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{
8839 .body_len = @intCast(body_len),
8840 .body_index = @intCast(astgen.extra.items.len),
8841 .src_node = gz.nodeIndexToRelative(node),
8842 });
8843 try astgen.extra.ensureUnusedCapacity(gpa, body_len);
8844 astgen.appendBodyWithFixups(body);
8845 typeof_scope.unstack();
8846
8847 return rvalue(gz, ri, typeof_inst, node);
8848}
8849
8850fn minMax(
8851 gz: *GenZir,
8852 scope: *Scope,
8853 ri: ResultInfo,
8854 node: Ast.Node.Index,
8855 args: []const Ast.Node.Index,
8856 comptime op: enum { min, max },
8857) InnerError!Zir.Inst.Ref {
8858 const astgen = gz.astgen;
8859 if (args.len < 2) {
8860 return astgen.failNode(node, "expected at least 2 arguments, found 0", .{});
8861 }
8862 if (args.len == 2) {
8863 const tag: Zir.Inst.Tag = switch (op) {
8864 .min => .min,
8865 .max => .max,
8866 };
8867 const a = try expr(gz, scope, .{ .rl = .none }, args[0]);
8868 const b = try expr(gz, scope, .{ .rl = .none }, args[1]);
8869 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
8870 .lhs = a,
8871 .rhs = b,
8872 });
8873 return rvalue(gz, ri, result, node);
8874 }
8875 const payload_index = try addExtra(astgen, Zir.Inst.NodeMultiOp{
8876 .src_node = gz.nodeIndexToRelative(node),
8877 });
8878 var extra_index = try reserveExtra(gz.astgen, args.len);
8879 for (args) |arg| {
8880 const arg_ref = try expr(gz, scope, .{ .rl = .none }, arg);
8881 astgen.extra.items[extra_index] = @intFromEnum(arg_ref);
8882 extra_index += 1;
8883 }
8884 const tag: Zir.Inst.Extended = switch (op) {
8885 .min => .min_multi,
8886 .max => .max_multi,
8887 };
8888 const result = try gz.addExtendedMultiOpPayloadIndex(tag, payload_index, args.len);
8889 return rvalue(gz, ri, result, node);
8890}
8891
8892fn builtinCall(
8893 gz: *GenZir,
8894 scope: *Scope,
8895 ri: ResultInfo,
8896 node: Ast.Node.Index,
8897 params: []const Ast.Node.Index,
8898) InnerError!Zir.Inst.Ref {
8899 const astgen = gz.astgen;
8900 const tree = astgen.tree;
8901 const main_tokens = tree.nodes.items(.main_token);
8902
8903 const builtin_token = main_tokens[node];
8904 const builtin_name = tree.tokenSlice(builtin_token);
8905
8906 // We handle the different builtins manually because they have different semantics depending
8907 // on the function. For example, `@as` and others participate in result location semantics,
8908 // and `@cImport` creates a special scope that collects a .c source code text buffer.
8909 // Also, some builtins have a variable number of parameters.
8910
8911 const info = BuiltinFn.list.get(builtin_name) orelse {
8912 return astgen.failNode(node, "invalid builtin function: '{s}'", .{
8913 builtin_name,
8914 });
8915 };
8916 if (info.param_count) |expected| {
8917 if (expected != params.len) {
8918 const s = if (expected == 1) "" else "s";
8919 return astgen.failNode(node, "expected {d} argument{s}, found {d}", .{
8920 expected, s, params.len,
8921 });
8922 }
8923 }
8924
8925 // Check function scope-only builtins
8926
8927 if (astgen.fn_block == null and info.illegal_outside_function)
8928 return astgen.failNode(node, "'{s}' outside function scope", .{builtin_name});
8929
8930 switch (info.tag) {
8931 .import => {
8932 const node_tags = tree.nodes.items(.tag);
8933 const operand_node = params[0];
8934
8935 if (node_tags[operand_node] != .string_literal) {
8936 // Spec reference: https://github.com/ziglang/zig/issues/2206
8937 return astgen.failNode(operand_node, "@import operand must be a string literal", .{});
8938 }
8939 const str_lit_token = main_tokens[operand_node];
8940 const str = try astgen.strLitAsString(str_lit_token);
8941 const str_slice = astgen.string_bytes.items[@intFromEnum(str.index)..][0..str.len];
8942 if (mem.indexOfScalar(u8, str_slice, 0) != null) {
8943 return astgen.failTok(str_lit_token, "import path cannot contain null bytes", .{});
8944 } else if (str.len == 0) {
8945 return astgen.failTok(str_lit_token, "import path cannot be empty", .{});
8946 }
8947 const result = try gz.addStrTok(.import, str.index, str_lit_token);
8948 const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
8949 if (!gop.found_existing) {
8950 gop.value_ptr.* = str_lit_token;
8951 }
8952 return rvalue(gz, ri, result, node);
8953 },
8954 .compile_log => {
8955 const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{
8956 .src_node = gz.nodeIndexToRelative(node),
8957 });
8958 var extra_index = try reserveExtra(gz.astgen, params.len);
8959 for (params) |param| {
8960 const param_ref = try expr(gz, scope, .{ .rl = .none }, param);
8961 astgen.extra.items[extra_index] = @intFromEnum(param_ref);
8962 extra_index += 1;
8963 }
8964 const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len);
8965 return rvalue(gz, ri, result, node);
8966 },
8967 .field => {
8968 if (ri.rl == .ref or ri.rl == .ref_coerced_ty) {
8969 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
8970 .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]),
8971 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]),
8972 });
8973 }
8974 const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
8975 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
8976 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]),
8977 });
8978 return rvalue(gz, ri, result, node);
8979 },
8980
8981 // zig fmt: off
8982 .as => return as( gz, scope, ri, node, params[0], params[1]),
8983 .bit_cast => return bitCast( gz, scope, ri, node, params[0]),
8984 .TypeOf => return typeOf( gz, scope, ri, node, params),
8985 .union_init => return unionInit(gz, scope, ri, node, params),
8986 .c_import => return cImport( gz, scope, node, params[0]),
8987 .min => return minMax( gz, scope, ri, node, params, .min),
8988 .max => return minMax( gz, scope, ri, node, params, .max),
8989 // zig fmt: on
8990
8991 .@"export" => {
8992 const node_tags = tree.nodes.items(.tag);
8993 const node_datas = tree.nodes.items(.data);
8994 // This function causes a Decl to be exported. The first parameter is not an expression,
8995 // but an identifier of the Decl to be exported.
8996 var namespace: Zir.Inst.Ref = .none;
8997 var decl_name: Zir.NullTerminatedString = .empty;
8998 switch (node_tags[params[0]]) {
8999 .identifier => {
9000 const ident_token = main_tokens[params[0]];
9001 if (isPrimitive(tree.tokenSlice(ident_token))) {
9002 return astgen.failTok(ident_token, "unable to export primitive value", .{});
9003 }
9004 decl_name = try astgen.identAsString(ident_token);
9005
9006 var s = scope;
9007 var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
9008 while (true) switch (s.tag) {
9009 .local_val => {
9010 const local_val = s.cast(Scope.LocalVal).?;
9011 if (local_val.name == decl_name) {
9012 local_val.used = ident_token;
9013 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
9014 .operand = local_val.inst,
9015 .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]),
9016 });
9017 return rvalue(gz, ri, .void_value, node);
9018 }
9019 s = local_val.parent;
9020 },
9021 .local_ptr => {
9022 const local_ptr = s.cast(Scope.LocalPtr).?;
9023 if (local_ptr.name == decl_name) {
9024 if (!local_ptr.maybe_comptime)
9025 return astgen.failNode(params[0], "unable to export runtime-known value", .{});
9026 local_ptr.used = ident_token;
9027 const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);
9028 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
9029 .operand = loaded,
9030 .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]),
9031 });
9032 return rvalue(gz, ri, .void_value, node);
9033 }
9034 s = local_ptr.parent;
9035 },
9036 .gen_zir => s = s.cast(GenZir).?.parent,
9037 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
9038 .namespace, .enum_namespace => {
9039 const ns = s.cast(Scope.Namespace).?;
9040 if (ns.decls.get(decl_name)) |i| {
9041 if (found_already) |f| {
9042 return astgen.failNodeNotes(node, "ambiguous reference", .{}, &.{
9043 try astgen.errNoteNode(f, "declared here", .{}),
9044 try astgen.errNoteNode(i, "also declared here", .{}),
9045 });
9046 }
9047 // We found a match but must continue looking for ambiguous references to decls.
9048 found_already = i;
9049 }
9050 s = ns.parent;
9051 },
9052 .top => break,
9053 };
9054 if (found_already == null) {
9055 const ident_name = try astgen.identifierTokenString(ident_token);
9056 return astgen.failNode(params[0], "use of undeclared identifier '{s}'", .{ident_name});
9057 }
9058 },
9059 .field_access => {
9060 const namespace_node = node_datas[params[0]].lhs;
9061 namespace = try typeExpr(gz, scope, namespace_node);
9062 const dot_token = main_tokens[params[0]];
9063 const field_ident = dot_token + 1;
9064 decl_name = try astgen.identAsString(field_ident);
9065 },
9066 else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),
9067 }
9068 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]);
9069 _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
9070 .namespace = namespace,
9071 .decl_name = decl_name,
9072 .options = options,
9073 });
9074 return rvalue(gz, ri, .void_value, node);
9075 },
9076 .@"extern" => {
9077 const type_inst = try typeExpr(gz, scope, params[0]);
9078 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .extern_options_type } }, params[1]);
9079 const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{
9080 .node = gz.nodeIndexToRelative(node),
9081 .lhs = type_inst,
9082 .rhs = options,
9083 });
9084 return rvalue(gz, ri, result, node);
9085 },
9086 .fence => {
9087 const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[0]);
9088 _ = try gz.addExtendedPayload(.fence, Zir.Inst.UnNode{
9089 .node = gz.nodeIndexToRelative(node),
9090 .operand = order,
9091 });
9092 return rvalue(gz, ri, .void_value, node);
9093 },
9094 .set_float_mode => {
9095 const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .float_mode_type } }, params[0]);
9096 _ = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{
9097 .node = gz.nodeIndexToRelative(node),
9098 .operand = order,
9099 });
9100 return rvalue(gz, ri, .void_value, node);
9101 },
9102 .set_align_stack => {
9103 const order = try expr(gz, scope, coerced_align_ri, params[0]);
9104 _ = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{
9105 .node = gz.nodeIndexToRelative(node),
9106 .operand = order,
9107 });
9108 return rvalue(gz, ri, .void_value, node);
9109 },
9110 .set_cold => {
9111 const order = try expr(gz, scope, ri, params[0]);
9112 _ = try gz.addExtendedPayload(.set_cold, Zir.Inst.UnNode{
9113 .node = gz.nodeIndexToRelative(node),
9114 .operand = order,
9115 });
9116 return rvalue(gz, ri, .void_value, node);
9117 },
9118
9119 .src => {
9120 const token_starts = tree.tokens.items(.start);
9121 const node_start = token_starts[tree.firstToken(node)];
9122 astgen.advanceSourceCursor(node_start);
9123 const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.Src{
9124 .node = gz.nodeIndexToRelative(node),
9125 .line = astgen.source_line,
9126 .column = astgen.source_column,
9127 });
9128 return rvalue(gz, ri, result, node);
9129 },
9130
9131 // zig fmt: off
9132 .This => return rvalue(gz, ri, try gz.addNodeExtended(.this, node), node),
9133 .return_address => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr, node), node),
9134 .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),
9135 .frame => return rvalue(gz, ri, try gz.addNodeExtended(.frame, node), node),
9136 .frame_address => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address, node), node),
9137 .breakpoint => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),
9138 .in_comptime => return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime, node), node),
9139
9140 .type_info => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
9141 .size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
9142 .bit_size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .bit_size_of),
9143 .align_of => return simpleUnOpType(gz, scope, ri, node, params[0], .align_of),
9144
9145 .int_from_ptr => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_ptr),
9146 .compile_error => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .compile_error),
9147 .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .set_eval_branch_quota),
9148 .int_from_enum => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_enum),
9149 .int_from_bool => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .int_from_bool),
9150 .embed_file => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .embed_file),
9151 .error_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } }, params[0], .error_name),
9152 .set_runtime_safety => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, params[0], .set_runtime_safety),
9153 .sqrt => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sqrt),
9154 .sin => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .sin),
9155 .cos => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .cos),
9156 .tan => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tan),
9157 .exp => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .exp),
9158 .exp2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .exp2),
9159 .log => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log),
9160 .log2 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log2),
9161 .log10 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .log10),
9162 .abs => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .abs),
9163 .floor => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .floor),
9164 .ceil => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .ceil),
9165 .trunc => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .trunc),
9166 .round => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .round),
9167 .tag_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .tag_name),
9168 .type_name => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .type_name),
9169 .Frame => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_type),
9170 .frame_size => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, params[0], .frame_size),
9171
9172 .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),
9173 .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),
9174 .ptr_from_int => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name),
9175 .enum_from_int => return typeCast(gz, scope, ri, node, params[0], .enum_from_int, builtin_name),
9176 .float_cast => return typeCast(gz, scope, ri, node, params[0], .float_cast, builtin_name),
9177 .int_cast => return typeCast(gz, scope, ri, node, params[0], .int_cast, builtin_name),
9178 .truncate => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name),
9179 // zig fmt: on
9180
9181 .Type => {
9182 const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .type_info_type } }, params[0]);
9183
9184 const gpa = gz.astgen.gpa;
9185
9186 try gz.instructions.ensureUnusedCapacity(gpa, 1);
9187 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
9188
9189 const payload_index = try gz.astgen.addExtra(Zir.Inst.UnNode{
9190 .node = gz.nodeIndexToRelative(node),
9191 .operand = operand,
9192 });
9193 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
9194 gz.astgen.instructions.appendAssumeCapacity(.{
9195 .tag = .extended,
9196 .data = .{ .extended = .{
9197 .opcode = .reify,
9198 .small = @intFromEnum(gz.anon_name_strategy),
9199 .operand = payload_index,
9200 } },
9201 });
9202 gz.instructions.appendAssumeCapacity(new_index);
9203 const result = new_index.toRef();
9204 return rvalue(gz, ri, result, node);
9205 },
9206 .panic => {
9207 try emitDbgNode(gz, node);
9208 return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .panic);
9209 },
9210 .trap => {
9211 try emitDbgNode(gz, node);
9212 _ = try gz.addNode(.trap, node);
9213 return rvalue(gz, ri, .unreachable_value, node);
9214 },
9215 .int_from_error => {
9216 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
9217 const result = try gz.addExtendedPayload(.int_from_error, Zir.Inst.UnNode{
9218 .node = gz.nodeIndexToRelative(node),
9219 .operand = operand,
9220 });
9221 return rvalue(gz, ri, result, node);
9222 },
9223 .error_from_int => {
9224 const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
9225 const result = try gz.addExtendedPayload(.error_from_int, Zir.Inst.UnNode{
9226 .node = gz.nodeIndexToRelative(node),
9227 .operand = operand,
9228 });
9229 return rvalue(gz, ri, result, node);
9230 },
9231 .error_cast => {
9232 try emitDbgNode(gz, node);
9233
9234 const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{
9235 .lhs = try ri.rl.resultTypeForCast(gz, node, "@errorCast"),
9236 .rhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
9237 .node = gz.nodeIndexToRelative(node),
9238 });
9239 return rvalue(gz, ri, result, node);
9240 },
9241 .ptr_cast,
9242 .align_cast,
9243 .addrspace_cast,
9244 .const_cast,
9245 .volatile_cast,
9246 => return ptrCast(gz, scope, ri, node),
9247
9248 // zig fmt: off
9249 .has_decl => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
9250 .has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field),
9251
9252 .clz => return bitBuiltin(gz, scope, ri, node, params[0], .clz),
9253 .ctz => return bitBuiltin(gz, scope, ri, node, params[0], .ctz),
9254 .pop_count => return bitBuiltin(gz, scope, ri, node, params[0], .pop_count),
9255 .byte_swap => return bitBuiltin(gz, scope, ri, node, params[0], .byte_swap),
9256 .bit_reverse => return bitBuiltin(gz, scope, ri, node, params[0], .bit_reverse),
9257
9258 .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),
9259 .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor),
9260 .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),
9261 .mod => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod),
9262 .rem => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem),
9263
9264 .shl_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shl_exact),
9265 .shr_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shr_exact),
9266
9267 .bit_offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .bit_offset_of),
9268 .offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .offset_of),
9269
9270 .c_undef => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_undef),
9271 .c_include => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_include),
9272
9273 .cmpxchg_strong => return cmpxchg(gz, scope, ri, node, params, 1),
9274 .cmpxchg_weak => return cmpxchg(gz, scope, ri, node, params, 0),
9275 // zig fmt: on
9276
9277 .wasm_memory_size => {
9278 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
9279 const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{
9280 .node = gz.nodeIndexToRelative(node),
9281 .operand = operand,
9282 });
9283 return rvalue(gz, ri, result, node);
9284 },
9285 .wasm_memory_grow => {
9286 const index_arg = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
9287 const delta_arg = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[1]);
9288 const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{
9289 .node = gz.nodeIndexToRelative(node),
9290 .lhs = index_arg,
9291 .rhs = delta_arg,
9292 });
9293 return rvalue(gz, ri, result, node);
9294 },
9295 .c_define => {
9296 if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{});
9297 const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0]);
9298 const value = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]);
9299 const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{
9300 .node = gz.nodeIndexToRelative(node),
9301 .lhs = name,
9302 .rhs = value,
9303 });
9304 return rvalue(gz, ri, result, node);
9305 },
9306
9307 .splat => {
9308 const result_type = try ri.rl.resultTypeForCast(gz, node, "@splat");
9309 const elem_type = try gz.addUnNode(.vector_elem_type, result_type, node);
9310 const scalar = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, params[0]);
9311 const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{
9312 .lhs = result_type,
9313 .rhs = scalar,
9314 });
9315 return rvalue(gz, ri, result, node);
9316 },
9317 .reduce => {
9318 const op = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .reduce_op_type } }, params[0]);
9319 const scalar = try expr(gz, scope, .{ .rl = .none }, params[1]);
9320 const result = try gz.addPlNode(.reduce, node, Zir.Inst.Bin{
9321 .lhs = op,
9322 .rhs = scalar,
9323 });
9324 return rvalue(gz, ri, result, node);
9325 },
9326
9327 .add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow),
9328 .sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow),
9329 .mul_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .mul_with_overflow),
9330 .shl_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .shl_with_overflow),
9331
9332 .atomic_load => {
9333 const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{
9334 // zig fmt: off
9335 .elem_type = try typeExpr(gz, scope, params[0]),
9336 .ptr = try expr (gz, scope, .{ .rl = .none }, params[1]),
9337 .ordering = try expr (gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[2]),
9338 // zig fmt: on
9339 });
9340 return rvalue(gz, ri, result, node);
9341 },
9342 .atomic_rmw => {
9343 const int_type = try typeExpr(gz, scope, params[0]);
9344 const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{
9345 // zig fmt: off
9346 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
9347 .operation = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_rmw_op_type } }, params[2]),
9348 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[3]),
9349 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[4]),
9350 // zig fmt: on
9351 });
9352 return rvalue(gz, ri, result, node);
9353 },
9354 .atomic_store => {
9355 const int_type = try typeExpr(gz, scope, params[0]);
9356 _ = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{
9357 // zig fmt: off
9358 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
9359 .operand = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]),
9360 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[3]),
9361 // zig fmt: on
9362 });
9363 return rvalue(gz, ri, .void_value, node);
9364 },
9365 .mul_add => {
9366 const float_type = try typeExpr(gz, scope, params[0]);
9367 const mulend1 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[1]);
9368 const mulend2 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[2]);
9369 const addend = try expr(gz, scope, .{ .rl = .{ .ty = float_type } }, params[3]);
9370 const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{
9371 .mulend1 = mulend1,
9372 .mulend2 = mulend2,
9373 .addend = addend,
9374 });
9375 return rvalue(gz, ri, result, node);
9376 },
9377 .call => {
9378 const modifier = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .call_modifier_type } }, params[0]);
9379 const callee = try expr(gz, scope, .{ .rl = .none }, params[1]);
9380 const args = try expr(gz, scope, .{ .rl = .none }, params[2]);
9381 const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{
9382 .modifier = modifier,
9383 .callee = callee,
9384 .args = args,
9385 .flags = .{
9386 .is_nosuspend = gz.nosuspend_node != 0,
9387 .ensure_result_used = false,
9388 },
9389 });
9390 return rvalue(gz, ri, result, node);
9391 },
9392 .field_parent_ptr => {
9393 const parent_type = try typeExpr(gz, scope, params[0]);
9394 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]);
9395 const result = try gz.addPlNode(.field_parent_ptr, node, Zir.Inst.FieldParentPtr{
9396 .parent_type = parent_type,
9397 .field_name = field_name,
9398 .field_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]),
9399 });
9400 return rvalue(gz, ri, result, node);
9401 },
9402 .memcpy => {
9403 _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{
9404 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
9405 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
9406 });
9407 return rvalue(gz, ri, .void_value, node);
9408 },
9409 .memset => {
9410 const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]);
9411 const lhs_ty = try gz.addUnNode(.typeof, lhs, params[0]);
9412 const elem_ty = try gz.addUnNode(.indexable_ptr_elem_type, lhs_ty, params[0]);
9413 _ = try gz.addPlNode(.memset, node, Zir.Inst.Bin{
9414 .lhs = lhs,
9415 .rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = elem_ty } }, params[1]),
9416 });
9417 return rvalue(gz, ri, .void_value, node);
9418 },
9419 .shuffle => {
9420 const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
9421 .elem_type = try typeExpr(gz, scope, params[0]),
9422 .a = try expr(gz, scope, .{ .rl = .none }, params[1]),
9423 .b = try expr(gz, scope, .{ .rl = .none }, params[2]),
9424 .mask = try comptimeExpr(gz, scope, .{ .rl = .none }, params[3]),
9425 });
9426 return rvalue(gz, ri, result, node);
9427 },
9428 .select => {
9429 const result = try gz.addExtendedPayload(.select, Zir.Inst.Select{
9430 .node = gz.nodeIndexToRelative(node),
9431 .elem_type = try typeExpr(gz, scope, params[0]),
9432 .pred = try expr(gz, scope, .{ .rl = .none }, params[1]),
9433 .a = try expr(gz, scope, .{ .rl = .none }, params[2]),
9434 .b = try expr(gz, scope, .{ .rl = .none }, params[3]),
9435 });
9436 return rvalue(gz, ri, result, node);
9437 },
9438 .async_call => {
9439 const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{
9440 .node = gz.nodeIndexToRelative(node),
9441 .frame_buffer = try expr(gz, scope, .{ .rl = .none }, params[0]),
9442 .result_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
9443 .fn_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]),
9444 .args = try expr(gz, scope, .{ .rl = .none }, params[3]),
9445 });
9446 return rvalue(gz, ri, result, node);
9447 },
9448 .Vector => {
9449 const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{
9450 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]),
9451 .rhs = try typeExpr(gz, scope, params[1]),
9452 });
9453 return rvalue(gz, ri, result, node);
9454 },
9455 .prefetch => {
9456 const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);
9457 const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .prefetch_options_type } }, params[1]);
9458 _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
9459 .node = gz.nodeIndexToRelative(node),
9460 .lhs = ptr,
9461 .rhs = options,
9462 });
9463 return rvalue(gz, ri, .void_value, node);
9464 },
9465 .c_va_arg => {
9466 const result = try gz.addExtendedPayload(.c_va_arg, Zir.Inst.BinNode{
9467 .node = gz.nodeIndexToRelative(node),
9468 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
9469 .rhs = try typeExpr(gz, scope, params[1]),
9470 });
9471 return rvalue(gz, ri, result, node);
9472 },
9473 .c_va_copy => {
9474 const result = try gz.addExtendedPayload(.c_va_copy, Zir.Inst.UnNode{
9475 .node = gz.nodeIndexToRelative(node),
9476 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
9477 });
9478 return rvalue(gz, ri, result, node);
9479 },
9480 .c_va_end => {
9481 const result = try gz.addExtendedPayload(.c_va_end, Zir.Inst.UnNode{
9482 .node = gz.nodeIndexToRelative(node),
9483 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
9484 });
9485 return rvalue(gz, ri, result, node);
9486 },
9487 .c_va_start => {
9488 if (!astgen.fn_var_args) {
9489 return astgen.failNode(node, "'@cVaStart' in a non-variadic function", .{});
9490 }
9491 return rvalue(gz, ri, try gz.addNodeExtended(.c_va_start, node), node);
9492 },
9493
9494 .work_item_id => {
9495 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
9496 const result = try gz.addExtendedPayload(.work_item_id, Zir.Inst.UnNode{
9497 .node = gz.nodeIndexToRelative(node),
9498 .operand = operand,
9499 });
9500 return rvalue(gz, ri, result, node);
9501 },
9502 .work_group_size => {
9503 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
9504 const result = try gz.addExtendedPayload(.work_group_size, Zir.Inst.UnNode{
9505 .node = gz.nodeIndexToRelative(node),
9506 .operand = operand,
9507 });
9508 return rvalue(gz, ri, result, node);
9509 },
9510 .work_group_id => {
9511 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
9512 const result = try gz.addExtendedPayload(.work_group_id, Zir.Inst.UnNode{
9513 .node = gz.nodeIndexToRelative(node),
9514 .operand = operand,
9515 });
9516 return rvalue(gz, ri, result, node);
9517 },
9518 }
9519}
9520
9521fn hasDeclOrField(
9522 gz: *GenZir,
9523 scope: *Scope,
9524 ri: ResultInfo,
9525 node: Ast.Node.Index,
9526 lhs_node: Ast.Node.Index,
9527 rhs_node: Ast.Node.Index,
9528 tag: Zir.Inst.Tag,
9529) InnerError!Zir.Inst.Ref {
9530 const container_type = try typeExpr(gz, scope, lhs_node);
9531 const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node);
9532 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
9533 .lhs = container_type,
9534 .rhs = name,
9535 });
9536 return rvalue(gz, ri, result, node);
9537}
9538
9539fn typeCast(
9540 gz: *GenZir,
9541 scope: *Scope,
9542 ri: ResultInfo,
9543 node: Ast.Node.Index,
9544 operand_node: Ast.Node.Index,
9545 tag: Zir.Inst.Tag,
9546 builtin_name: []const u8,
9547) InnerError!Zir.Inst.Ref {
9548 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
9549 const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);
9550 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
9551
9552 try emitDbgStmt(gz, cursor);
9553 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
9554 .lhs = result_type,
9555 .rhs = operand,
9556 });
9557 return rvalue(gz, ri, result, node);
9558}
9559
9560fn simpleUnOpType(
9561 gz: *GenZir,
9562 scope: *Scope,
9563 ri: ResultInfo,
9564 node: Ast.Node.Index,
9565 operand_node: Ast.Node.Index,
9566 tag: Zir.Inst.Tag,
9567) InnerError!Zir.Inst.Ref {
9568 const operand = try typeExpr(gz, scope, operand_node);
9569 const result = try gz.addUnNode(tag, operand, node);
9570 return rvalue(gz, ri, result, node);
9571}
9572
9573fn simpleUnOp(
9574 gz: *GenZir,
9575 scope: *Scope,
9576 ri: ResultInfo,
9577 node: Ast.Node.Index,
9578 operand_ri: ResultInfo,
9579 operand_node: Ast.Node.Index,
9580 tag: Zir.Inst.Tag,
9581) InnerError!Zir.Inst.Ref {
9582 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
9583 const operand = if (tag == .compile_error)
9584 try comptimeExpr(gz, scope, operand_ri, operand_node)
9585 else
9586 try expr(gz, scope, operand_ri, operand_node);
9587 switch (tag) {
9588 .tag_name, .error_name, .int_from_ptr => try emitDbgStmt(gz, cursor),
9589 else => {},
9590 }
9591 const result = try gz.addUnNode(tag, operand, node);
9592 return rvalue(gz, ri, result, node);
9593}
9594
9595fn negation(
9596 gz: *GenZir,
9597 scope: *Scope,
9598 ri: ResultInfo,
9599 node: Ast.Node.Index,
9600) InnerError!Zir.Inst.Ref {
9601 const astgen = gz.astgen;
9602 const tree = astgen.tree;
9603 const node_tags = tree.nodes.items(.tag);
9604 const node_datas = tree.nodes.items(.data);
9605
9606 // Check for float literal as the sub-expression because we want to preserve
9607 // its negativity rather than having it go through comptime subtraction.
9608 const operand_node = node_datas[node].lhs;
9609 if (node_tags[operand_node] == .number_literal) {
9610 return numberLiteral(gz, ri, operand_node, node, .negative);
9611 }
9612
9613 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
9614 const result = try gz.addUnNode(.negate, operand, node);
9615 return rvalue(gz, ri, result, node);
9616}
9617
9618fn cmpxchg(
9619 gz: *GenZir,
9620 scope: *Scope,
9621 ri: ResultInfo,
9622 node: Ast.Node.Index,
9623 params: []const Ast.Node.Index,
9624 small: u16,
9625) InnerError!Zir.Inst.Ref {
9626 const int_type = try typeExpr(gz, scope, params[0]);
9627 const result = try gz.addExtendedPayloadSmall(.cmpxchg, small, Zir.Inst.Cmpxchg{
9628 // zig fmt: off
9629 .node = gz.nodeIndexToRelative(node),
9630 .ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
9631 .expected_value = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]),
9632 .new_value = try expr(gz, scope, .{ .rl = .{ .coerced_ty = int_type } }, params[3]),
9633 .success_order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[4]),
9634 .failure_order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[5]),
9635 // zig fmt: on
9636 });
9637 return rvalue(gz, ri, result, node);
9638}
9639
9640fn bitBuiltin(
9641 gz: *GenZir,
9642 scope: *Scope,
9643 ri: ResultInfo,
9644 node: Ast.Node.Index,
9645 operand_node: Ast.Node.Index,
9646 tag: Zir.Inst.Tag,
9647) InnerError!Zir.Inst.Ref {
9648 const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
9649 const result = try gz.addUnNode(tag, operand, node);
9650 return rvalue(gz, ri, result, node);
9651}
9652
9653fn divBuiltin(
9654 gz: *GenZir,
9655 scope: *Scope,
9656 ri: ResultInfo,
9657 node: Ast.Node.Index,
9658 lhs_node: Ast.Node.Index,
9659 rhs_node: Ast.Node.Index,
9660 tag: Zir.Inst.Tag,
9661) InnerError!Zir.Inst.Ref {
9662 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
9663 const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
9664 const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node);
9665
9666 try emitDbgStmt(gz, cursor);
9667 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
9668 return rvalue(gz, ri, result, node);
9669}
9670
9671fn simpleCBuiltin(
9672 gz: *GenZir,
9673 scope: *Scope,
9674 ri: ResultInfo,
9675 node: Ast.Node.Index,
9676 operand_node: Ast.Node.Index,
9677 tag: Zir.Inst.Extended,
9678) InnerError!Zir.Inst.Ref {
9679 const name: []const u8 = if (tag == .c_undef) "C undef" else "C include";
9680 if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name});
9681 const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, operand_node);
9682 _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{
9683 .node = gz.nodeIndexToRelative(node),
9684 .operand = operand,
9685 });
9686 return rvalue(gz, ri, .void_value, node);
9687}
9688
9689fn offsetOf(
9690 gz: *GenZir,
9691 scope: *Scope,
9692 ri: ResultInfo,
9693 node: Ast.Node.Index,
9694 lhs_node: Ast.Node.Index,
9695 rhs_node: Ast.Node.Index,
9696 tag: Zir.Inst.Tag,
9697) InnerError!Zir.Inst.Ref {
9698 const type_inst = try typeExpr(gz, scope, lhs_node);
9699 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node);
9700 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
9701 .lhs = type_inst,
9702 .rhs = field_name,
9703 });
9704 return rvalue(gz, ri, result, node);
9705}
9706
9707fn shiftOp(
9708 gz: *GenZir,
9709 scope: *Scope,
9710 ri: ResultInfo,
9711 node: Ast.Node.Index,
9712 lhs_node: Ast.Node.Index,
9713 rhs_node: Ast.Node.Index,
9714 tag: Zir.Inst.Tag,
9715) InnerError!Zir.Inst.Ref {
9716 const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
9717
9718 const cursor = switch (gz.astgen.tree.nodes.items(.tag)[node]) {
9719 .shl, .shr => maybeAdvanceSourceCursorToMainToken(gz, node),
9720 else => undefined,
9721 };
9722
9723 const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node);
9724 const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node);
9725
9726 switch (gz.astgen.tree.nodes.items(.tag)[node]) {
9727 .shl, .shr => try emitDbgStmt(gz, cursor),
9728 else => undefined,
9729 }
9730
9731 const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
9732 .lhs = lhs,
9733 .rhs = rhs,
9734 });
9735 return rvalue(gz, ri, result, node);
9736}
9737
9738fn cImport(
9739 gz: *GenZir,
9740 scope: *Scope,
9741 node: Ast.Node.Index,
9742 body_node: Ast.Node.Index,
9743) InnerError!Zir.Inst.Ref {
9744 const astgen = gz.astgen;
9745 const gpa = astgen.gpa;
9746
9747 if (gz.c_import) return gz.astgen.failNode(node, "cannot nest @cImport", .{});
9748
9749 var block_scope = gz.makeSubBlock(scope);
9750 block_scope.is_comptime = true;
9751 block_scope.c_import = true;
9752 defer block_scope.unstack();
9753
9754 const block_inst = try gz.makeBlockInst(.c_import, node);
9755 const block_result = try expr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node);
9756 _ = try gz.addUnNode(.ensure_result_used, block_result, node);
9757 if (!gz.refIsNoReturn(block_result)) {
9758 _ = try block_scope.addBreak(.break_inline, block_inst, .void_value);
9759 }
9760 try block_scope.setBlockBody(block_inst);
9761 // block_scope unstacked now, can add new instructions to gz
9762 try gz.instructions.append(gpa, block_inst);
9763
9764 return block_inst.toRef();
9765}
9766
9767fn overflowArithmetic(
9768 gz: *GenZir,
9769 scope: *Scope,
9770 ri: ResultInfo,
9771 node: Ast.Node.Index,
9772 params: []const Ast.Node.Index,
9773 tag: Zir.Inst.Extended,
9774) InnerError!Zir.Inst.Ref {
9775 const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]);
9776 const rhs = try expr(gz, scope, .{ .rl = .none }, params[1]);
9777 const result = try gz.addExtendedPayload(tag, Zir.Inst.BinNode{
9778 .node = gz.nodeIndexToRelative(node),
9779 .lhs = lhs,
9780 .rhs = rhs,
9781 });
9782 return rvalue(gz, ri, result, node);
9783}
9784
9785fn callExpr(
9786 gz: *GenZir,
9787 scope: *Scope,
9788 ri: ResultInfo,
9789 node: Ast.Node.Index,
9790 call: Ast.full.Call,
9791) InnerError!Zir.Inst.Ref {
9792 const astgen = gz.astgen;
9793
9794 const callee = try calleeExpr(gz, scope, call.ast.fn_expr);
9795 const modifier: std.builtin.CallModifier = blk: {
9796 if (gz.is_comptime) {
9797 break :blk .compile_time;
9798 }
9799 if (call.async_token != null) {
9800 break :blk .async_kw;
9801 }
9802 if (gz.nosuspend_node != 0) {
9803 break :blk .no_async;
9804 }
9805 break :blk .auto;
9806 };
9807
9808 {
9809 astgen.advanceSourceCursor(astgen.tree.tokens.items(.start)[call.ast.lparen]);
9810 const line = astgen.source_line - gz.decl_line;
9811 const column = astgen.source_column;
9812 // Sema expects a dbg_stmt immediately before call,
9813 try emitDbgStmtForceCurrentIndex(gz, .{ line, column });
9814 }
9815
9816 switch (callee) {
9817 .direct => |obj| assert(obj != .none),
9818 .field => |field| assert(field.obj_ptr != .none),
9819 }
9820 assert(node != 0);
9821
9822 const call_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
9823 const call_inst = call_index.toRef();
9824 try gz.astgen.instructions.append(astgen.gpa, undefined);
9825 try gz.instructions.append(astgen.gpa, call_index);
9826
9827 const scratch_top = astgen.scratch.items.len;
9828 defer astgen.scratch.items.len = scratch_top;
9829
9830 var scratch_index = scratch_top;
9831 try astgen.scratch.resize(astgen.gpa, scratch_top + call.ast.params.len);
9832
9833 for (call.ast.params) |param_node| {
9834 var arg_block = gz.makeSubBlock(scope);
9835 defer arg_block.unstack();
9836
9837 // `call_inst` is reused to provide the param type.
9838 const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node);
9839 _ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node);
9840
9841 const body = arg_block.instructionsSlice();
9842 try astgen.scratch.ensureUnusedCapacity(astgen.gpa, countBodyLenAfterFixups(astgen, body));
9843 appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
9844
9845 astgen.scratch.items[scratch_index] = @intCast(astgen.scratch.items.len - scratch_top);
9846 scratch_index += 1;
9847 }
9848
9849 // If our result location is a try/catch/error-union-if/return, a function argument,
9850 // or an initializer for a `const` variable, the error trace propagates.
9851 // Otherwise, it should always be popped (handled in Sema).
9852 const propagate_error_trace = switch (ri.ctx) {
9853 .error_handling_expr, .@"return", .fn_arg, .const_init => true,
9854 else => false,
9855 };
9856
9857 switch (callee) {
9858 .direct => |callee_obj| {
9859 const payload_index = try addExtra(astgen, Zir.Inst.Call{
9860 .callee = callee_obj,
9861 .flags = .{
9862 .pop_error_return_trace = !propagate_error_trace,
9863 .packed_modifier = @intCast(@intFromEnum(modifier)),
9864 .args_len = @intCast(call.ast.params.len),
9865 },
9866 });
9867 if (call.ast.params.len != 0) {
9868 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
9869 }
9870 gz.astgen.instructions.set(@intFromEnum(call_index), .{
9871 .tag = .call,
9872 .data = .{ .pl_node = .{
9873 .src_node = gz.nodeIndexToRelative(node),
9874 .payload_index = payload_index,
9875 } },
9876 });
9877 },
9878 .field => |callee_field| {
9879 const payload_index = try addExtra(astgen, Zir.Inst.FieldCall{
9880 .obj_ptr = callee_field.obj_ptr,
9881 .field_name_start = callee_field.field_name_start,
9882 .flags = .{
9883 .pop_error_return_trace = !propagate_error_trace,
9884 .packed_modifier = @intCast(@intFromEnum(modifier)),
9885 .args_len = @intCast(call.ast.params.len),
9886 },
9887 });
9888 if (call.ast.params.len != 0) {
9889 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
9890 }
9891 gz.astgen.instructions.set(@intFromEnum(call_index), .{
9892 .tag = .field_call,
9893 .data = .{ .pl_node = .{
9894 .src_node = gz.nodeIndexToRelative(node),
9895 .payload_index = payload_index,
9896 } },
9897 });
9898 },
9899 }
9900 return rvalue(gz, ri, call_inst, node); // TODO function call with result location
9901}
9902
9903const Callee = union(enum) {
9904 field: struct {
9905 /// A *pointer* to the object the field is fetched on, so that we can
9906 /// promote the lvalue to an address if the first parameter requires it.
9907 obj_ptr: Zir.Inst.Ref,
9908 /// Offset into `string_bytes`.
9909 field_name_start: Zir.NullTerminatedString,
9910 },
9911 direct: Zir.Inst.Ref,
9912};
9913
9914/// calleeExpr generates the function part of a call expression (f in f(x)), but
9915/// *not* the callee argument to the @call() builtin. Its purpose is to
9916/// distinguish between standard calls and method call syntax `a.b()`. Thus, if
9917/// the lhs is a field access, we return using the `field` union field;
9918/// otherwise, we use the `direct` union field.
9919fn calleeExpr(
9920 gz: *GenZir,
9921 scope: *Scope,
9922 node: Ast.Node.Index,
9923) InnerError!Callee {
9924 const astgen = gz.astgen;
9925 const tree = astgen.tree;
9926
9927 const tag = tree.nodes.items(.tag)[node];
9928 switch (tag) {
9929 .field_access => {
9930 const main_tokens = tree.nodes.items(.main_token);
9931 const node_datas = tree.nodes.items(.data);
9932 const object_node = node_datas[node].lhs;
9933 const dot_token = main_tokens[node];
9934 const field_ident = dot_token + 1;
9935 const str_index = try astgen.identAsString(field_ident);
9936 // Capture the object by reference so we can promote it to an
9937 // address in Sema if needed.
9938 const lhs = try expr(gz, scope, .{ .rl = .ref }, object_node);
9939
9940 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
9941 try emitDbgStmt(gz, cursor);
9942
9943 return .{ .field = .{
9944 .obj_ptr = lhs,
9945 .field_name_start = str_index,
9946 } };
9947 },
9948 else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) },
9949 }
9950}
9951
9952const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{
9953 .{ "anyerror", .anyerror_type },
9954 .{ "anyframe", .anyframe_type },
9955 .{ "anyopaque", .anyopaque_type },
9956 .{ "bool", .bool_type },
9957 .{ "c_int", .c_int_type },
9958 .{ "c_long", .c_long_type },
9959 .{ "c_longdouble", .c_longdouble_type },
9960 .{ "c_longlong", .c_longlong_type },
9961 .{ "c_char", .c_char_type },
9962 .{ "c_short", .c_short_type },
9963 .{ "c_uint", .c_uint_type },
9964 .{ "c_ulong", .c_ulong_type },
9965 .{ "c_ulonglong", .c_ulonglong_type },
9966 .{ "c_ushort", .c_ushort_type },
9967 .{ "comptime_float", .comptime_float_type },
9968 .{ "comptime_int", .comptime_int_type },
9969 .{ "f128", .f128_type },
9970 .{ "f16", .f16_type },
9971 .{ "f32", .f32_type },
9972 .{ "f64", .f64_type },
9973 .{ "f80", .f80_type },
9974 .{ "false", .bool_false },
9975 .{ "i16", .i16_type },
9976 .{ "i32", .i32_type },
9977 .{ "i64", .i64_type },
9978 .{ "i128", .i128_type },
9979 .{ "i8", .i8_type },
9980 .{ "isize", .isize_type },
9981 .{ "noreturn", .noreturn_type },
9982 .{ "null", .null_value },
9983 .{ "true", .bool_true },
9984 .{ "type", .type_type },
9985 .{ "u16", .u16_type },
9986 .{ "u29", .u29_type },
9987 .{ "u32", .u32_type },
9988 .{ "u64", .u64_type },
9989 .{ "u128", .u128_type },
9990 .{ "u1", .u1_type },
9991 .{ "u8", .u8_type },
9992 .{ "undefined", .undef },
9993 .{ "usize", .usize_type },
9994 .{ "void", .void_type },
9995});
9996
9997comptime {
9998 // These checks ensure that std.zig.primitives stays in sync with the primitive->Zir map.
9999 const primitives = std.zig.primitives;
10000 for (primitive_instrs.kvs) |kv| {
10001 if (!primitives.isPrimitive(kv.key)) {
10002 @compileError("std.zig.isPrimitive() is not aware of Zir instr '" ++ @tagName(kv.value) ++ "'");
10003 }
10004 }
10005 for (primitives.names.kvs) |kv| {
10006 if (primitive_instrs.get(kv.key) == null) {
10007 @compileError("std.zig.primitives entry '" ++ kv.key ++ "' does not have a corresponding Zir instr");
10008 }
10009 }
10010}
10011
10012fn nodeIsTriviallyZero(tree: *const Ast, node: Ast.Node.Index) bool {
10013 const node_tags = tree.nodes.items(.tag);
10014 const main_tokens = tree.nodes.items(.main_token);
10015
10016 switch (node_tags[node]) {
10017 .number_literal => {
10018 const ident = main_tokens[node];
10019 return switch (std.zig.parseNumberLiteral(tree.tokenSlice(ident))) {
10020 .int => |number| switch (number) {
10021 0 => true,
10022 else => false,
10023 },
10024 else => false,
10025 };
10026 },
10027 else => return false,
10028 }
10029}
10030
10031fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool {
10032 const node_tags = tree.nodes.items(.tag);
10033 const node_datas = tree.nodes.items(.data);
10034
10035 var node = start_node;
10036 while (true) {
10037 switch (node_tags[node]) {
10038 // These don't have the opportunity to call any runtime functions.
10039 .error_value,
10040 .identifier,
10041 .@"comptime",
10042 => return false,
10043
10044 // Forward the question to the LHS sub-expression.
10045 .grouped_expression,
10046 .@"try",
10047 .@"nosuspend",
10048 .unwrap_optional,
10049 => node = node_datas[node].lhs,
10050
10051 // Anything that does not eval to an error is guaranteed to pop any
10052 // additions to the error trace, so it effectively does not append.
10053 else => return nodeMayEvalToError(tree, start_node) != .never,
10054 }
10055 }
10056}
10057
10058fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.EvalToError {
10059 const node_tags = tree.nodes.items(.tag);
10060 const node_datas = tree.nodes.items(.data);
10061 const main_tokens = tree.nodes.items(.main_token);
10062 const token_tags = tree.tokens.items(.tag);
10063
10064 var node = start_node;
10065 while (true) {
10066 switch (node_tags[node]) {
10067 .root,
10068 .@"usingnamespace",
10069 .test_decl,
10070 .switch_case,
10071 .switch_case_inline,
10072 .switch_case_one,
10073 .switch_case_inline_one,
10074 .container_field_init,
10075 .container_field_align,
10076 .container_field,
10077 .asm_output,
10078 .asm_input,
10079 => unreachable,
10080
10081 .error_value => return .always,
10082
10083 .@"asm",
10084 .asm_simple,
10085 .identifier,
10086 .field_access,
10087 .deref,
10088 .array_access,
10089 .while_simple,
10090 .while_cont,
10091 .for_simple,
10092 .if_simple,
10093 .@"while",
10094 .@"if",
10095 .@"for",
10096 .@"switch",
10097 .switch_comma,
10098 .call_one,
10099 .call_one_comma,
10100 .async_call_one,
10101 .async_call_one_comma,
10102 .call,
10103 .call_comma,
10104 .async_call,
10105 .async_call_comma,
10106 => return .maybe,
10107
10108 .@"return",
10109 .@"break",
10110 .@"continue",
10111 .bit_not,
10112 .bool_not,
10113 .global_var_decl,
10114 .local_var_decl,
10115 .simple_var_decl,
10116 .aligned_var_decl,
10117 .@"defer",
10118 .@"errdefer",
10119 .address_of,
10120 .optional_type,
10121 .negation,
10122 .negation_wrap,
10123 .@"resume",
10124 .array_type,
10125 .array_type_sentinel,
10126 .ptr_type_aligned,
10127 .ptr_type_sentinel,
10128 .ptr_type,
10129 .ptr_type_bit_range,
10130 .@"suspend",
10131 .fn_proto_simple,
10132 .fn_proto_multi,
10133 .fn_proto_one,
10134 .fn_proto,
10135 .fn_decl,
10136 .anyframe_type,
10137 .anyframe_literal,
10138 .number_literal,
10139 .enum_literal,
10140 .string_literal,
10141 .multiline_string_literal,
10142 .char_literal,
10143 .unreachable_literal,
10144 .error_set_decl,
10145 .container_decl,
10146 .container_decl_trailing,
10147 .container_decl_two,
10148 .container_decl_two_trailing,
10149 .container_decl_arg,
10150 .container_decl_arg_trailing,
10151 .tagged_union,
10152 .tagged_union_trailing,
10153 .tagged_union_two,
10154 .tagged_union_two_trailing,
10155 .tagged_union_enum_tag,
10156 .tagged_union_enum_tag_trailing,
10157 .add,
10158 .add_wrap,
10159 .add_sat,
10160 .array_cat,
10161 .array_mult,
10162 .assign,
10163 .assign_destructure,
10164 .assign_bit_and,
10165 .assign_bit_or,
10166 .assign_shl,
10167 .assign_shl_sat,
10168 .assign_shr,
10169 .assign_bit_xor,
10170 .assign_div,
10171 .assign_sub,
10172 .assign_sub_wrap,
10173 .assign_sub_sat,
10174 .assign_mod,
10175 .assign_add,
10176 .assign_add_wrap,
10177 .assign_add_sat,
10178 .assign_mul,
10179 .assign_mul_wrap,
10180 .assign_mul_sat,
10181 .bang_equal,
10182 .bit_and,
10183 .bit_or,
10184 .shl,
10185 .shl_sat,
10186 .shr,
10187 .bit_xor,
10188 .bool_and,
10189 .bool_or,
10190 .div,
10191 .equal_equal,
10192 .error_union,
10193 .greater_or_equal,
10194 .greater_than,
10195 .less_or_equal,
10196 .less_than,
10197 .merge_error_sets,
10198 .mod,
10199 .mul,
10200 .mul_wrap,
10201 .mul_sat,
10202 .switch_range,
10203 .for_range,
10204 .sub,
10205 .sub_wrap,
10206 .sub_sat,
10207 .slice,
10208 .slice_open,
10209 .slice_sentinel,
10210 .array_init_one,
10211 .array_init_one_comma,
10212 .array_init_dot_two,
10213 .array_init_dot_two_comma,
10214 .array_init_dot,
10215 .array_init_dot_comma,
10216 .array_init,
10217 .array_init_comma,
10218 .struct_init_one,
10219 .struct_init_one_comma,
10220 .struct_init_dot_two,
10221 .struct_init_dot_two_comma,
10222 .struct_init_dot,
10223 .struct_init_dot_comma,
10224 .struct_init,
10225 .struct_init_comma,
10226 => return .never,
10227
10228 // Forward the question to the LHS sub-expression.
10229 .grouped_expression,
10230 .@"try",
10231 .@"await",
10232 .@"comptime",
10233 .@"nosuspend",
10234 .unwrap_optional,
10235 => node = node_datas[node].lhs,
10236
10237 // LHS sub-expression may still be an error under the outer optional or error union
10238 .@"catch",
10239 .@"orelse",
10240 => return .maybe,
10241
10242 .block_two,
10243 .block_two_semicolon,
10244 .block,
10245 .block_semicolon,
10246 => {
10247 const lbrace = main_tokens[node];
10248 if (token_tags[lbrace - 1] == .colon) {
10249 // Labeled blocks may need a memory location to forward
10250 // to their break statements.
10251 return .maybe;
10252 } else {
10253 return .never;
10254 }
10255 },
10256
10257 .builtin_call,
10258 .builtin_call_comma,
10259 .builtin_call_two,
10260 .builtin_call_two_comma,
10261 => {
10262 const builtin_token = main_tokens[node];
10263 const builtin_name = tree.tokenSlice(builtin_token);
10264 // If the builtin is an invalid name, we don't cause an error here; instead
10265 // let it pass, and the error will be "invalid builtin function" later.
10266 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe;
10267 return builtin_info.eval_to_error;
10268 },
10269 }
10270 }
10271}
10272
10273/// Returns `true` if it is known the type expression has more than one possible value;
10274/// `false` otherwise.
10275fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.Index) bool {
10276 const node_tags = tree.nodes.items(.tag);
10277 const node_datas = tree.nodes.items(.data);
10278
10279 var node = start_node;
10280 while (true) {
10281 switch (node_tags[node]) {
10282 .root,
10283 .@"usingnamespace",
10284 .test_decl,
10285 .switch_case,
10286 .switch_case_inline,
10287 .switch_case_one,
10288 .switch_case_inline_one,
10289 .container_field_init,
10290 .container_field_align,
10291 .container_field,
10292 .asm_output,
10293 .asm_input,
10294 .global_var_decl,
10295 .local_var_decl,
10296 .simple_var_decl,
10297 .aligned_var_decl,
10298 => unreachable,
10299
10300 .@"return",
10301 .@"break",
10302 .@"continue",
10303 .bit_not,
10304 .bool_not,
10305 .@"defer",
10306 .@"errdefer",
10307 .address_of,
10308 .negation,
10309 .negation_wrap,
10310 .@"resume",
10311 .array_type,
10312 .@"suspend",
10313 .fn_decl,
10314 .anyframe_literal,
10315 .number_literal,
10316 .enum_literal,
10317 .string_literal,
10318 .multiline_string_literal,
10319 .char_literal,
10320 .unreachable_literal,
10321 .error_set_decl,
10322 .container_decl,
10323 .container_decl_trailing,
10324 .container_decl_two,
10325 .container_decl_two_trailing,
10326 .container_decl_arg,
10327 .container_decl_arg_trailing,
10328 .tagged_union,
10329 .tagged_union_trailing,
10330 .tagged_union_two,
10331 .tagged_union_two_trailing,
10332 .tagged_union_enum_tag,
10333 .tagged_union_enum_tag_trailing,
10334 .@"asm",
10335 .asm_simple,
10336 .add,
10337 .add_wrap,
10338 .add_sat,
10339 .array_cat,
10340 .array_mult,
10341 .assign,
10342 .assign_destructure,
10343 .assign_bit_and,
10344 .assign_bit_or,
10345 .assign_shl,
10346 .assign_shl_sat,
10347 .assign_shr,
10348 .assign_bit_xor,
10349 .assign_div,
10350 .assign_sub,
10351 .assign_sub_wrap,
10352 .assign_sub_sat,
10353 .assign_mod,
10354 .assign_add,
10355 .assign_add_wrap,
10356 .assign_add_sat,
10357 .assign_mul,
10358 .assign_mul_wrap,
10359 .assign_mul_sat,
10360 .bang_equal,
10361 .bit_and,
10362 .bit_or,
10363 .shl,
10364 .shl_sat,
10365 .shr,
10366 .bit_xor,
10367 .bool_and,
10368 .bool_or,
10369 .div,
10370 .equal_equal,
10371 .error_union,
10372 .greater_or_equal,
10373 .greater_than,
10374 .less_or_equal,
10375 .less_than,
10376 .merge_error_sets,
10377 .mod,
10378 .mul,
10379 .mul_wrap,
10380 .mul_sat,
10381 .switch_range,
10382 .for_range,
10383 .field_access,
10384 .sub,
10385 .sub_wrap,
10386 .sub_sat,
10387 .slice,
10388 .slice_open,
10389 .slice_sentinel,
10390 .deref,
10391 .array_access,
10392 .error_value,
10393 .while_simple,
10394 .while_cont,
10395 .for_simple,
10396 .if_simple,
10397 .@"catch",
10398 .@"orelse",
10399 .array_init_one,
10400 .array_init_one_comma,
10401 .array_init_dot_two,
10402 .array_init_dot_two_comma,
10403 .array_init_dot,
10404 .array_init_dot_comma,
10405 .array_init,
10406 .array_init_comma,
10407 .struct_init_one,
10408 .struct_init_one_comma,
10409 .struct_init_dot_two,
10410 .struct_init_dot_two_comma,
10411 .struct_init_dot,
10412 .struct_init_dot_comma,
10413 .struct_init,
10414 .struct_init_comma,
10415 .@"while",
10416 .@"if",
10417 .@"for",
10418 .@"switch",
10419 .switch_comma,
10420 .call_one,
10421 .call_one_comma,
10422 .async_call_one,
10423 .async_call_one_comma,
10424 .call,
10425 .call_comma,
10426 .async_call,
10427 .async_call_comma,
10428 .block_two,
10429 .block_two_semicolon,
10430 .block,
10431 .block_semicolon,
10432 .builtin_call,
10433 .builtin_call_comma,
10434 .builtin_call_two,
10435 .builtin_call_two_comma,
10436 // these are function bodies, not pointers
10437 .fn_proto_simple,
10438 .fn_proto_multi,
10439 .fn_proto_one,
10440 .fn_proto,
10441 => return false,
10442
10443 // Forward the question to the LHS sub-expression.
10444 .grouped_expression,
10445 .@"try",
10446 .@"await",
10447 .@"comptime",
10448 .@"nosuspend",
10449 .unwrap_optional,
10450 => node = node_datas[node].lhs,
10451
10452 .ptr_type_aligned,
10453 .ptr_type_sentinel,
10454 .ptr_type,
10455 .ptr_type_bit_range,
10456 .optional_type,
10457 .anyframe_type,
10458 .array_type_sentinel,
10459 => return true,
10460
10461 .identifier => {
10462 const main_tokens = tree.nodes.items(.main_token);
10463 const ident_bytes = tree.tokenSlice(main_tokens[node]);
10464 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
10465 .anyerror_type,
10466 .anyframe_type,
10467 .anyopaque_type,
10468 .bool_type,
10469 .c_int_type,
10470 .c_long_type,
10471 .c_longdouble_type,
10472 .c_longlong_type,
10473 .c_char_type,
10474 .c_short_type,
10475 .c_uint_type,
10476 .c_ulong_type,
10477 .c_ulonglong_type,
10478 .c_ushort_type,
10479 .comptime_float_type,
10480 .comptime_int_type,
10481 .f16_type,
10482 .f32_type,
10483 .f64_type,
10484 .f80_type,
10485 .f128_type,
10486 .i16_type,
10487 .i32_type,
10488 .i64_type,
10489 .i128_type,
10490 .i8_type,
10491 .isize_type,
10492 .type_type,
10493 .u16_type,
10494 .u29_type,
10495 .u32_type,
10496 .u64_type,
10497 .u128_type,
10498 .u1_type,
10499 .u8_type,
10500 .usize_type,
10501 => return true,
10502
10503 .void_type,
10504 .bool_false,
10505 .bool_true,
10506 .null_value,
10507 .undef,
10508 .noreturn_type,
10509 => return false,
10510
10511 else => unreachable, // that's all the values from `primitives`.
10512 } else {
10513 return false;
10514 }
10515 },
10516 }
10517 }
10518}
10519
10520/// Returns `true` if it is known the expression is a type that cannot be used at runtime;
10521/// `false` otherwise.
10522fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
10523 const node_tags = tree.nodes.items(.tag);
10524 const node_datas = tree.nodes.items(.data);
10525
10526 var node = start_node;
10527 while (true) {
10528 switch (node_tags[node]) {
10529 .root,
10530 .@"usingnamespace",
10531 .test_decl,
10532 .switch_case,
10533 .switch_case_inline,
10534 .switch_case_one,
10535 .switch_case_inline_one,
10536 .container_field_init,
10537 .container_field_align,
10538 .container_field,
10539 .asm_output,
10540 .asm_input,
10541 .global_var_decl,
10542 .local_var_decl,
10543 .simple_var_decl,
10544 .aligned_var_decl,
10545 => unreachable,
10546
10547 .@"return",
10548 .@"break",
10549 .@"continue",
10550 .bit_not,
10551 .bool_not,
10552 .@"defer",
10553 .@"errdefer",
10554 .address_of,
10555 .negation,
10556 .negation_wrap,
10557 .@"resume",
10558 .array_type,
10559 .@"suspend",
10560 .fn_decl,
10561 .anyframe_literal,
10562 .number_literal,
10563 .enum_literal,
10564 .string_literal,
10565 .multiline_string_literal,
10566 .char_literal,
10567 .unreachable_literal,
10568 .error_set_decl,
10569 .container_decl,
10570 .container_decl_trailing,
10571 .container_decl_two,
10572 .container_decl_two_trailing,
10573 .container_decl_arg,
10574 .container_decl_arg_trailing,
10575 .tagged_union,
10576 .tagged_union_trailing,
10577 .tagged_union_two,
10578 .tagged_union_two_trailing,
10579 .tagged_union_enum_tag,
10580 .tagged_union_enum_tag_trailing,
10581 .@"asm",
10582 .asm_simple,
10583 .add,
10584 .add_wrap,
10585 .add_sat,
10586 .array_cat,
10587 .array_mult,
10588 .assign,
10589 .assign_destructure,
10590 .assign_bit_and,
10591 .assign_bit_or,
10592 .assign_shl,
10593 .assign_shl_sat,
10594 .assign_shr,
10595 .assign_bit_xor,
10596 .assign_div,
10597 .assign_sub,
10598 .assign_sub_wrap,
10599 .assign_sub_sat,
10600 .assign_mod,
10601 .assign_add,
10602 .assign_add_wrap,
10603 .assign_add_sat,
10604 .assign_mul,
10605 .assign_mul_wrap,
10606 .assign_mul_sat,
10607 .bang_equal,
10608 .bit_and,
10609 .bit_or,
10610 .shl,
10611 .shl_sat,
10612 .shr,
10613 .bit_xor,
10614 .bool_and,
10615 .bool_or,
10616 .div,
10617 .equal_equal,
10618 .error_union,
10619 .greater_or_equal,
10620 .greater_than,
10621 .less_or_equal,
10622 .less_than,
10623 .merge_error_sets,
10624 .mod,
10625 .mul,
10626 .mul_wrap,
10627 .mul_sat,
10628 .switch_range,
10629 .for_range,
10630 .field_access,
10631 .sub,
10632 .sub_wrap,
10633 .sub_sat,
10634 .slice,
10635 .slice_open,
10636 .slice_sentinel,
10637 .deref,
10638 .array_access,
10639 .error_value,
10640 .while_simple,
10641 .while_cont,
10642 .for_simple,
10643 .if_simple,
10644 .@"catch",
10645 .@"orelse",
10646 .array_init_one,
10647 .array_init_one_comma,
10648 .array_init_dot_two,
10649 .array_init_dot_two_comma,
10650 .array_init_dot,
10651 .array_init_dot_comma,
10652 .array_init,
10653 .array_init_comma,
10654 .struct_init_one,
10655 .struct_init_one_comma,
10656 .struct_init_dot_two,
10657 .struct_init_dot_two_comma,
10658 .struct_init_dot,
10659 .struct_init_dot_comma,
10660 .struct_init,
10661 .struct_init_comma,
10662 .@"while",
10663 .@"if",
10664 .@"for",
10665 .@"switch",
10666 .switch_comma,
10667 .call_one,
10668 .call_one_comma,
10669 .async_call_one,
10670 .async_call_one_comma,
10671 .call,
10672 .call_comma,
10673 .async_call,
10674 .async_call_comma,
10675 .block_two,
10676 .block_two_semicolon,
10677 .block,
10678 .block_semicolon,
10679 .builtin_call,
10680 .builtin_call_comma,
10681 .builtin_call_two,
10682 .builtin_call_two_comma,
10683 .ptr_type_aligned,
10684 .ptr_type_sentinel,
10685 .ptr_type,
10686 .ptr_type_bit_range,
10687 .optional_type,
10688 .anyframe_type,
10689 .array_type_sentinel,
10690 => return false,
10691
10692 // these are function bodies, not pointers
10693 .fn_proto_simple,
10694 .fn_proto_multi,
10695 .fn_proto_one,
10696 .fn_proto,
10697 => return true,
10698
10699 // Forward the question to the LHS sub-expression.
10700 .grouped_expression,
10701 .@"try",
10702 .@"await",
10703 .@"comptime",
10704 .@"nosuspend",
10705 .unwrap_optional,
10706 => node = node_datas[node].lhs,
10707
10708 .identifier => {
10709 const main_tokens = tree.nodes.items(.main_token);
10710 const ident_bytes = tree.tokenSlice(main_tokens[node]);
10711 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
10712 .anyerror_type,
10713 .anyframe_type,
10714 .anyopaque_type,
10715 .bool_type,
10716 .c_int_type,
10717 .c_long_type,
10718 .c_longdouble_type,
10719 .c_longlong_type,
10720 .c_char_type,
10721 .c_short_type,
10722 .c_uint_type,
10723 .c_ulong_type,
10724 .c_ulonglong_type,
10725 .c_ushort_type,
10726 .f16_type,
10727 .f32_type,
10728 .f64_type,
10729 .f80_type,
10730 .f128_type,
10731 .i16_type,
10732 .i32_type,
10733 .i64_type,
10734 .i128_type,
10735 .i8_type,
10736 .isize_type,
10737 .u16_type,
10738 .u29_type,
10739 .u32_type,
10740 .u64_type,
10741 .u128_type,
10742 .u1_type,
10743 .u8_type,
10744 .usize_type,
10745 .void_type,
10746 .bool_false,
10747 .bool_true,
10748 .null_value,
10749 .undef,
10750 .noreturn_type,
10751 => return false,
10752
10753 .comptime_float_type,
10754 .comptime_int_type,
10755 .type_type,
10756 => return true,
10757
10758 else => unreachable, // that's all the values from `primitives`.
10759 } else {
10760 return false;
10761 }
10762 },
10763 }
10764 }
10765}
10766
10767/// Returns `true` if the node uses `gz.anon_name_strategy`.
10768fn nodeUsesAnonNameStrategy(tree: *const Ast, node: Ast.Node.Index) bool {
10769 const node_tags = tree.nodes.items(.tag);
10770 switch (node_tags[node]) {
10771 .container_decl,
10772 .container_decl_trailing,
10773 .container_decl_two,
10774 .container_decl_two_trailing,
10775 .container_decl_arg,
10776 .container_decl_arg_trailing,
10777 .tagged_union,
10778 .tagged_union_trailing,
10779 .tagged_union_two,
10780 .tagged_union_two_trailing,
10781 .tagged_union_enum_tag,
10782 .tagged_union_enum_tag_trailing,
10783 => return true,
10784 .builtin_call_two, .builtin_call_two_comma, .builtin_call, .builtin_call_comma => {
10785 const builtin_token = tree.nodes.items(.main_token)[node];
10786 const builtin_name = tree.tokenSlice(builtin_token);
10787 return std.mem.eql(u8, builtin_name, "@Type");
10788 },
10789 else => return false,
10790 }
10791}
10792
10793/// Applies `rl` semantics to `result`. Expressions which do not do their own handling of
10794/// result locations must call this function on their result.
10795/// As an example, if `ri.rl` is `.ptr`, it will write the result to the pointer.
10796/// If `ri.rl` is `.ty`, it will coerce the result to the type.
10797/// Assumes nothing stacked on `gz`.
10798fn rvalue(
10799 gz: *GenZir,
10800 ri: ResultInfo,
10801 raw_result: Zir.Inst.Ref,
10802 src_node: Ast.Node.Index,
10803) InnerError!Zir.Inst.Ref {
10804 return rvalueInner(gz, ri, raw_result, src_node, true);
10805}
10806
10807/// Like `rvalue`, but refuses to perform coercions before taking references for
10808/// the `ref_coerced_ty` result type. This is used for local variables which do
10809/// not have `alloc`s, because we want variables to have consistent addresses,
10810/// i.e. we want them to act like lvalues.
10811fn rvalueNoCoercePreRef(
10812 gz: *GenZir,
10813 ri: ResultInfo,
10814 raw_result: Zir.Inst.Ref,
10815 src_node: Ast.Node.Index,
10816) InnerError!Zir.Inst.Ref {
10817 return rvalueInner(gz, ri, raw_result, src_node, false);
10818}
10819
10820fn rvalueInner(
10821 gz: *GenZir,
10822 ri: ResultInfo,
10823 raw_result: Zir.Inst.Ref,
10824 src_node: Ast.Node.Index,
10825 allow_coerce_pre_ref: bool,
10826) InnerError!Zir.Inst.Ref {
10827 const result = r: {
10828 if (raw_result.toIndex()) |result_index| {
10829 const zir_tags = gz.astgen.instructions.items(.tag);
10830 const data = gz.astgen.instructions.items(.data)[@intFromEnum(result_index)];
10831 if (zir_tags[@intFromEnum(result_index)].isAlwaysVoid(data)) {
10832 break :r Zir.Inst.Ref.void_value;
10833 }
10834 }
10835 break :r raw_result;
10836 };
10837 if (gz.endsWithNoReturn()) return result;
10838 switch (ri.rl) {
10839 .none, .coerced_ty => return result,
10840 .discard => {
10841 // Emit a compile error for discarding error values.
10842 _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
10843 return .void_value;
10844 },
10845 .ref, .ref_coerced_ty => {
10846 const coerced_result = if (allow_coerce_pre_ref and ri.rl == .ref_coerced_ty) res: {
10847 const ptr_ty = ri.rl.ref_coerced_ty;
10848 break :res try gz.addPlNode(.coerce_ptr_elem_ty, src_node, Zir.Inst.Bin{
10849 .lhs = ptr_ty,
10850 .rhs = result,
10851 });
10852 } else result;
10853 // We need a pointer but we have a value.
10854 // Unfortunately it's not quite as simple as directly emitting a ref
10855 // instruction here because we need subsequent address-of operator on
10856 // const locals to return the same address.
10857 const astgen = gz.astgen;
10858 const tree = astgen.tree;
10859 const src_token = tree.firstToken(src_node);
10860 const result_index = coerced_result.toIndex() orelse
10861 return gz.addUnTok(.ref, coerced_result, src_token);
10862 const zir_tags = gz.astgen.instructions.items(.tag);
10863 if (zir_tags[@intFromEnum(result_index)].isParam() or astgen.isInferred(coerced_result))
10864 return gz.addUnTok(.ref, coerced_result, src_token);
10865 const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index);
10866 if (!gop.found_existing) {
10867 gop.value_ptr.* = try gz.makeUnTok(.ref, coerced_result, src_token);
10868 }
10869 return gop.value_ptr.*.toRef();
10870 },
10871 .ty => |ty_inst| {
10872 // Quickly eliminate some common, unnecessary type coercion.
10873 const as_ty = @as(u64, @intFromEnum(Zir.Inst.Ref.type_type)) << 32;
10874 const as_comptime_int = @as(u64, @intFromEnum(Zir.Inst.Ref.comptime_int_type)) << 32;
10875 const as_bool = @as(u64, @intFromEnum(Zir.Inst.Ref.bool_type)) << 32;
10876 const as_usize = @as(u64, @intFromEnum(Zir.Inst.Ref.usize_type)) << 32;
10877 const as_void = @as(u64, @intFromEnum(Zir.Inst.Ref.void_type)) << 32;
10878 switch ((@as(u64, @intFromEnum(ty_inst)) << 32) | @as(u64, @intFromEnum(result))) {
10879 as_ty | @intFromEnum(Zir.Inst.Ref.u1_type),
10880 as_ty | @intFromEnum(Zir.Inst.Ref.u8_type),
10881 as_ty | @intFromEnum(Zir.Inst.Ref.i8_type),
10882 as_ty | @intFromEnum(Zir.Inst.Ref.u16_type),
10883 as_ty | @intFromEnum(Zir.Inst.Ref.u29_type),
10884 as_ty | @intFromEnum(Zir.Inst.Ref.i16_type),
10885 as_ty | @intFromEnum(Zir.Inst.Ref.u32_type),
10886 as_ty | @intFromEnum(Zir.Inst.Ref.i32_type),
10887 as_ty | @intFromEnum(Zir.Inst.Ref.u64_type),
10888 as_ty | @intFromEnum(Zir.Inst.Ref.i64_type),
10889 as_ty | @intFromEnum(Zir.Inst.Ref.u128_type),
10890 as_ty | @intFromEnum(Zir.Inst.Ref.i128_type),
10891 as_ty | @intFromEnum(Zir.Inst.Ref.usize_type),
10892 as_ty | @intFromEnum(Zir.Inst.Ref.isize_type),
10893 as_ty | @intFromEnum(Zir.Inst.Ref.c_char_type),
10894 as_ty | @intFromEnum(Zir.Inst.Ref.c_short_type),
10895 as_ty | @intFromEnum(Zir.Inst.Ref.c_ushort_type),
10896 as_ty | @intFromEnum(Zir.Inst.Ref.c_int_type),
10897 as_ty | @intFromEnum(Zir.Inst.Ref.c_uint_type),
10898 as_ty | @intFromEnum(Zir.Inst.Ref.c_long_type),
10899 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulong_type),
10900 as_ty | @intFromEnum(Zir.Inst.Ref.c_longlong_type),
10901 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulonglong_type),
10902 as_ty | @intFromEnum(Zir.Inst.Ref.c_longdouble_type),
10903 as_ty | @intFromEnum(Zir.Inst.Ref.f16_type),
10904 as_ty | @intFromEnum(Zir.Inst.Ref.f32_type),
10905 as_ty | @intFromEnum(Zir.Inst.Ref.f64_type),
10906 as_ty | @intFromEnum(Zir.Inst.Ref.f80_type),
10907 as_ty | @intFromEnum(Zir.Inst.Ref.f128_type),
10908 as_ty | @intFromEnum(Zir.Inst.Ref.anyopaque_type),
10909 as_ty | @intFromEnum(Zir.Inst.Ref.bool_type),
10910 as_ty | @intFromEnum(Zir.Inst.Ref.void_type),
10911 as_ty | @intFromEnum(Zir.Inst.Ref.type_type),
10912 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_type),
10913 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_int_type),
10914 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_float_type),
10915 as_ty | @intFromEnum(Zir.Inst.Ref.noreturn_type),
10916 as_ty | @intFromEnum(Zir.Inst.Ref.anyframe_type),
10917 as_ty | @intFromEnum(Zir.Inst.Ref.null_type),
10918 as_ty | @intFromEnum(Zir.Inst.Ref.undefined_type),
10919 as_ty | @intFromEnum(Zir.Inst.Ref.enum_literal_type),
10920 as_ty | @intFromEnum(Zir.Inst.Ref.atomic_order_type),
10921 as_ty | @intFromEnum(Zir.Inst.Ref.atomic_rmw_op_type),
10922 as_ty | @intFromEnum(Zir.Inst.Ref.calling_convention_type),
10923 as_ty | @intFromEnum(Zir.Inst.Ref.address_space_type),
10924 as_ty | @intFromEnum(Zir.Inst.Ref.float_mode_type),
10925 as_ty | @intFromEnum(Zir.Inst.Ref.reduce_op_type),
10926 as_ty | @intFromEnum(Zir.Inst.Ref.call_modifier_type),
10927 as_ty | @intFromEnum(Zir.Inst.Ref.prefetch_options_type),
10928 as_ty | @intFromEnum(Zir.Inst.Ref.export_options_type),
10929 as_ty | @intFromEnum(Zir.Inst.Ref.extern_options_type),
10930 as_ty | @intFromEnum(Zir.Inst.Ref.type_info_type),
10931 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_u8_type),
10932 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_type),
10933 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_sentinel_0_type),
10934 as_ty | @intFromEnum(Zir.Inst.Ref.single_const_pointer_to_comptime_int_type),
10935 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_type),
10936 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_sentinel_0_type),
10937 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_void_error_union_type),
10938 as_ty | @intFromEnum(Zir.Inst.Ref.generic_poison_type),
10939 as_ty | @intFromEnum(Zir.Inst.Ref.empty_struct_type),
10940 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero),
10941 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one),
10942 as_bool | @intFromEnum(Zir.Inst.Ref.bool_true),
10943 as_bool | @intFromEnum(Zir.Inst.Ref.bool_false),
10944 as_usize | @intFromEnum(Zir.Inst.Ref.zero_usize),
10945 as_usize | @intFromEnum(Zir.Inst.Ref.one_usize),
10946 as_void | @intFromEnum(Zir.Inst.Ref.void_value),
10947 => return result, // type of result is already correct
10948
10949 // Need an explicit type coercion instruction.
10950 else => return gz.addPlNode(ri.zirTag(), src_node, Zir.Inst.As{
10951 .dest_type = ty_inst,
10952 .operand = result,
10953 }),
10954 }
10955 },
10956 .ptr => |ptr_res| {
10957 _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{
10958 .lhs = ptr_res.inst,
10959 .rhs = result,
10960 });
10961 return .void_value;
10962 },
10963 .inferred_ptr => |alloc| {
10964 _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{
10965 .lhs = alloc,
10966 .rhs = result,
10967 });
10968 return .void_value;
10969 },
10970 .destructure => |destructure| {
10971 const components = destructure.components;
10972 _ = try gz.addPlNode(.validate_destructure, src_node, Zir.Inst.ValidateDestructure{
10973 .operand = result,
10974 .destructure_node = gz.nodeIndexToRelative(destructure.src_node),
10975 .expect_len = @intCast(components.len),
10976 });
10977 for (components, 0..) |component, i| {
10978 if (component == .discard) continue;
10979 const elem_val = try gz.add(.{
10980 .tag = .elem_val_imm,
10981 .data = .{ .elem_val_imm = .{
10982 .operand = result,
10983 .idx = @intCast(i),
10984 } },
10985 });
10986 switch (component) {
10987 .typed_ptr => |ptr_res| {
10988 _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{
10989 .lhs = ptr_res.inst,
10990 .rhs = elem_val,
10991 });
10992 },
10993 .inferred_ptr => |ptr_inst| {
10994 _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{
10995 .lhs = ptr_inst,
10996 .rhs = elem_val,
10997 });
10998 },
10999 .discard => unreachable,
11000 }
11001 }
11002 return .void_value;
11003 },
11004 }
11005}
11006
11007/// Given an identifier token, obtain the string for it.
11008/// If the token uses @"" syntax, parses as a string, reports errors if applicable,
11009/// and allocates the result within `astgen.arena`.
11010/// Otherwise, returns a reference to the source code bytes directly.
11011/// See also `appendIdentStr` and `parseStrLit`.
11012fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]const u8 {
11013 const tree = astgen.tree;
11014 const token_tags = tree.tokens.items(.tag);
11015 assert(token_tags[token] == .identifier);
11016 const ident_name = tree.tokenSlice(token);
11017 if (!mem.startsWith(u8, ident_name, "@")) {
11018 return ident_name;
11019 }
11020 var buf: ArrayListUnmanaged(u8) = .{};
11021 defer buf.deinit(astgen.gpa);
11022 try astgen.parseStrLit(token, &buf, ident_name, 1);
11023 if (mem.indexOfScalar(u8, buf.items, 0) != null) {
11024 return astgen.failTok(token, "identifier cannot contain null bytes", .{});
11025 } else if (buf.items.len == 0) {
11026 return astgen.failTok(token, "identifier cannot be empty", .{});
11027 }
11028 const duped = try astgen.arena.dupe(u8, buf.items);
11029 return duped;
11030}
11031
11032/// Given an identifier token, obtain the string for it (possibly parsing as a string
11033/// literal if it is @"" syntax), and append the string to `buf`.
11034/// See also `identifierTokenString` and `parseStrLit`.
11035fn appendIdentStr(
11036 astgen: *AstGen,
11037 token: Ast.TokenIndex,
11038 buf: *ArrayListUnmanaged(u8),
11039) InnerError!void {
11040 const tree = astgen.tree;
11041 const token_tags = tree.tokens.items(.tag);
11042 assert(token_tags[token] == .identifier);
11043 const ident_name = tree.tokenSlice(token);
11044 if (!mem.startsWith(u8, ident_name, "@")) {
11045 return buf.appendSlice(astgen.gpa, ident_name);
11046 } else {
11047 const start = buf.items.len;
11048 try astgen.parseStrLit(token, buf, ident_name, 1);
11049 const slice = buf.items[start..];
11050 if (mem.indexOfScalar(u8, slice, 0) != null) {
11051 return astgen.failTok(token, "identifier cannot contain null bytes", .{});
11052 } else if (slice.len == 0) {
11053 return astgen.failTok(token, "identifier cannot be empty", .{});
11054 }
11055 }
11056}
11057
11058/// Appends the result to `buf`.
11059fn parseStrLit(
11060 astgen: *AstGen,
11061 token: Ast.TokenIndex,
11062 buf: *ArrayListUnmanaged(u8),
11063 bytes: []const u8,
11064 offset: u32,
11065) InnerError!void {
11066 const raw_string = bytes[offset..];
11067 var buf_managed = buf.toManaged(astgen.gpa);
11068 const result = std.zig.string_literal.parseWrite(buf_managed.writer(), raw_string);
11069 buf.* = buf_managed.moveToUnmanaged();
11070 switch (try result) {
11071 .success => return,
11072 .failure => |err| return astgen.failWithStrLitError(err, token, bytes, offset),
11073 }
11074}
11075
11076fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError {
11077 const raw_string = bytes[offset..];
11078 switch (err) {
11079 .invalid_escape_character => |bad_index| {
11080 return astgen.failOff(
11081 token,
11082 offset + @as(u32, @intCast(bad_index)),
11083 "invalid escape character: '{c}'",
11084 .{raw_string[bad_index]},
11085 );
11086 },
11087 .expected_hex_digit => |bad_index| {
11088 return astgen.failOff(
11089 token,
11090 offset + @as(u32, @intCast(bad_index)),
11091 "expected hex digit, found '{c}'",
11092 .{raw_string[bad_index]},
11093 );
11094 },
11095 .empty_unicode_escape_sequence => |bad_index| {
11096 return astgen.failOff(
11097 token,
11098 offset + @as(u32, @intCast(bad_index)),
11099 "empty unicode escape sequence",
11100 .{},
11101 );
11102 },
11103 .expected_hex_digit_or_rbrace => |bad_index| {
11104 return astgen.failOff(
11105 token,
11106 offset + @as(u32, @intCast(bad_index)),
11107 "expected hex digit or '}}', found '{c}'",
11108 .{raw_string[bad_index]},
11109 );
11110 },
11111 .invalid_unicode_codepoint => |bad_index| {
11112 return astgen.failOff(
11113 token,
11114 offset + @as(u32, @intCast(bad_index)),
11115 "unicode escape does not correspond to a valid codepoint",
11116 .{},
11117 );
11118 },
11119 .expected_lbrace => |bad_index| {
11120 return astgen.failOff(
11121 token,
11122 offset + @as(u32, @intCast(bad_index)),
11123 "expected '{{', found '{c}",
11124 .{raw_string[bad_index]},
11125 );
11126 },
11127 .expected_rbrace => |bad_index| {
11128 return astgen.failOff(
11129 token,
11130 offset + @as(u32, @intCast(bad_index)),
11131 "expected '}}', found '{c}",
11132 .{raw_string[bad_index]},
11133 );
11134 },
11135 .expected_single_quote => |bad_index| {
11136 return astgen.failOff(
11137 token,
11138 offset + @as(u32, @intCast(bad_index)),
11139 "expected single quote ('), found '{c}",
11140 .{raw_string[bad_index]},
11141 );
11142 },
11143 .invalid_character => |bad_index| {
11144 return astgen.failOff(
11145 token,
11146 offset + @as(u32, @intCast(bad_index)),
11147 "invalid byte in string or character literal: '{c}'",
11148 .{raw_string[bad_index]},
11149 );
11150 },
11151 }
11152}
11153
11154fn failNode(
11155 astgen: *AstGen,
11156 node: Ast.Node.Index,
11157 comptime format: []const u8,
11158 args: anytype,
11159) InnerError {
11160 return astgen.failNodeNotes(node, format, args, &[0]u32{});
11161}
11162
11163fn appendErrorNode(
11164 astgen: *AstGen,
11165 node: Ast.Node.Index,
11166 comptime format: []const u8,
11167 args: anytype,
11168) Allocator.Error!void {
11169 try astgen.appendErrorNodeNotes(node, format, args, &[0]u32{});
11170}
11171
11172fn appendErrorNodeNotes(
11173 astgen: *AstGen,
11174 node: Ast.Node.Index,
11175 comptime format: []const u8,
11176 args: anytype,
11177 notes: []const u32,
11178) Allocator.Error!void {
11179 @setCold(true);
11180 const string_bytes = &astgen.string_bytes;
11181 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
11182 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
11183 const notes_index: u32 = if (notes.len != 0) blk: {
11184 const notes_start = astgen.extra.items.len;
11185 try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len);
11186 astgen.extra.appendAssumeCapacity(@intCast(notes.len));
11187 astgen.extra.appendSliceAssumeCapacity(notes);
11188 break :blk @intCast(notes_start);
11189 } else 0;
11190 try astgen.compile_errors.append(astgen.gpa, .{
11191 .msg = msg,
11192 .node = node,
11193 .token = 0,
11194 .byte_offset = 0,
11195 .notes = notes_index,
11196 });
11197}
11198
11199fn failNodeNotes(
11200 astgen: *AstGen,
11201 node: Ast.Node.Index,
11202 comptime format: []const u8,
11203 args: anytype,
11204 notes: []const u32,
11205) InnerError {
11206 try appendErrorNodeNotes(astgen, node, format, args, notes);
11207 return error.AnalysisFail;
11208}
11209
11210fn failTok(
11211 astgen: *AstGen,
11212 token: Ast.TokenIndex,
11213 comptime format: []const u8,
11214 args: anytype,
11215) InnerError {
11216 return astgen.failTokNotes(token, format, args, &[0]u32{});
11217}
11218
11219fn appendErrorTok(
11220 astgen: *AstGen,
11221 token: Ast.TokenIndex,
11222 comptime format: []const u8,
11223 args: anytype,
11224) !void {
11225 try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{});
11226}
11227
11228fn failTokNotes(
11229 astgen: *AstGen,
11230 token: Ast.TokenIndex,
11231 comptime format: []const u8,
11232 args: anytype,
11233 notes: []const u32,
11234) InnerError {
11235 try appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
11236 return error.AnalysisFail;
11237}
11238
11239fn appendErrorTokNotes(
11240 astgen: *AstGen,
11241 token: Ast.TokenIndex,
11242 comptime format: []const u8,
11243 args: anytype,
11244 notes: []const u32,
11245) !void {
11246 return appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
11247}
11248
11249/// Same as `fail`, except given a token plus an offset from its starting byte
11250/// offset.
11251fn failOff(
11252 astgen: *AstGen,
11253 token: Ast.TokenIndex,
11254 byte_offset: u32,
11255 comptime format: []const u8,
11256 args: anytype,
11257) InnerError {
11258 try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{});
11259 return error.AnalysisFail;
11260}
11261
11262fn appendErrorTokNotesOff(
11263 astgen: *AstGen,
11264 token: Ast.TokenIndex,
11265 byte_offset: u32,
11266 comptime format: []const u8,
11267 args: anytype,
11268 notes: []const u32,
11269) !void {
11270 @setCold(true);
11271 const gpa = astgen.gpa;
11272 const string_bytes = &astgen.string_bytes;
11273 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
11274 try string_bytes.writer(gpa).print(format ++ "\x00", args);
11275 const notes_index: u32 = if (notes.len != 0) blk: {
11276 const notes_start = astgen.extra.items.len;
11277 try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len);
11278 astgen.extra.appendAssumeCapacity(@intCast(notes.len));
11279 astgen.extra.appendSliceAssumeCapacity(notes);
11280 break :blk @intCast(notes_start);
11281 } else 0;
11282 try astgen.compile_errors.append(gpa, .{
11283 .msg = msg,
11284 .node = 0,
11285 .token = token,
11286 .byte_offset = byte_offset,
11287 .notes = notes_index,
11288 });
11289}
11290
11291fn errNoteTok(
11292 astgen: *AstGen,
11293 token: Ast.TokenIndex,
11294 comptime format: []const u8,
11295 args: anytype,
11296) Allocator.Error!u32 {
11297 return errNoteTokOff(astgen, token, 0, format, args);
11298}
11299
11300fn errNoteTokOff(
11301 astgen: *AstGen,
11302 token: Ast.TokenIndex,
11303 byte_offset: u32,
11304 comptime format: []const u8,
11305 args: anytype,
11306) Allocator.Error!u32 {
11307 @setCold(true);
11308 const string_bytes = &astgen.string_bytes;
11309 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
11310 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
11311 return astgen.addExtra(Zir.Inst.CompileErrors.Item{
11312 .msg = msg,
11313 .node = 0,
11314 .token = token,
11315 .byte_offset = byte_offset,
11316 .notes = 0,
11317 });
11318}
11319
11320fn errNoteNode(
11321 astgen: *AstGen,
11322 node: Ast.Node.Index,
11323 comptime format: []const u8,
11324 args: anytype,
11325) Allocator.Error!u32 {
11326 @setCold(true);
11327 const string_bytes = &astgen.string_bytes;
11328 const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
11329 try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
11330 return astgen.addExtra(Zir.Inst.CompileErrors.Item{
11331 .msg = msg,
11332 .node = node,
11333 .token = 0,
11334 .byte_offset = 0,
11335 .notes = 0,
11336 });
11337}
11338
11339fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !Zir.NullTerminatedString {
11340 const gpa = astgen.gpa;
11341 const string_bytes = &astgen.string_bytes;
11342 const str_index: u32 = @intCast(string_bytes.items.len);
11343 try astgen.appendIdentStr(ident_token, string_bytes);
11344 const key: []const u8 = string_bytes.items[str_index..];
11345 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
11346 .bytes = string_bytes,
11347 }, StringIndexContext{
11348 .bytes = string_bytes,
11349 });
11350 if (gop.found_existing) {
11351 string_bytes.shrinkRetainingCapacity(str_index);
11352 return @enumFromInt(gop.key_ptr.*);
11353 } else {
11354 gop.key_ptr.* = str_index;
11355 try string_bytes.append(gpa, 0);
11356 return @enumFromInt(str_index);
11357 }
11358}
11359
11360/// Adds a doc comment block to `string_bytes` by walking backwards from `end_token`.
11361/// `end_token` must point at the first token after the last doc coment line.
11362/// Returns 0 if no doc comment is present.
11363fn docCommentAsString(astgen: *AstGen, end_token: Ast.TokenIndex) !Zir.NullTerminatedString {
11364 if (end_token == 0) return .empty;
11365
11366 const token_tags = astgen.tree.tokens.items(.tag);
11367
11368 var tok = end_token - 1;
11369 while (token_tags[tok] == .doc_comment) {
11370 if (tok == 0) break;
11371 tok -= 1;
11372 } else {
11373 tok += 1;
11374 }
11375
11376 return docCommentAsStringFromFirst(astgen, end_token, tok);
11377}
11378
11379/// end_token must be > the index of the last doc comment.
11380fn docCommentAsStringFromFirst(
11381 astgen: *AstGen,
11382 end_token: Ast.TokenIndex,
11383 start_token: Ast.TokenIndex,
11384) !Zir.NullTerminatedString {
11385 if (start_token == end_token) return .empty;
11386
11387 const gpa = astgen.gpa;
11388 const string_bytes = &astgen.string_bytes;
11389 const str_index: u32 = @intCast(string_bytes.items.len);
11390 const token_starts = astgen.tree.tokens.items(.start);
11391 const token_tags = astgen.tree.tokens.items(.tag);
11392
11393 const total_bytes = token_starts[end_token] - token_starts[start_token];
11394 try string_bytes.ensureUnusedCapacity(gpa, total_bytes);
11395
11396 var current_token = start_token;
11397 while (current_token < end_token) : (current_token += 1) {
11398 switch (token_tags[current_token]) {
11399 .doc_comment => {
11400 const tok_bytes = astgen.tree.tokenSlice(current_token)[3..];
11401 string_bytes.appendSliceAssumeCapacity(tok_bytes);
11402 if (current_token != end_token - 1) {
11403 string_bytes.appendAssumeCapacity('\n');
11404 }
11405 },
11406 else => break,
11407 }
11408 }
11409
11410 const key: []const u8 = string_bytes.items[str_index..];
11411 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
11412 .bytes = string_bytes,
11413 }, StringIndexContext{
11414 .bytes = string_bytes,
11415 });
11416
11417 if (gop.found_existing) {
11418 string_bytes.shrinkRetainingCapacity(str_index);
11419 return @enumFromInt(gop.key_ptr.*);
11420 } else {
11421 gop.key_ptr.* = str_index;
11422 try string_bytes.append(gpa, 0);
11423 return @enumFromInt(str_index);
11424 }
11425}
11426
11427const IndexSlice = struct { index: Zir.NullTerminatedString, len: u32 };
11428
11429fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice {
11430 const gpa = astgen.gpa;
11431 const string_bytes = &astgen.string_bytes;
11432 const str_index: u32 = @intCast(string_bytes.items.len);
11433 const token_bytes = astgen.tree.tokenSlice(str_lit_token);
11434 try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
11435 const key: []const u8 = string_bytes.items[str_index..];
11436 if (std.mem.indexOfScalar(u8, key, 0)) |_| return .{
11437 .index = @enumFromInt(str_index),
11438 .len = @intCast(key.len),
11439 };
11440 const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
11441 .bytes = string_bytes,
11442 }, StringIndexContext{
11443 .bytes = string_bytes,
11444 });
11445 if (gop.found_existing) {
11446 string_bytes.shrinkRetainingCapacity(str_index);
11447 return .{
11448 .index = @enumFromInt(gop.key_ptr.*),
11449 .len = @intCast(key.len),
11450 };
11451 } else {
11452 gop.key_ptr.* = str_index;
11453 // Still need a null byte because we are using the same table
11454 // to lookup null terminated strings, so if we get a match, it has to
11455 // be null terminated for that to work.
11456 try string_bytes.append(gpa, 0);
11457 return .{
11458 .index = @enumFromInt(str_index),
11459 .len = @intCast(key.len),
11460 };
11461 }
11462}
11463
11464fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice {
11465 const tree = astgen.tree;
11466 const node_datas = tree.nodes.items(.data);
11467
11468 const start = node_datas[node].lhs;
11469 const end = node_datas[node].rhs;
11470
11471 const gpa = astgen.gpa;
11472 const string_bytes = &astgen.string_bytes;
11473 const str_index = string_bytes.items.len;
11474
11475 // First line: do not append a newline.
11476 var tok_i = start;
11477 {
11478 const slice = tree.tokenSlice(tok_i);
11479 const carriage_return_ending: usize = if (slice[slice.len - 2] == '\r') 2 else 1;
11480 const line_bytes = slice[2 .. slice.len - carriage_return_ending];
11481 try string_bytes.appendSlice(gpa, line_bytes);
11482 tok_i += 1;
11483 }
11484 // Following lines: each line prepends a newline.
11485 while (tok_i <= end) : (tok_i += 1) {
11486 const slice = tree.tokenSlice(tok_i);
11487 const carriage_return_ending: usize = if (slice[slice.len - 2] == '\r') 2 else 1;
11488 const line_bytes = slice[2 .. slice.len - carriage_return_ending];
11489 try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1);
11490 string_bytes.appendAssumeCapacity('\n');
11491 string_bytes.appendSliceAssumeCapacity(line_bytes);
11492 }
11493 const len = string_bytes.items.len - str_index;
11494 try string_bytes.append(gpa, 0);
11495 return IndexSlice{
11496 .index = @enumFromInt(str_index),
11497 .len = @intCast(len),
11498 };
11499}
11500
11501fn testNameString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !Zir.NullTerminatedString {
11502 const gpa = astgen.gpa;
11503 const string_bytes = &astgen.string_bytes;
11504 const str_index: u32 = @intCast(string_bytes.items.len);
11505 const token_bytes = astgen.tree.tokenSlice(str_lit_token);
11506 try string_bytes.append(gpa, 0); // Indicates this is a test.
11507 try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
11508 const slice = string_bytes.items[str_index + 1 ..];
11509 if (mem.indexOfScalar(u8, slice, 0) != null) {
11510 return astgen.failTok(str_lit_token, "test name cannot contain null bytes", .{});
11511 } else if (slice.len == 0) {
11512 return astgen.failTok(str_lit_token, "empty test name must be omitted", .{});
11513 }
11514 try string_bytes.append(gpa, 0);
11515 return @enumFromInt(str_index);
11516}
11517
11518const Scope = struct {
11519 tag: Tag,
11520
11521 fn cast(base: *Scope, comptime T: type) ?*T {
11522 if (T == Defer) {
11523 switch (base.tag) {
11524 .defer_normal, .defer_error => return @fieldParentPtr(T, "base", base),
11525 else => return null,
11526 }
11527 }
11528 if (T == Namespace) {
11529 switch (base.tag) {
11530 .namespace, .enum_namespace => return @fieldParentPtr(T, "base", base),
11531 else => return null,
11532 }
11533 }
11534 if (base.tag != T.base_tag)
11535 return null;
11536
11537 return @fieldParentPtr(T, "base", base);
11538 }
11539
11540 fn parent(base: *Scope) ?*Scope {
11541 return switch (base.tag) {
11542 .gen_zir => base.cast(GenZir).?.parent,
11543 .local_val => base.cast(LocalVal).?.parent,
11544 .local_ptr => base.cast(LocalPtr).?.parent,
11545 .defer_normal, .defer_error => base.cast(Defer).?.parent,
11546 .namespace, .enum_namespace => base.cast(Namespace).?.parent,
11547 .top => null,
11548 };
11549 }
11550
11551 const Tag = enum {
11552 gen_zir,
11553 local_val,
11554 local_ptr,
11555 defer_normal,
11556 defer_error,
11557 namespace,
11558 enum_namespace,
11559 top,
11560 };
11561
11562 /// The category of identifier. These tag names are user-visible in compile errors.
11563 const IdCat = enum {
11564 @"function parameter",
11565 @"local constant",
11566 @"local variable",
11567 @"switch tag capture",
11568 capture,
11569 };
11570
11571 /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
11572 /// This structure lives as long as the AST generation of the Block
11573 /// node that contains the variable.
11574 const LocalVal = struct {
11575 const base_tag: Tag = .local_val;
11576 base: Scope = Scope{ .tag = base_tag },
11577 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
11578 parent: *Scope,
11579 gen_zir: *GenZir,
11580 inst: Zir.Inst.Ref,
11581 /// Source location of the corresponding variable declaration.
11582 token_src: Ast.TokenIndex,
11583 /// Track the first identifer where it is referenced.
11584 /// 0 means never referenced.
11585 used: Ast.TokenIndex = 0,
11586 /// Track the identifier where it is discarded, like this `_ = foo;`.
11587 /// 0 means never discarded.
11588 discarded: Ast.TokenIndex = 0,
11589 /// String table index.
11590 name: Zir.NullTerminatedString,
11591 id_cat: IdCat,
11592 };
11593
11594 /// This could be a `const` or `var` local. It has a pointer instead of a value.
11595 /// This structure lives as long as the AST generation of the Block
11596 /// node that contains the variable.
11597 const LocalPtr = struct {
11598 const base_tag: Tag = .local_ptr;
11599 base: Scope = Scope{ .tag = base_tag },
11600 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
11601 parent: *Scope,
11602 gen_zir: *GenZir,
11603 ptr: Zir.Inst.Ref,
11604 /// Source location of the corresponding variable declaration.
11605 token_src: Ast.TokenIndex,
11606 /// Track the first identifer where it is referenced.
11607 /// 0 means never referenced.
11608 used: Ast.TokenIndex = 0,
11609 /// Track the identifier where it is discarded, like this `_ = foo;`.
11610 /// 0 means never discarded.
11611 discarded: Ast.TokenIndex = 0,
11612 /// Whether this value is used as an lvalue after inititialization.
11613 /// If not, we know it can be `const`, so will emit a compile error if it is `var`.
11614 used_as_lvalue: bool = false,
11615 /// String table index.
11616 name: Zir.NullTerminatedString,
11617 id_cat: IdCat,
11618 /// true means we find out during Sema whether the value is comptime.
11619 /// false means it is already known at AstGen the value is runtime-known.
11620 maybe_comptime: bool,
11621 };
11622
11623 const Defer = struct {
11624 base: Scope,
11625 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
11626 parent: *Scope,
11627 index: u32,
11628 len: u32,
11629 remapped_err_code: Zir.Inst.OptionalIndex = .none,
11630 };
11631
11632 /// Represents a global scope that has any number of declarations in it.
11633 /// Each declaration has this as the parent scope.
11634 const Namespace = struct {
11635 const base_tag: Tag = .namespace;
11636 base: Scope = Scope{ .tag = base_tag },
11637
11638 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
11639 parent: *Scope,
11640 /// Maps string table index to the source location of declaration,
11641 /// for the purposes of reporting name shadowing compile errors.
11642 decls: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .{},
11643 node: Ast.Node.Index,
11644 inst: Zir.Inst.Index,
11645
11646 /// The astgen scope containing this namespace.
11647 /// Only valid during astgen.
11648 declaring_gz: ?*GenZir,
11649
11650 /// Map from the raw captured value to the instruction
11651 /// ref of the capture for decls in this namespace
11652 captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
11653
11654 fn deinit(self: *Namespace, gpa: Allocator) void {
11655 self.decls.deinit(gpa);
11656 self.captures.deinit(gpa);
11657 self.* = undefined;
11658 }
11659 };
11660
11661 const Top = struct {
11662 const base_tag: Scope.Tag = .top;
11663 base: Scope = Scope{ .tag = base_tag },
11664 };
11665};
11666
11667/// This is a temporary structure; references to it are valid only
11668/// while constructing a `Zir`.
11669const GenZir = struct {
11670 const base_tag: Scope.Tag = .gen_zir;
11671 base: Scope = Scope{ .tag = base_tag },
11672 /// Whether we're already in a scope known to be comptime. This is set
11673 /// whenever we know Sema will analyze the current block with `is_comptime`,
11674 /// for instance when we're within a `struct_decl` or a `block_comptime`.
11675 is_comptime: bool,
11676 /// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime
11677 /// variables is permitted where it is usually not.
11678 is_typeof: bool = false,
11679 /// This is set to true for inline loops; false otherwise.
11680 is_inline: bool = false,
11681 c_import: bool = false,
11682 /// How decls created in this scope should be named.
11683 anon_name_strategy: Zir.Inst.NameStrategy = .anon,
11684 /// The containing decl AST node.
11685 decl_node_index: Ast.Node.Index,
11686 /// The containing decl line index, absolute.
11687 decl_line: u32,
11688 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
11689 parent: *Scope,
11690 /// All `GenZir` scopes for the same ZIR share this.
11691 astgen: *AstGen,
11692 /// Keeps track of the list of instructions in this scope. Possibly shared.
11693 /// Indexes to instructions in `astgen`.
11694 instructions: *ArrayListUnmanaged(Zir.Inst.Index),
11695 /// A sub-block may share its instructions ArrayList with containing GenZir,
11696 /// if use is strictly nested. This saves prior size of list for unstacking.
11697 instructions_top: usize,
11698 label: ?Label = null,
11699 break_block: Zir.Inst.OptionalIndex = .none,
11700 continue_block: Zir.Inst.OptionalIndex = .none,
11701 /// Only valid when setBreakResultInfo is called.
11702 break_result_info: AstGen.ResultInfo = undefined,
11703
11704 suspend_node: Ast.Node.Index = 0,
11705 nosuspend_node: Ast.Node.Index = 0,
11706 /// Set if this GenZir is a defer.
11707 cur_defer_node: Ast.Node.Index = 0,
11708 // Set if this GenZir is a defer or it is inside a defer.
11709 any_defer_node: Ast.Node.Index = 0,
11710
11711 /// Namespace members are lazy. When executing a decl within a namespace,
11712 /// any references to external instructions need to be treated specially.
11713 /// This list tracks those references. See also .closure_capture and .closure_get.
11714 /// Keys are the raw instruction index, values are the closure_capture instruction.
11715 captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
11716
11717 const unstacked_top = std.math.maxInt(usize);
11718 /// Call unstack before adding any new instructions to containing GenZir.
11719 fn unstack(self: *GenZir) void {
11720 if (self.instructions_top != unstacked_top) {
11721 self.instructions.items.len = self.instructions_top;
11722 self.instructions_top = unstacked_top;
11723 }
11724 }
11725
11726 fn isEmpty(self: *const GenZir) bool {
11727 return (self.instructions_top == unstacked_top) or
11728 (self.instructions.items.len == self.instructions_top);
11729 }
11730
11731 fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index {
11732 return if (self.instructions_top == unstacked_top)
11733 &[0]Zir.Inst.Index{}
11734 else
11735 self.instructions.items[self.instructions_top..];
11736 }
11737
11738 fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index {
11739 return if (self.instructions_top == unstacked_top)
11740 &[0]Zir.Inst.Index{}
11741 else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top)
11742 self.instructions.items[self.instructions_top..stacked_gz.instructions_top]
11743 else
11744 self.instructions.items[self.instructions_top..];
11745 }
11746
11747 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
11748 return .{
11749 .is_comptime = gz.is_comptime,
11750 .is_typeof = gz.is_typeof,
11751 .c_import = gz.c_import,
11752 .decl_node_index = gz.decl_node_index,
11753 .decl_line = gz.decl_line,
11754 .parent = scope,
11755 .astgen = gz.astgen,
11756 .suspend_node = gz.suspend_node,
11757 .nosuspend_node = gz.nosuspend_node,
11758 .any_defer_node = gz.any_defer_node,
11759 .instructions = gz.instructions,
11760 .instructions_top = gz.instructions.items.len,
11761 };
11762 }
11763
11764 const Label = struct {
11765 token: Ast.TokenIndex,
11766 block_inst: Zir.Inst.Index,
11767 used: bool = false,
11768 };
11769
11770 /// Assumes nothing stacked on `gz`.
11771 fn endsWithNoReturn(gz: GenZir) bool {
11772 if (gz.isEmpty()) return false;
11773 const tags = gz.astgen.instructions.items(.tag);
11774 const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
11775 return tags[@intFromEnum(last_inst)].isNoReturn();
11776 }
11777
11778 /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`.
11779 fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
11780 if (inst_ref == .unreachable_value) return true;
11781 if (inst_ref.toIndex()) |inst_index| {
11782 return gz.astgen.instructions.items(.tag)[@intFromEnum(inst_index)].isNoReturn();
11783 }
11784 return false;
11785 }
11786
11787 fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) i32 {
11788 return @as(i32, @bitCast(node_index)) - @as(i32, @bitCast(gz.decl_node_index));
11789 }
11790
11791 fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) u32 {
11792 return token - gz.srcToken();
11793 }
11794
11795 fn srcToken(gz: GenZir) Ast.TokenIndex {
11796 return gz.astgen.tree.firstToken(gz.decl_node_index);
11797 }
11798
11799 fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void {
11800 // Depending on whether the result location is a pointer or value, different
11801 // ZIR needs to be generated. In the former case we rely on storing to the
11802 // pointer to communicate the result, and use breakvoid; in the latter case
11803 // the block break instructions will have the result values.
11804 switch (parent_ri.rl) {
11805 .coerced_ty => |ty_inst| {
11806 // Type coercion needs to happen before breaks.
11807 gz.break_result_info = .{ .rl = .{ .ty = ty_inst }, .ctx = parent_ri.ctx };
11808 },
11809 .discard => {
11810 // We don't forward the result context here. This prevents
11811 // "unnecessary discard" errors from being caused by expressions
11812 // far from the actual discard, such as a `break` from a
11813 // discarded block.
11814 gz.break_result_info = .{ .rl = .discard };
11815 },
11816 else => {
11817 gz.break_result_info = parent_ri;
11818 },
11819 }
11820 }
11821
11822 /// Assumes nothing stacked on `gz`. Unstacks `gz`.
11823 fn setBoolBrBody(gz: *GenZir, bool_br: Zir.Inst.Index, bool_br_lhs: Zir.Inst.Ref) !void {
11824 const astgen = gz.astgen;
11825 const gpa = astgen.gpa;
11826 const body = gz.instructionsSlice();
11827 const body_len = astgen.countBodyLenAfterFixups(body);
11828 try astgen.extra.ensureUnusedCapacity(
11829 gpa,
11830 @typeInfo(Zir.Inst.BoolBr).Struct.fields.len + body_len,
11831 );
11832 const zir_datas = astgen.instructions.items(.data);
11833 zir_datas[@intFromEnum(bool_br)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.BoolBr{
11834 .lhs = bool_br_lhs,
11835 .body_len = body_len,
11836 });
11837 astgen.appendBodyWithFixups(body);
11838 gz.unstack();
11839 }
11840
11841 /// Assumes nothing stacked on `gz`. Unstacks `gz`.
11842 fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
11843 const astgen = gz.astgen;
11844 const gpa = astgen.gpa;
11845 const body = gz.instructionsSlice();
11846 const body_len = astgen.countBodyLenAfterFixups(body);
11847 try astgen.extra.ensureUnusedCapacity(
11848 gpa,
11849 @typeInfo(Zir.Inst.Block).Struct.fields.len + body_len,
11850 );
11851 const zir_datas = astgen.instructions.items(.data);
11852 zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
11853 Zir.Inst.Block{ .body_len = body_len },
11854 );
11855 astgen.appendBodyWithFixups(body);
11856 gz.unstack();
11857 }
11858
11859 /// Assumes nothing stacked on `gz`. Unstacks `gz`.
11860 fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
11861 const astgen = gz.astgen;
11862 const gpa = astgen.gpa;
11863 const body = gz.instructionsSlice();
11864 const body_len = astgen.countBodyLenAfterFixups(body);
11865 try astgen.extra.ensureUnusedCapacity(
11866 gpa,
11867 @typeInfo(Zir.Inst.Try).Struct.fields.len + body_len,
11868 );
11869 const zir_datas = astgen.instructions.items(.data);
11870 zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
11871 Zir.Inst.Try{
11872 .operand = operand,
11873 .body_len = body_len,
11874 },
11875 );
11876 astgen.appendBodyWithFixups(body);
11877 gz.unstack();
11878 }
11879
11880 /// Must be called with the following stack set up:
11881 /// * gz (bottom)
11882 /// * align_gz
11883 /// * addrspace_gz
11884 /// * section_gz
11885 /// * cc_gz
11886 /// * ret_gz
11887 /// * body_gz (top)
11888 /// Unstacks all of those except for `gz`.
11889 fn addFunc(gz: *GenZir, args: struct {
11890 src_node: Ast.Node.Index,
11891 lbrace_line: u32 = 0,
11892 lbrace_column: u32 = 0,
11893 param_block: Zir.Inst.Index,
11894
11895 align_gz: ?*GenZir,
11896 addrspace_gz: ?*GenZir,
11897 section_gz: ?*GenZir,
11898 cc_gz: ?*GenZir,
11899 ret_gz: ?*GenZir,
11900 body_gz: ?*GenZir,
11901
11902 align_ref: Zir.Inst.Ref,
11903 addrspace_ref: Zir.Inst.Ref,
11904 section_ref: Zir.Inst.Ref,
11905 cc_ref: Zir.Inst.Ref,
11906 ret_ref: Zir.Inst.Ref,
11907
11908 lib_name: Zir.NullTerminatedString,
11909 noalias_bits: u32,
11910 is_var_args: bool,
11911 is_inferred_error: bool,
11912 is_test: bool,
11913 is_extern: bool,
11914 is_noinline: bool,
11915 }) !Zir.Inst.Ref {
11916 assert(args.src_node != 0);
11917 const astgen = gz.astgen;
11918 const gpa = astgen.gpa;
11919 const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref;
11920 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
11921
11922 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
11923
11924 var body: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
11925 var ret_body: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
11926 var src_locs_and_hash_buffer: [7]u32 = undefined;
11927 var src_locs_and_hash: []u32 = src_locs_and_hash_buffer[0..0];
11928 if (args.body_gz) |body_gz| {
11929 const tree = astgen.tree;
11930 const node_tags = tree.nodes.items(.tag);
11931 const node_datas = tree.nodes.items(.data);
11932 const token_starts = tree.tokens.items(.start);
11933 const fn_decl = args.src_node;
11934 assert(node_tags[fn_decl] == .fn_decl or node_tags[fn_decl] == .test_decl);
11935 const block = node_datas[fn_decl].rhs;
11936 const rbrace_start = token_starts[tree.lastToken(block)];
11937 astgen.advanceSourceCursor(rbrace_start);
11938 const rbrace_line: u32 = @intCast(astgen.source_line - gz.decl_line);
11939 const rbrace_column: u32 = @intCast(astgen.source_column);
11940
11941 const columns = args.lbrace_column | (rbrace_column << 16);
11942
11943 const proto_hash: std.zig.SrcHash = switch (node_tags[fn_decl]) {
11944 .fn_decl => sig_hash: {
11945 const proto_node = node_datas[fn_decl].lhs;
11946 break :sig_hash std.zig.hashSrc(tree.getNodeSource(proto_node));
11947 },
11948 .test_decl => std.zig.hashSrc(""), // tests don't have a prototype
11949 else => unreachable,
11950 };
11951 const proto_hash_arr: [4]u32 = @bitCast(proto_hash);
11952
11953 src_locs_and_hash_buffer = .{
11954 args.lbrace_line,
11955 rbrace_line,
11956 columns,
11957 proto_hash_arr[0],
11958 proto_hash_arr[1],
11959 proto_hash_arr[2],
11960 proto_hash_arr[3],
11961 };
11962 src_locs_and_hash = &src_locs_and_hash_buffer;
11963
11964 body = body_gz.instructionsSlice();
11965 if (args.ret_gz) |ret_gz|
11966 ret_body = ret_gz.instructionsSliceUpto(body_gz);
11967 } else {
11968 if (args.ret_gz) |ret_gz|
11969 ret_body = ret_gz.instructionsSlice();
11970 }
11971 const body_len = astgen.countBodyLenAfterFixups(body);
11972
11973 if (args.cc_ref != .none or args.lib_name != .empty or args.is_var_args or args.is_test or
11974 args.is_extern or args.align_ref != .none or args.section_ref != .none or
11975 args.addrspace_ref != .none or args.noalias_bits != 0 or args.is_noinline)
11976 {
11977 var align_body: []Zir.Inst.Index = &.{};
11978 var addrspace_body: []Zir.Inst.Index = &.{};
11979 var section_body: []Zir.Inst.Index = &.{};
11980 var cc_body: []Zir.Inst.Index = &.{};
11981 if (args.ret_gz != null) {
11982 align_body = args.align_gz.?.instructionsSliceUpto(args.addrspace_gz.?);
11983 addrspace_body = args.addrspace_gz.?.instructionsSliceUpto(args.section_gz.?);
11984 section_body = args.section_gz.?.instructionsSliceUpto(args.cc_gz.?);
11985 cc_body = args.cc_gz.?.instructionsSliceUpto(args.ret_gz.?);
11986 }
11987
11988 try astgen.extra.ensureUnusedCapacity(
11989 gpa,
11990 @typeInfo(Zir.Inst.FuncFancy).Struct.fields.len +
11991 fancyFnExprExtraLen(astgen, align_body, args.align_ref) +
11992 fancyFnExprExtraLen(astgen, addrspace_body, args.addrspace_ref) +
11993 fancyFnExprExtraLen(astgen, section_body, args.section_ref) +
11994 fancyFnExprExtraLen(astgen, cc_body, args.cc_ref) +
11995 fancyFnExprExtraLen(astgen, ret_body, ret_ref) +
11996 body_len + src_locs_and_hash.len +
11997 @intFromBool(args.lib_name != .empty) +
11998 @intFromBool(args.noalias_bits != 0),
11999 );
12000 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
12001 .param_block = args.param_block,
12002 .body_len = body_len,
12003 .bits = .{
12004 .is_var_args = args.is_var_args,
12005 .is_inferred_error = args.is_inferred_error,
12006 .is_test = args.is_test,
12007 .is_extern = args.is_extern,
12008 .is_noinline = args.is_noinline,
12009 .has_lib_name = args.lib_name != .empty,
12010 .has_any_noalias = args.noalias_bits != 0,
12011
12012 .has_align_ref = args.align_ref != .none,
12013 .has_addrspace_ref = args.addrspace_ref != .none,
12014 .has_section_ref = args.section_ref != .none,
12015 .has_cc_ref = args.cc_ref != .none,
12016 .has_ret_ty_ref = ret_ref != .none,
12017
12018 .has_align_body = align_body.len != 0,
12019 .has_addrspace_body = addrspace_body.len != 0,
12020 .has_section_body = section_body.len != 0,
12021 .has_cc_body = cc_body.len != 0,
12022 .has_ret_ty_body = ret_body.len != 0,
12023 },
12024 });
12025 if (args.lib_name != .empty) {
12026 astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name));
12027 }
12028
12029 const zir_datas = astgen.instructions.items(.data);
12030 if (align_body.len != 0) {
12031 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, align_body));
12032 astgen.appendBodyWithFixups(align_body);
12033 const break_extra = zir_datas[@intFromEnum(align_body[align_body.len - 1])].@"break".payload_index;
12034 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
12035 @intFromEnum(new_index);
12036 } else if (args.align_ref != .none) {
12037 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_ref));
12038 }
12039 if (addrspace_body.len != 0) {
12040 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, addrspace_body));
12041 astgen.appendBodyWithFixups(addrspace_body);
12042 const break_extra =
12043 zir_datas[@intFromEnum(addrspace_body[addrspace_body.len - 1])].@"break".payload_index;
12044 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
12045 @intFromEnum(new_index);
12046 } else if (args.addrspace_ref != .none) {
12047 astgen.extra.appendAssumeCapacity(@intFromEnum(args.addrspace_ref));
12048 }
12049 if (section_body.len != 0) {
12050 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, section_body));
12051 astgen.appendBodyWithFixups(section_body);
12052 const break_extra =
12053 zir_datas[@intFromEnum(section_body[section_body.len - 1])].@"break".payload_index;
12054 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
12055 @intFromEnum(new_index);
12056 } else if (args.section_ref != .none) {
12057 astgen.extra.appendAssumeCapacity(@intFromEnum(args.section_ref));
12058 }
12059 if (cc_body.len != 0) {
12060 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, cc_body));
12061 astgen.appendBodyWithFixups(cc_body);
12062 const break_extra = zir_datas[@intFromEnum(cc_body[cc_body.len - 1])].@"break".payload_index;
12063 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
12064 @intFromEnum(new_index);
12065 } else if (args.cc_ref != .none) {
12066 astgen.extra.appendAssumeCapacity(@intFromEnum(args.cc_ref));
12067 }
12068 if (ret_body.len != 0) {
12069 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, ret_body));
12070 astgen.appendBodyWithFixups(ret_body);
12071 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index;
12072 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
12073 @intFromEnum(new_index);
12074 } else if (ret_ref != .none) {
12075 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref));
12076 }
12077
12078 if (args.noalias_bits != 0) {
12079 astgen.extra.appendAssumeCapacity(args.noalias_bits);
12080 }
12081
12082 astgen.appendBodyWithFixups(body);
12083 astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
12084
12085 // Order is important when unstacking.
12086 if (args.body_gz) |body_gz| body_gz.unstack();
12087 if (args.ret_gz != null) {
12088 args.ret_gz.?.unstack();
12089 args.cc_gz.?.unstack();
12090 args.section_gz.?.unstack();
12091 args.addrspace_gz.?.unstack();
12092 args.align_gz.?.unstack();
12093 }
12094
12095 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12096
12097 astgen.instructions.appendAssumeCapacity(.{
12098 .tag = .func_fancy,
12099 .data = .{ .pl_node = .{
12100 .src_node = gz.nodeIndexToRelative(args.src_node),
12101 .payload_index = payload_index,
12102 } },
12103 });
12104 gz.instructions.appendAssumeCapacity(new_index);
12105 return new_index.toRef();
12106 } else {
12107 try astgen.extra.ensureUnusedCapacity(
12108 gpa,
12109 @typeInfo(Zir.Inst.Func).Struct.fields.len + 1 +
12110 fancyFnExprExtraLen(astgen, ret_body, ret_ref) +
12111 body_len + src_locs_and_hash.len,
12112 );
12113
12114 const ret_body_len = if (ret_body.len != 0)
12115 countBodyLenAfterFixups(astgen, ret_body)
12116 else
12117 @intFromBool(ret_ref != .none);
12118
12119 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{
12120 .param_block = args.param_block,
12121 .ret_body_len = ret_body_len,
12122 .body_len = body_len,
12123 });
12124 const zir_datas = astgen.instructions.items(.data);
12125 if (ret_body.len != 0) {
12126 astgen.appendBodyWithFixups(ret_body);
12127
12128 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index;
12129 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
12130 @intFromEnum(new_index);
12131 } else if (ret_ref != .none) {
12132 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref));
12133 }
12134 astgen.appendBodyWithFixups(body);
12135 astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
12136
12137 // Order is important when unstacking.
12138 if (args.body_gz) |body_gz| body_gz.unstack();
12139 if (args.ret_gz) |ret_gz| ret_gz.unstack();
12140 if (args.cc_gz) |cc_gz| cc_gz.unstack();
12141 if (args.section_gz) |section_gz| section_gz.unstack();
12142 if (args.addrspace_gz) |addrspace_gz| addrspace_gz.unstack();
12143 if (args.align_gz) |align_gz| align_gz.unstack();
12144
12145 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12146
12147 const tag: Zir.Inst.Tag = if (args.is_inferred_error) .func_inferred else .func;
12148 astgen.instructions.appendAssumeCapacity(.{
12149 .tag = tag,
12150 .data = .{ .pl_node = .{
12151 .src_node = gz.nodeIndexToRelative(args.src_node),
12152 .payload_index = payload_index,
12153 } },
12154 });
12155 gz.instructions.appendAssumeCapacity(new_index);
12156 return new_index.toRef();
12157 }
12158 }
12159
12160 fn fancyFnExprExtraLen(astgen: *AstGen, body: []Zir.Inst.Index, ref: Zir.Inst.Ref) u32 {
12161 // In the case of non-empty body, there is one for the body length,
12162 // and then one for each instruction.
12163 return countBodyLenAfterFixups(astgen, body) + @intFromBool(ref != .none);
12164 }
12165
12166 fn addVar(gz: *GenZir, args: struct {
12167 align_inst: Zir.Inst.Ref,
12168 lib_name: Zir.NullTerminatedString,
12169 var_type: Zir.Inst.Ref,
12170 init: Zir.Inst.Ref,
12171 is_extern: bool,
12172 is_const: bool,
12173 is_threadlocal: bool,
12174 }) !Zir.Inst.Ref {
12175 const astgen = gz.astgen;
12176 const gpa = astgen.gpa;
12177
12178 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12179 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
12180
12181 try astgen.extra.ensureUnusedCapacity(
12182 gpa,
12183 @typeInfo(Zir.Inst.ExtendedVar).Struct.fields.len +
12184 @intFromBool(args.lib_name != .empty) +
12185 @intFromBool(args.align_inst != .none) +
12186 @intFromBool(args.init != .none),
12187 );
12188 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedVar{
12189 .var_type = args.var_type,
12190 });
12191 if (args.lib_name != .empty) {
12192 astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name));
12193 }
12194 if (args.align_inst != .none) {
12195 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst));
12196 }
12197 if (args.init != .none) {
12198 astgen.extra.appendAssumeCapacity(@intFromEnum(args.init));
12199 }
12200
12201 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12202 astgen.instructions.appendAssumeCapacity(.{
12203 .tag = .extended,
12204 .data = .{ .extended = .{
12205 .opcode = .variable,
12206 .small = @bitCast(Zir.Inst.ExtendedVar.Small{
12207 .has_lib_name = args.lib_name != .empty,
12208 .has_align = args.align_inst != .none,
12209 .has_init = args.init != .none,
12210 .is_extern = args.is_extern,
12211 .is_const = args.is_const,
12212 .is_threadlocal = args.is_threadlocal,
12213 }),
12214 .operand = payload_index,
12215 } },
12216 });
12217 gz.instructions.appendAssumeCapacity(new_index);
12218 return new_index.toRef();
12219 }
12220
12221 fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref {
12222 return gz.add(.{
12223 .tag = .int,
12224 .data = .{ .int = integer },
12225 });
12226 }
12227
12228 fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !Zir.Inst.Ref {
12229 const astgen = gz.astgen;
12230 const gpa = astgen.gpa;
12231 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12232 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
12233 try astgen.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len);
12234
12235 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12236 astgen.instructions.appendAssumeCapacity(.{
12237 .tag = .int_big,
12238 .data = .{ .str = .{
12239 .start = @enumFromInt(astgen.string_bytes.items.len),
12240 .len = @intCast(limbs.len),
12241 } },
12242 });
12243 gz.instructions.appendAssumeCapacity(new_index);
12244 astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
12245 return new_index.toRef();
12246 }
12247
12248 fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
12249 return gz.add(.{
12250 .tag = .float,
12251 .data = .{ .float = number },
12252 });
12253 }
12254
12255 fn addUnNode(
12256 gz: *GenZir,
12257 tag: Zir.Inst.Tag,
12258 operand: Zir.Inst.Ref,
12259 /// Absolute node index. This function does the conversion to offset from Decl.
12260 src_node: Ast.Node.Index,
12261 ) !Zir.Inst.Ref {
12262 assert(operand != .none);
12263 return gz.add(.{
12264 .tag = tag,
12265 .data = .{ .un_node = .{
12266 .operand = operand,
12267 .src_node = gz.nodeIndexToRelative(src_node),
12268 } },
12269 });
12270 }
12271
12272 fn makeUnNode(
12273 gz: *GenZir,
12274 tag: Zir.Inst.Tag,
12275 operand: Zir.Inst.Ref,
12276 /// Absolute node index. This function does the conversion to offset from Decl.
12277 src_node: Ast.Node.Index,
12278 ) !Zir.Inst.Index {
12279 assert(operand != .none);
12280 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12281 try gz.astgen.instructions.append(gz.astgen.gpa, .{
12282 .tag = tag,
12283 .data = .{ .un_node = .{
12284 .operand = operand,
12285 .src_node = gz.nodeIndexToRelative(src_node),
12286 } },
12287 });
12288 return new_index;
12289 }
12290
12291 fn addPlNode(
12292 gz: *GenZir,
12293 tag: Zir.Inst.Tag,
12294 /// Absolute node index. This function does the conversion to offset from Decl.
12295 src_node: Ast.Node.Index,
12296 extra: anytype,
12297 ) !Zir.Inst.Ref {
12298 const gpa = gz.astgen.gpa;
12299 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12300 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
12301
12302 const payload_index = try gz.astgen.addExtra(extra);
12303 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12304 gz.astgen.instructions.appendAssumeCapacity(.{
12305 .tag = tag,
12306 .data = .{ .pl_node = .{
12307 .src_node = gz.nodeIndexToRelative(src_node),
12308 .payload_index = payload_index,
12309 } },
12310 });
12311 gz.instructions.appendAssumeCapacity(new_index);
12312 return new_index.toRef();
12313 }
12314
12315 fn addPlNodePayloadIndex(
12316 gz: *GenZir,
12317 tag: Zir.Inst.Tag,
12318 /// Absolute node index. This function does the conversion to offset from Decl.
12319 src_node: Ast.Node.Index,
12320 payload_index: u32,
12321 ) !Zir.Inst.Ref {
12322 return try gz.add(.{
12323 .tag = tag,
12324 .data = .{ .pl_node = .{
12325 .src_node = gz.nodeIndexToRelative(src_node),
12326 .payload_index = payload_index,
12327 } },
12328 });
12329 }
12330
12331 /// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`.
12332 fn addParam(
12333 gz: *GenZir,
12334 param_gz: *GenZir,
12335 tag: Zir.Inst.Tag,
12336 /// Absolute token index. This function does the conversion to Decl offset.
12337 abs_tok_index: Ast.TokenIndex,
12338 name: Zir.NullTerminatedString,
12339 first_doc_comment: ?Ast.TokenIndex,
12340 ) !Zir.Inst.Index {
12341 const gpa = gz.astgen.gpa;
12342 const param_body = param_gz.instructionsSlice();
12343 const body_len = gz.astgen.countBodyLenAfterFixups(param_body);
12344 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
12345 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).Struct.fields.len + body_len);
12346
12347 const doc_comment_index = if (first_doc_comment) |first|
12348 try gz.astgen.docCommentAsStringFromFirst(abs_tok_index, first)
12349 else
12350 .empty;
12351
12352 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{
12353 .name = name,
12354 .doc_comment = doc_comment_index,
12355 .body_len = @intCast(body_len),
12356 });
12357 gz.astgen.appendBodyWithFixups(param_body);
12358 param_gz.unstack();
12359
12360 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12361 gz.astgen.instructions.appendAssumeCapacity(.{
12362 .tag = tag,
12363 .data = .{ .pl_tok = .{
12364 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
12365 .payload_index = payload_index,
12366 } },
12367 });
12368 gz.instructions.appendAssumeCapacity(new_index);
12369 return new_index;
12370 }
12371
12372 fn addExtendedPayload(gz: *GenZir, opcode: Zir.Inst.Extended, extra: anytype) !Zir.Inst.Ref {
12373 return addExtendedPayloadSmall(gz, opcode, undefined, extra);
12374 }
12375
12376 fn addExtendedPayloadSmall(
12377 gz: *GenZir,
12378 opcode: Zir.Inst.Extended,
12379 small: u16,
12380 extra: anytype,
12381 ) !Zir.Inst.Ref {
12382 const gpa = gz.astgen.gpa;
12383
12384 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12385 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
12386
12387 const payload_index = try gz.astgen.addExtra(extra);
12388 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12389 gz.astgen.instructions.appendAssumeCapacity(.{
12390 .tag = .extended,
12391 .data = .{ .extended = .{
12392 .opcode = opcode,
12393 .small = small,
12394 .operand = payload_index,
12395 } },
12396 });
12397 gz.instructions.appendAssumeCapacity(new_index);
12398 return new_index.toRef();
12399 }
12400
12401 fn addExtendedMultiOp(
12402 gz: *GenZir,
12403 opcode: Zir.Inst.Extended,
12404 node: Ast.Node.Index,
12405 operands: []const Zir.Inst.Ref,
12406 ) !Zir.Inst.Ref {
12407 const astgen = gz.astgen;
12408 const gpa = astgen.gpa;
12409
12410 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12411 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
12412 try astgen.extra.ensureUnusedCapacity(
12413 gpa,
12414 @typeInfo(Zir.Inst.NodeMultiOp).Struct.fields.len + operands.len,
12415 );
12416
12417 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{
12418 .src_node = gz.nodeIndexToRelative(node),
12419 });
12420 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12421 astgen.instructions.appendAssumeCapacity(.{
12422 .tag = .extended,
12423 .data = .{ .extended = .{
12424 .opcode = opcode,
12425 .small = @intCast(operands.len),
12426 .operand = payload_index,
12427 } },
12428 });
12429 gz.instructions.appendAssumeCapacity(new_index);
12430 astgen.appendRefsAssumeCapacity(operands);
12431 return new_index.toRef();
12432 }
12433
12434 fn addExtendedMultiOpPayloadIndex(
12435 gz: *GenZir,
12436 opcode: Zir.Inst.Extended,
12437 payload_index: u32,
12438 trailing_len: usize,
12439 ) !Zir.Inst.Ref {
12440 const astgen = gz.astgen;
12441 const gpa = astgen.gpa;
12442
12443 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12444 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
12445 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12446 astgen.instructions.appendAssumeCapacity(.{
12447 .tag = .extended,
12448 .data = .{ .extended = .{
12449 .opcode = opcode,
12450 .small = @intCast(trailing_len),
12451 .operand = payload_index,
12452 } },
12453 });
12454 gz.instructions.appendAssumeCapacity(new_index);
12455 return new_index.toRef();
12456 }
12457
12458 fn addUnTok(
12459 gz: *GenZir,
12460 tag: Zir.Inst.Tag,
12461 operand: Zir.Inst.Ref,
12462 /// Absolute token index. This function does the conversion to Decl offset.
12463 abs_tok_index: Ast.TokenIndex,
12464 ) !Zir.Inst.Ref {
12465 assert(operand != .none);
12466 return gz.add(.{
12467 .tag = tag,
12468 .data = .{ .un_tok = .{
12469 .operand = operand,
12470 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
12471 } },
12472 });
12473 }
12474
12475 fn makeUnTok(
12476 gz: *GenZir,
12477 tag: Zir.Inst.Tag,
12478 operand: Zir.Inst.Ref,
12479 /// Absolute token index. This function does the conversion to Decl offset.
12480 abs_tok_index: Ast.TokenIndex,
12481 ) !Zir.Inst.Index {
12482 const astgen = gz.astgen;
12483 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12484 assert(operand != .none);
12485 try astgen.instructions.append(astgen.gpa, .{
12486 .tag = tag,
12487 .data = .{ .un_tok = .{
12488 .operand = operand,
12489 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
12490 } },
12491 });
12492 return new_index;
12493 }
12494
12495 fn addStrTok(
12496 gz: *GenZir,
12497 tag: Zir.Inst.Tag,
12498 str_index: Zir.NullTerminatedString,
12499 /// Absolute token index. This function does the conversion to Decl offset.
12500 abs_tok_index: Ast.TokenIndex,
12501 ) !Zir.Inst.Ref {
12502 return gz.add(.{
12503 .tag = tag,
12504 .data = .{ .str_tok = .{
12505 .start = str_index,
12506 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
12507 } },
12508 });
12509 }
12510
12511 fn addSaveErrRetIndex(
12512 gz: *GenZir,
12513 cond: union(enum) {
12514 always: void,
12515 if_of_error_type: Zir.Inst.Ref,
12516 },
12517 ) !Zir.Inst.Index {
12518 return gz.addAsIndex(.{
12519 .tag = .save_err_ret_index,
12520 .data = .{ .save_err_ret_index = .{
12521 .operand = switch (cond) {
12522 .if_of_error_type => |x| x,
12523 else => .none,
12524 },
12525 } },
12526 });
12527 }
12528
12529 const BranchTarget = union(enum) {
12530 ret,
12531 block: Zir.Inst.Index,
12532 };
12533
12534 fn addRestoreErrRetIndex(
12535 gz: *GenZir,
12536 bt: BranchTarget,
12537 cond: union(enum) {
12538 always: void,
12539 if_non_error: Zir.Inst.Ref,
12540 },
12541 src_node: Ast.Node.Index,
12542 ) !Zir.Inst.Index {
12543 switch (cond) {
12544 .always => return gz.addAsIndex(.{
12545 .tag = .restore_err_ret_index_unconditional,
12546 .data = .{ .un_node = .{
12547 .operand = switch (bt) {
12548 .ret => .none,
12549 .block => |b| b.toRef(),
12550 },
12551 .src_node = gz.nodeIndexToRelative(src_node),
12552 } },
12553 }),
12554 .if_non_error => |operand| switch (bt) {
12555 .ret => return gz.addAsIndex(.{
12556 .tag = .restore_err_ret_index_fn_entry,
12557 .data = .{ .un_node = .{
12558 .operand = operand,
12559 .src_node = gz.nodeIndexToRelative(src_node),
12560 } },
12561 }),
12562 .block => |block| return (try gz.addExtendedPayload(
12563 .restore_err_ret_index,
12564 Zir.Inst.RestoreErrRetIndex{
12565 .src_node = gz.nodeIndexToRelative(src_node),
12566 .block = block.toRef(),
12567 .operand = operand,
12568 },
12569 )).toIndex().?,
12570 },
12571 }
12572 }
12573
12574 fn addBreak(
12575 gz: *GenZir,
12576 tag: Zir.Inst.Tag,
12577 block_inst: Zir.Inst.Index,
12578 operand: Zir.Inst.Ref,
12579 ) !Zir.Inst.Index {
12580 const gpa = gz.astgen.gpa;
12581 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12582
12583 const new_index = try gz.makeBreak(tag, block_inst, operand);
12584 gz.instructions.appendAssumeCapacity(new_index);
12585 return new_index;
12586 }
12587
12588 fn makeBreak(
12589 gz: *GenZir,
12590 tag: Zir.Inst.Tag,
12591 block_inst: Zir.Inst.Index,
12592 operand: Zir.Inst.Ref,
12593 ) !Zir.Inst.Index {
12594 return gz.makeBreakCommon(tag, block_inst, operand, null);
12595 }
12596
12597 fn addBreakWithSrcNode(
12598 gz: *GenZir,
12599 tag: Zir.Inst.Tag,
12600 block_inst: Zir.Inst.Index,
12601 operand: Zir.Inst.Ref,
12602 operand_src_node: Ast.Node.Index,
12603 ) !Zir.Inst.Index {
12604 const gpa = gz.astgen.gpa;
12605 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12606
12607 const new_index = try gz.makeBreakWithSrcNode(tag, block_inst, operand, operand_src_node);
12608 gz.instructions.appendAssumeCapacity(new_index);
12609 return new_index;
12610 }
12611
12612 fn makeBreakWithSrcNode(
12613 gz: *GenZir,
12614 tag: Zir.Inst.Tag,
12615 block_inst: Zir.Inst.Index,
12616 operand: Zir.Inst.Ref,
12617 operand_src_node: Ast.Node.Index,
12618 ) !Zir.Inst.Index {
12619 return gz.makeBreakCommon(tag, block_inst, operand, operand_src_node);
12620 }
12621
12622 fn makeBreakCommon(
12623 gz: *GenZir,
12624 tag: Zir.Inst.Tag,
12625 block_inst: Zir.Inst.Index,
12626 operand: Zir.Inst.Ref,
12627 operand_src_node: ?Ast.Node.Index,
12628 ) !Zir.Inst.Index {
12629 const gpa = gz.astgen.gpa;
12630 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
12631 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Break).Struct.fields.len);
12632
12633 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12634 gz.astgen.instructions.appendAssumeCapacity(.{
12635 .tag = tag,
12636 .data = .{ .@"break" = .{
12637 .operand = operand,
12638 .payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Break{
12639 .operand_src_node = if (operand_src_node) |src_node|
12640 gz.nodeIndexToRelative(src_node)
12641 else
12642 Zir.Inst.Break.no_src_node,
12643 .block_inst = block_inst,
12644 }),
12645 } },
12646 });
12647 return new_index;
12648 }
12649
12650 fn addBin(
12651 gz: *GenZir,
12652 tag: Zir.Inst.Tag,
12653 lhs: Zir.Inst.Ref,
12654 rhs: Zir.Inst.Ref,
12655 ) !Zir.Inst.Ref {
12656 assert(lhs != .none);
12657 assert(rhs != .none);
12658 return gz.add(.{
12659 .tag = tag,
12660 .data = .{ .bin = .{
12661 .lhs = lhs,
12662 .rhs = rhs,
12663 } },
12664 });
12665 }
12666
12667 fn addDefer(gz: *GenZir, index: u32, len: u32) !void {
12668 _ = try gz.add(.{
12669 .tag = .@"defer",
12670 .data = .{ .@"defer" = .{
12671 .index = index,
12672 .len = len,
12673 } },
12674 });
12675 }
12676
12677 fn addDecl(
12678 gz: *GenZir,
12679 tag: Zir.Inst.Tag,
12680 decl_index: u32,
12681 src_node: Ast.Node.Index,
12682 ) !Zir.Inst.Ref {
12683 return gz.add(.{
12684 .tag = tag,
12685 .data = .{ .pl_node = .{
12686 .src_node = gz.nodeIndexToRelative(src_node),
12687 .payload_index = decl_index,
12688 } },
12689 });
12690 }
12691
12692 fn addNode(
12693 gz: *GenZir,
12694 tag: Zir.Inst.Tag,
12695 /// Absolute node index. This function does the conversion to offset from Decl.
12696 src_node: Ast.Node.Index,
12697 ) !Zir.Inst.Ref {
12698 return gz.add(.{
12699 .tag = tag,
12700 .data = .{ .node = gz.nodeIndexToRelative(src_node) },
12701 });
12702 }
12703
12704 fn addInstNode(
12705 gz: *GenZir,
12706 tag: Zir.Inst.Tag,
12707 inst: Zir.Inst.Index,
12708 /// Absolute node index. This function does the conversion to offset from Decl.
12709 src_node: Ast.Node.Index,
12710 ) !Zir.Inst.Ref {
12711 return gz.add(.{
12712 .tag = tag,
12713 .data = .{ .inst_node = .{
12714 .inst = inst,
12715 .src_node = gz.nodeIndexToRelative(src_node),
12716 } },
12717 });
12718 }
12719
12720 fn addNodeExtended(
12721 gz: *GenZir,
12722 opcode: Zir.Inst.Extended,
12723 /// Absolute node index. This function does the conversion to offset from Decl.
12724 src_node: Ast.Node.Index,
12725 ) !Zir.Inst.Ref {
12726 return gz.add(.{
12727 .tag = .extended,
12728 .data = .{ .extended = .{
12729 .opcode = opcode,
12730 .small = undefined,
12731 .operand = @bitCast(gz.nodeIndexToRelative(src_node)),
12732 } },
12733 });
12734 }
12735
12736 fn addAllocExtended(
12737 gz: *GenZir,
12738 args: struct {
12739 /// Absolute node index. This function does the conversion to offset from Decl.
12740 node: Ast.Node.Index,
12741 type_inst: Zir.Inst.Ref,
12742 align_inst: Zir.Inst.Ref,
12743 is_const: bool,
12744 is_comptime: bool,
12745 },
12746 ) !Zir.Inst.Ref {
12747 const astgen = gz.astgen;
12748 const gpa = astgen.gpa;
12749
12750 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12751 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
12752 try astgen.extra.ensureUnusedCapacity(
12753 gpa,
12754 @typeInfo(Zir.Inst.AllocExtended).Struct.fields.len +
12755 @intFromBool(args.type_inst != .none) +
12756 @intFromBool(args.align_inst != .none),
12757 );
12758 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{
12759 .src_node = gz.nodeIndexToRelative(args.node),
12760 });
12761 if (args.type_inst != .none) {
12762 astgen.extra.appendAssumeCapacity(@intFromEnum(args.type_inst));
12763 }
12764 if (args.align_inst != .none) {
12765 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst));
12766 }
12767
12768 const has_type: u4 = @intFromBool(args.type_inst != .none);
12769 const has_align: u4 = @intFromBool(args.align_inst != .none);
12770 const is_const: u4 = @intFromBool(args.is_const);
12771 const is_comptime: u4 = @intFromBool(args.is_comptime);
12772 const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3);
12773
12774 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12775 astgen.instructions.appendAssumeCapacity(.{
12776 .tag = .extended,
12777 .data = .{ .extended = .{
12778 .opcode = .alloc,
12779 .small = small,
12780 .operand = payload_index,
12781 } },
12782 });
12783 gz.instructions.appendAssumeCapacity(new_index);
12784 return new_index.toRef();
12785 }
12786
12787 fn addAsm(
12788 gz: *GenZir,
12789 args: struct {
12790 tag: Zir.Inst.Extended,
12791 /// Absolute node index. This function does the conversion to offset from Decl.
12792 node: Ast.Node.Index,
12793 asm_source: Zir.NullTerminatedString,
12794 output_type_bits: u32,
12795 is_volatile: bool,
12796 outputs: []const Zir.Inst.Asm.Output,
12797 inputs: []const Zir.Inst.Asm.Input,
12798 clobbers: []const u32,
12799 },
12800 ) !Zir.Inst.Ref {
12801 const astgen = gz.astgen;
12802 const gpa = astgen.gpa;
12803
12804 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12805 try astgen.instructions.ensureUnusedCapacity(gpa, 1);
12806 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).Struct.fields.len +
12807 args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).Struct.fields.len +
12808 args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).Struct.fields.len +
12809 args.clobbers.len);
12810
12811 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{
12812 .src_node = gz.nodeIndexToRelative(args.node),
12813 .asm_source = args.asm_source,
12814 .output_type_bits = args.output_type_bits,
12815 });
12816 for (args.outputs) |output| {
12817 _ = gz.astgen.addExtraAssumeCapacity(output);
12818 }
12819 for (args.inputs) |input| {
12820 _ = gz.astgen.addExtraAssumeCapacity(input);
12821 }
12822 gz.astgen.extra.appendSliceAssumeCapacity(args.clobbers);
12823
12824 // * 0b00000000_000XXXXX - `outputs_len`.
12825 // * 0b000000XX_XXX00000 - `inputs_len`.
12826 // * 0b0XXXXX00_00000000 - `clobbers_len`.
12827 // * 0bX0000000_00000000 - is volatile
12828 const small: u16 = @as(u16, @intCast(args.outputs.len)) |
12829 @as(u16, @intCast(args.inputs.len << 5)) |
12830 @as(u16, @intCast(args.clobbers.len << 10)) |
12831 (@as(u16, @intFromBool(args.is_volatile)) << 15);
12832
12833 const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
12834 astgen.instructions.appendAssumeCapacity(.{
12835 .tag = .extended,
12836 .data = .{ .extended = .{
12837 .opcode = args.tag,
12838 .small = small,
12839 .operand = payload_index,
12840 } },
12841 });
12842 gz.instructions.appendAssumeCapacity(new_index);
12843 return new_index.toRef();
12844 }
12845
12846 /// Note that this returns a `Zir.Inst.Index` not a ref.
12847 /// Does *not* append the block instruction to the scope.
12848 /// Leaves the `payload_index` field undefined.
12849 fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
12850 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12851 const gpa = gz.astgen.gpa;
12852 try gz.astgen.instructions.append(gpa, .{
12853 .tag = tag,
12854 .data = .{ .pl_node = .{
12855 .src_node = gz.nodeIndexToRelative(node),
12856 .payload_index = undefined,
12857 } },
12858 });
12859 return new_index;
12860 }
12861
12862 /// Note that this returns a `Zir.Inst.Index` not a ref.
12863 /// Leaves the `payload_index` field undefined.
12864 fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
12865 const gpa = gz.astgen.gpa;
12866 try gz.instructions.ensureUnusedCapacity(gpa, 1);
12867 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
12868 try gz.astgen.instructions.append(gpa, .{
12869 .tag = tag,
12870 .data = .{ .pl_node = .{
12871 .src_node = gz.nodeIndexToRelative(node),
12872 .payload_index = undefined,
12873 } },
12874 });
12875 gz.instructions.appendAssumeCapacity(new_index);
12876 return new_index;
12877 }
12878
12879 fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
12880 src_node: Ast.Node.Index,
12881 fields_len: u32,
12882 decls_len: u32,
12883 backing_int_ref: Zir.Inst.Ref,
12884 backing_int_body_len: u32,
12885 layout: std.builtin.Type.ContainerLayout,
12886 known_non_opv: bool,
12887 known_comptime_only: bool,
12888 is_tuple: bool,
12889 any_comptime_fields: bool,
12890 any_default_inits: bool,
12891 any_aligned_fields: bool,
12892 fields_hash: std.zig.SrcHash,
12893 }) !void {
12894 const astgen = gz.astgen;
12895 const gpa = astgen.gpa;
12896
12897 // Node 0 is valid for the root `struct_decl` of a file!
12898 assert(args.src_node != 0 or gz.parent.tag == .top);
12899
12900 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
12901
12902 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).Struct.fields.len + 4);
12903 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{
12904 .fields_hash_0 = fields_hash_arr[0],
12905 .fields_hash_1 = fields_hash_arr[1],
12906 .fields_hash_2 = fields_hash_arr[2],
12907 .fields_hash_3 = fields_hash_arr[3],
12908 .src_node = gz.nodeIndexToRelative(args.src_node),
12909 });
12910
12911 if (args.fields_len != 0) {
12912 astgen.extra.appendAssumeCapacity(args.fields_len);
12913 }
12914 if (args.decls_len != 0) {
12915 astgen.extra.appendAssumeCapacity(args.decls_len);
12916 }
12917 if (args.backing_int_ref != .none) {
12918 astgen.extra.appendAssumeCapacity(args.backing_int_body_len);
12919 if (args.backing_int_body_len == 0) {
12920 astgen.extra.appendAssumeCapacity(@intFromEnum(args.backing_int_ref));
12921 }
12922 }
12923 astgen.instructions.set(@intFromEnum(inst), .{
12924 .tag = .extended,
12925 .data = .{ .extended = .{
12926 .opcode = .struct_decl,
12927 .small = @bitCast(Zir.Inst.StructDecl.Small{
12928 .has_fields_len = args.fields_len != 0,
12929 .has_decls_len = args.decls_len != 0,
12930 .has_backing_int = args.backing_int_ref != .none,
12931 .known_non_opv = args.known_non_opv,
12932 .known_comptime_only = args.known_comptime_only,
12933 .is_tuple = args.is_tuple,
12934 .name_strategy = gz.anon_name_strategy,
12935 .layout = args.layout,
12936 .any_comptime_fields = args.any_comptime_fields,
12937 .any_default_inits = args.any_default_inits,
12938 .any_aligned_fields = args.any_aligned_fields,
12939 }),
12940 .operand = payload_index,
12941 } },
12942 });
12943 }
12944
12945 fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
12946 src_node: Ast.Node.Index,
12947 tag_type: Zir.Inst.Ref,
12948 body_len: u32,
12949 fields_len: u32,
12950 decls_len: u32,
12951 layout: std.builtin.Type.ContainerLayout,
12952 auto_enum_tag: bool,
12953 any_aligned_fields: bool,
12954 fields_hash: std.zig.SrcHash,
12955 }) !void {
12956 const astgen = gz.astgen;
12957 const gpa = astgen.gpa;
12958
12959 assert(args.src_node != 0);
12960
12961 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
12962
12963 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).Struct.fields.len + 4);
12964 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{
12965 .fields_hash_0 = fields_hash_arr[0],
12966 .fields_hash_1 = fields_hash_arr[1],
12967 .fields_hash_2 = fields_hash_arr[2],
12968 .fields_hash_3 = fields_hash_arr[3],
12969 .src_node = gz.nodeIndexToRelative(args.src_node),
12970 });
12971
12972 if (args.tag_type != .none) {
12973 astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type));
12974 }
12975 if (args.body_len != 0) {
12976 astgen.extra.appendAssumeCapacity(args.body_len);
12977 }
12978 if (args.fields_len != 0) {
12979 astgen.extra.appendAssumeCapacity(args.fields_len);
12980 }
12981 if (args.decls_len != 0) {
12982 astgen.extra.appendAssumeCapacity(args.decls_len);
12983 }
12984 astgen.instructions.set(@intFromEnum(inst), .{
12985 .tag = .extended,
12986 .data = .{ .extended = .{
12987 .opcode = .union_decl,
12988 .small = @bitCast(Zir.Inst.UnionDecl.Small{
12989 .has_tag_type = args.tag_type != .none,
12990 .has_body_len = args.body_len != 0,
12991 .has_fields_len = args.fields_len != 0,
12992 .has_decls_len = args.decls_len != 0,
12993 .name_strategy = gz.anon_name_strategy,
12994 .layout = args.layout,
12995 .auto_enum_tag = args.auto_enum_tag,
12996 .any_aligned_fields = args.any_aligned_fields,
12997 }),
12998 .operand = payload_index,
12999 } },
13000 });
13001 }
13002
13003 fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
13004 src_node: Ast.Node.Index,
13005 tag_type: Zir.Inst.Ref,
13006 body_len: u32,
13007 fields_len: u32,
13008 decls_len: u32,
13009 nonexhaustive: bool,
13010 fields_hash: std.zig.SrcHash,
13011 }) !void {
13012 const astgen = gz.astgen;
13013 const gpa = astgen.gpa;
13014
13015 assert(args.src_node != 0);
13016
13017 const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
13018
13019 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).Struct.fields.len + 4);
13020 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{
13021 .fields_hash_0 = fields_hash_arr[0],
13022 .fields_hash_1 = fields_hash_arr[1],
13023 .fields_hash_2 = fields_hash_arr[2],
13024 .fields_hash_3 = fields_hash_arr[3],
13025 .src_node = gz.nodeIndexToRelative(args.src_node),
13026 });
13027
13028 if (args.tag_type != .none) {
13029 astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type));
13030 }
13031 if (args.body_len != 0) {
13032 astgen.extra.appendAssumeCapacity(args.body_len);
13033 }
13034 if (args.fields_len != 0) {
13035 astgen.extra.appendAssumeCapacity(args.fields_len);
13036 }
13037 if (args.decls_len != 0) {
13038 astgen.extra.appendAssumeCapacity(args.decls_len);
13039 }
13040 astgen.instructions.set(@intFromEnum(inst), .{
13041 .tag = .extended,
13042 .data = .{ .extended = .{
13043 .opcode = .enum_decl,
13044 .small = @bitCast(Zir.Inst.EnumDecl.Small{
13045 .has_tag_type = args.tag_type != .none,
13046 .has_body_len = args.body_len != 0,
13047 .has_fields_len = args.fields_len != 0,
13048 .has_decls_len = args.decls_len != 0,
13049 .name_strategy = gz.anon_name_strategy,
13050 .nonexhaustive = args.nonexhaustive,
13051 }),
13052 .operand = payload_index,
13053 } },
13054 });
13055 }
13056
13057 fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
13058 src_node: Ast.Node.Index,
13059 decls_len: u32,
13060 }) !void {
13061 const astgen = gz.astgen;
13062 const gpa = astgen.gpa;
13063
13064 assert(args.src_node != 0);
13065
13066 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).Struct.fields.len + 1);
13067 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{
13068 .src_node = gz.nodeIndexToRelative(args.src_node),
13069 });
13070
13071 if (args.decls_len != 0) {
13072 astgen.extra.appendAssumeCapacity(args.decls_len);
13073 }
13074 astgen.instructions.set(@intFromEnum(inst), .{
13075 .tag = .extended,
13076 .data = .{ .extended = .{
13077 .opcode = .opaque_decl,
13078 .small = @bitCast(Zir.Inst.OpaqueDecl.Small{
13079 .has_decls_len = args.decls_len != 0,
13080 .name_strategy = gz.anon_name_strategy,
13081 }),
13082 .operand = payload_index,
13083 } },
13084 });
13085 }
13086
13087 fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
13088 return (try gz.addAsIndex(inst)).toRef();
13089 }
13090
13091 fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
13092 const gpa = gz.astgen.gpa;
13093 try gz.instructions.ensureUnusedCapacity(gpa, 1);
13094 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
13095
13096 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
13097 gz.astgen.instructions.appendAssumeCapacity(inst);
13098 gz.instructions.appendAssumeCapacity(new_index);
13099 return new_index;
13100 }
13101
13102 fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index {
13103 const gpa = gz.astgen.gpa;
13104 try gz.instructions.ensureUnusedCapacity(gpa, 1);
13105 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
13106
13107 const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
13108 gz.astgen.instructions.len += 1;
13109 gz.instructions.appendAssumeCapacity(new_index);
13110 return new_index;
13111 }
13112
13113 fn addRet(gz: *GenZir, ri: ResultInfo, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
13114 switch (ri.rl) {
13115 .ptr => |ptr_res| _ = try gz.addUnNode(.ret_load, ptr_res.inst, node),
13116 .coerced_ty => _ = try gz.addUnNode(.ret_node, operand, node),
13117 else => unreachable,
13118 }
13119 }
13120
13121 fn addNamespaceCaptures(gz: *GenZir, namespace: *Scope.Namespace) !void {
13122 if (namespace.captures.count() > 0) {
13123 try gz.instructions.ensureUnusedCapacity(gz.astgen.gpa, namespace.captures.count());
13124 for (namespace.captures.values()) |capture| {
13125 gz.instructions.appendAssumeCapacity(capture);
13126 }
13127 }
13128 }
13129
13130 fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: Zir.NullTerminatedString, inst: Zir.Inst.Ref) !void {
13131 if (gz.is_comptime) return;
13132
13133 _ = try gz.add(.{ .tag = tag, .data = .{
13134 .str_op = .{
13135 .str = name,
13136 .operand = inst,
13137 },
13138 } });
13139 }
13140};
13141
13142/// This can only be for short-lived references; the memory becomes invalidated
13143/// when another string is added.
13144fn nullTerminatedString(astgen: AstGen, index: Zir.NullTerminatedString) [*:0]const u8 {
13145 return @ptrCast(astgen.string_bytes.items[@intFromEnum(index)..]);
13146}
13147
13148/// Local variables shadowing detection, including function parameters.
13149fn detectLocalShadowing(
13150 astgen: *AstGen,
13151 scope: *Scope,
13152 ident_name: Zir.NullTerminatedString,
13153 name_token: Ast.TokenIndex,
13154 token_bytes: []const u8,
13155 id_cat: Scope.IdCat,
13156) !void {
13157 const gpa = astgen.gpa;
13158 if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
13159 return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
13160 token_bytes,
13161 }, &[_]u32{
13162 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
13163 token_bytes,
13164 }),
13165 });
13166 }
13167
13168 var s = scope;
13169 var outer_scope = false;
13170 while (true) switch (s.tag) {
13171 .local_val => {
13172 const local_val = s.cast(Scope.LocalVal).?;
13173 if (local_val.name == ident_name) {
13174 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
13175 const name = try gpa.dupe(u8, name_slice);
13176 defer gpa.free(name);
13177 if (outer_scope) {
13178 return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
13179 @tagName(id_cat), name, @tagName(local_val.id_cat),
13180 }, &[_]u32{
13181 try astgen.errNoteTok(
13182 local_val.token_src,
13183 "previous declaration here",
13184 .{},
13185 ),
13186 });
13187 }
13188 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
13189 @tagName(local_val.id_cat), name,
13190 }, &[_]u32{
13191 try astgen.errNoteTok(
13192 local_val.token_src,
13193 "previous declaration here",
13194 .{},
13195 ),
13196 });
13197 }
13198 s = local_val.parent;
13199 },
13200 .local_ptr => {
13201 const local_ptr = s.cast(Scope.LocalPtr).?;
13202 if (local_ptr.name == ident_name) {
13203 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
13204 const name = try gpa.dupe(u8, name_slice);
13205 defer gpa.free(name);
13206 if (outer_scope) {
13207 return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
13208 @tagName(id_cat), name, @tagName(local_ptr.id_cat),
13209 }, &[_]u32{
13210 try astgen.errNoteTok(
13211 local_ptr.token_src,
13212 "previous declaration here",
13213 .{},
13214 ),
13215 });
13216 }
13217 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
13218 @tagName(local_ptr.id_cat), name,
13219 }, &[_]u32{
13220 try astgen.errNoteTok(
13221 local_ptr.token_src,
13222 "previous declaration here",
13223 .{},
13224 ),
13225 });
13226 }
13227 s = local_ptr.parent;
13228 },
13229 .namespace, .enum_namespace => {
13230 outer_scope = true;
13231 const ns = s.cast(Scope.Namespace).?;
13232 const decl_node = ns.decls.get(ident_name) orelse {
13233 s = ns.parent;
13234 continue;
13235 };
13236 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
13237 const name = try gpa.dupe(u8, name_slice);
13238 defer gpa.free(name);
13239 return astgen.failTokNotes(name_token, "{s} shadows declaration of '{s}'", .{
13240 @tagName(id_cat), name,
13241 }, &[_]u32{
13242 try astgen.errNoteNode(decl_node, "declared here", .{}),
13243 });
13244 },
13245 .gen_zir => {
13246 s = s.cast(GenZir).?.parent;
13247 outer_scope = true;
13248 },
13249 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
13250 .top => break,
13251 };
13252}
13253
13254const LineColumn = struct { u32, u32 };
13255
13256/// Advances the source cursor to the main token of `node` if not in comptime scope.
13257/// Usually paired with `emitDbgStmt`.
13258fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) LineColumn {
13259 if (gz.is_comptime) return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column };
13260
13261 const tree = gz.astgen.tree;
13262 const token_starts = tree.tokens.items(.start);
13263 const main_tokens = tree.nodes.items(.main_token);
13264 const node_start = token_starts[main_tokens[node]];
13265 gz.astgen.advanceSourceCursor(node_start);
13266
13267 return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column };
13268}
13269
13270/// Advances the source cursor to the beginning of `node`.
13271fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void {
13272 const tree = astgen.tree;
13273 const token_starts = tree.tokens.items(.start);
13274 const node_start = token_starts[tree.firstToken(node)];
13275 astgen.advanceSourceCursor(node_start);
13276}
13277
13278/// Advances the source cursor to an absolute byte offset `end` in the file.
13279fn advanceSourceCursor(astgen: *AstGen, end: usize) void {
13280 const source = astgen.tree.source;
13281 var i = astgen.source_offset;
13282 var line = astgen.source_line;
13283 var column = astgen.source_column;
13284 assert(i <= end);
13285 while (i < end) : (i += 1) {
13286 if (source[i] == '\n') {
13287 line += 1;
13288 column = 0;
13289 } else {
13290 column += 1;
13291 }
13292 }
13293 astgen.source_offset = i;
13294 astgen.source_line = line;
13295 astgen.source_column = column;
13296}
13297
13298fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !u32 {
13299 const gpa = astgen.gpa;
13300 const tree = astgen.tree;
13301 const node_tags = tree.nodes.items(.tag);
13302 const main_tokens = tree.nodes.items(.main_token);
13303 const token_tags = tree.tokens.items(.tag);
13304 var decl_count: u32 = 0;
13305 for (members) |member_node| {
13306 const name_token = switch (node_tags[member_node]) {
13307 .global_var_decl,
13308 .local_var_decl,
13309 .simple_var_decl,
13310 .aligned_var_decl,
13311 => blk: {
13312 decl_count += 1;
13313 break :blk main_tokens[member_node] + 1;
13314 },
13315
13316 .fn_proto_simple,
13317 .fn_proto_multi,
13318 .fn_proto_one,
13319 .fn_proto,
13320 .fn_decl,
13321 => blk: {
13322 decl_count += 1;
13323 const ident = main_tokens[member_node] + 1;
13324 if (token_tags[ident] != .identifier) {
13325 switch (astgen.failNode(member_node, "missing function name", .{})) {
13326 error.AnalysisFail => continue,
13327 error.OutOfMemory => return error.OutOfMemory,
13328 }
13329 }
13330 break :blk ident;
13331 },
13332
13333 .@"comptime", .@"usingnamespace", .test_decl => {
13334 decl_count += 1;
13335 continue;
13336 },
13337
13338 else => continue,
13339 };
13340
13341 const token_bytes = astgen.tree.tokenSlice(name_token);
13342 if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
13343 switch (astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
13344 token_bytes,
13345 }, &[_]u32{
13346 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
13347 token_bytes,
13348 }),
13349 })) {
13350 error.AnalysisFail => continue,
13351 error.OutOfMemory => return error.OutOfMemory,
13352 }
13353 }
13354
13355 const name_str_index = try astgen.identAsString(name_token);
13356 const gop = try namespace.decls.getOrPut(gpa, name_str_index);
13357 if (gop.found_existing) {
13358 const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(name_str_index)));
13359 defer gpa.free(name);
13360 switch (astgen.failNodeNotes(member_node, "redeclaration of '{s}'", .{
13361 name,
13362 }, &[_]u32{
13363 try astgen.errNoteNode(gop.value_ptr.*, "other declaration here", .{}),
13364 })) {
13365 error.AnalysisFail => continue,
13366 error.OutOfMemory => return error.OutOfMemory,
13367 }
13368 }
13369
13370 var s = namespace.parent;
13371 while (true) switch (s.tag) {
13372 .local_val => {
13373 const local_val = s.cast(Scope.LocalVal).?;
13374 if (local_val.name == name_str_index) {
13375 return astgen.failTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
13376 token_bytes, @tagName(local_val.id_cat),
13377 }, &[_]u32{
13378 try astgen.errNoteTok(
13379 local_val.token_src,
13380 "previous declaration here",
13381 .{},
13382 ),
13383 });
13384 }
13385 s = local_val.parent;
13386 },
13387 .local_ptr => {
13388 const local_ptr = s.cast(Scope.LocalPtr).?;
13389 if (local_ptr.name == name_str_index) {
13390 return astgen.failTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
13391 token_bytes, @tagName(local_ptr.id_cat),
13392 }, &[_]u32{
13393 try astgen.errNoteTok(
13394 local_ptr.token_src,
13395 "previous declaration here",
13396 .{},
13397 ),
13398 });
13399 }
13400 s = local_ptr.parent;
13401 },
13402 .namespace, .enum_namespace => s = s.cast(Scope.Namespace).?.parent,
13403 .gen_zir => s = s.cast(GenZir).?.parent,
13404 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
13405 .top => break,
13406 };
13407 gop.value_ptr.* = member_node;
13408 }
13409 return decl_count;
13410}
13411
13412fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {
13413 const inst = ref.toIndex() orelse return false;
13414 const zir_tags = astgen.instructions.items(.tag);
13415 return switch (zir_tags[@intFromEnum(inst)]) {
13416 .alloc_inferred,
13417 .alloc_inferred_mut,
13418 .alloc_inferred_comptime,
13419 .alloc_inferred_comptime_mut,
13420 => true,
13421
13422 .extended => {
13423 const zir_data = astgen.instructions.items(.data);
13424 if (zir_data[@intFromEnum(inst)].extended.opcode != .alloc) return false;
13425 const small: Zir.Inst.AllocExtended.Small = @bitCast(zir_data[@intFromEnum(inst)].extended.small);
13426 return !small.has_type;
13427 },
13428
13429 else => false,
13430 };
13431}
13432
13433/// Assumes capacity for body has already been added. Needed capacity taking into
13434/// account fixups can be found with `countBodyLenAfterFixups`.
13435fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void {
13436 return appendBodyWithFixupsArrayList(astgen, &astgen.extra, body);
13437}
13438
13439fn appendBodyWithFixupsArrayList(
13440 astgen: *AstGen,
13441 list: *std.ArrayListUnmanaged(u32),
13442 body: []const Zir.Inst.Index,
13443) void {
13444 for (body) |body_inst| {
13445 appendPossiblyRefdBodyInst(astgen, list, body_inst);
13446 }
13447}
13448
13449fn appendPossiblyRefdBodyInst(
13450 astgen: *AstGen,
13451 list: *std.ArrayListUnmanaged(u32),
13452 body_inst: Zir.Inst.Index,
13453) void {
13454 list.appendAssumeCapacity(@intFromEnum(body_inst));
13455 const kv = astgen.ref_table.fetchRemove(body_inst) orelse return;
13456 const ref_inst = kv.value;
13457 return appendPossiblyRefdBodyInst(astgen, list, ref_inst);
13458}
13459
13460fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
13461 var count = body.len;
13462 for (body) |body_inst| {
13463 var check_inst = body_inst;
13464 while (astgen.ref_table.get(check_inst)) |ref_inst| {
13465 count += 1;
13466 check_inst = ref_inst;
13467 }
13468 }
13469 return @intCast(count);
13470}
13471
13472fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void {
13473 if (gz.is_comptime) return;
13474 if (gz.instructions.items.len > 0) {
13475 const astgen = gz.astgen;
13476 const last = gz.instructions.items[gz.instructions.items.len - 1];
13477 if (astgen.instructions.items(.tag)[@intFromEnum(last)] == .dbg_stmt) {
13478 astgen.instructions.items(.data)[@intFromEnum(last)].dbg_stmt = .{
13479 .line = lc[0],
13480 .column = lc[1],
13481 };
13482 return;
13483 }
13484 }
13485
13486 _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
13487 .dbg_stmt = .{
13488 .line = lc[0],
13489 .column = lc[1],
13490 },
13491 } });
13492}
13493
13494/// In some cases, Sema expects us to generate a `dbg_stmt` at the instruction
13495/// *index* directly preceding the next instruction (e.g. if a call is %10, it
13496/// expects a dbg_stmt at %9). TODO: this logic may allow redundant dbg_stmt
13497/// instructions; fix up Sema so we don't need it!
13498fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void {
13499 const astgen = gz.astgen;
13500 if (gz.instructions.items.len > 0 and
13501 @intFromEnum(gz.instructions.items[gz.instructions.items.len - 1]) == astgen.instructions.len - 1)
13502 {
13503 const last = astgen.instructions.len - 1;
13504 if (astgen.instructions.items(.tag)[last] == .dbg_stmt) {
13505 astgen.instructions.items(.data)[last].dbg_stmt = .{
13506 .line = lc[0],
13507 .column = lc[1],
13508 };
13509 return;
13510 }
13511 }
13512
13513 _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
13514 .dbg_stmt = .{
13515 .line = lc[0],
13516 .column = lc[1],
13517 },
13518 } });
13519}
13520
13521fn lowerAstErrors(astgen: *AstGen) !void {
13522 const tree = astgen.tree;
13523 assert(tree.errors.len > 0);
13524
13525 const gpa = astgen.gpa;
13526 const parse_err = tree.errors[0];
13527
13528 var msg: std.ArrayListUnmanaged(u8) = .{};
13529 defer msg.deinit(gpa);
13530
13531 const token_starts = tree.tokens.items(.start);
13532 const token_tags = tree.tokens.items(.tag);
13533
13534 var notes: std.ArrayListUnmanaged(u32) = .{};
13535 defer notes.deinit(gpa);
13536
13537 if (token_tags[parse_err.token + @intFromBool(parse_err.token_is_prev)] == .invalid) {
13538 const tok = parse_err.token + @intFromBool(parse_err.token_is_prev);
13539 const bad_off: u32 = @intCast(tree.tokenSlice(parse_err.token + @intFromBool(parse_err.token_is_prev)).len);
13540 const byte_abs = token_starts[parse_err.token + @intFromBool(parse_err.token_is_prev)] + bad_off;
13541 try notes.append(gpa, try astgen.errNoteTokOff(tok, bad_off, "invalid byte: '{'}'", .{
13542 std.zig.fmtEscapes(tree.source[byte_abs..][0..1]),
13543 }));
13544 }
13545
13546 for (tree.errors[1..]) |note| {
13547 if (!note.is_note) break;
13548
13549 msg.clearRetainingCapacity();
13550 try tree.renderError(note, msg.writer(gpa));
13551 try notes.append(gpa, try astgen.errNoteTok(note.token, "{s}", .{msg.items}));
13552 }
13553
13554 const extra_offset = tree.errorOffset(parse_err);
13555 msg.clearRetainingCapacity();
13556 try tree.renderError(parse_err, msg.writer(gpa));
13557 try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
13558}
13559
13560const DeclarationName = union(enum) {
13561 named: Ast.TokenIndex,
13562 named_test: Ast.TokenIndex,
13563 unnamed_test,
13564 decltest: Zir.NullTerminatedString,
13565 @"comptime",
13566 @"usingnamespace",
13567};
13568
13569/// Sets all extra data for a `declaration` instruction.
13570/// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`.
13571fn setDeclaration(
13572 decl_inst: Zir.Inst.Index,
13573 src_hash: std.zig.SrcHash,
13574 name: DeclarationName,
13575 line_offset: u32,
13576 is_pub: bool,
13577 is_export: bool,
13578 doc_comment: Zir.NullTerminatedString,
13579 value_gz: *GenZir,
13580 /// May be `null` if all these blocks would be empty.
13581 /// If `null`, then `value_gz` must have nothing stacked on it.
13582 extra_gzs: ?struct {
13583 /// Must be stacked on `value_gz`.
13584 align_gz: *GenZir,
13585 /// Must be stacked on `align_gz`.
13586 linksection_gz: *GenZir,
13587 /// Must be stacked on `linksection_gz`, and have nothing stacked on it.
13588 addrspace_gz: *GenZir,
13589 },
13590) !void {
13591 const astgen = value_gz.astgen;
13592 const gpa = astgen.gpa;
13593
13594 const empty_body: []Zir.Inst.Index = &.{};
13595 const value_body, const align_body, const linksection_body, const addrspace_body = if (extra_gzs) |e| .{
13596 value_gz.instructionsSliceUpto(e.align_gz),
13597 e.align_gz.instructionsSliceUpto(e.linksection_gz),
13598 e.linksection_gz.instructionsSliceUpto(e.addrspace_gz),
13599 e.addrspace_gz.instructionsSlice(),
13600 } else .{ value_gz.instructionsSlice(), empty_body, empty_body, empty_body };
13601
13602 const value_len = astgen.countBodyLenAfterFixups(value_body);
13603 const align_len = astgen.countBodyLenAfterFixups(align_body);
13604 const linksection_len = astgen.countBodyLenAfterFixups(linksection_body);
13605 const addrspace_len = astgen.countBodyLenAfterFixups(addrspace_body);
13606
13607 const true_doc_comment: Zir.NullTerminatedString = switch (name) {
13608 .decltest => |test_name| test_name,
13609 else => doc_comment,
13610 };
13611
13612 const src_hash_arr: [4]u32 = @bitCast(src_hash);
13613
13614 const extra: Zir.Inst.Declaration = .{
13615 .src_hash_0 = src_hash_arr[0],
13616 .src_hash_1 = src_hash_arr[1],
13617 .src_hash_2 = src_hash_arr[2],
13618 .src_hash_3 = src_hash_arr[3],
13619 .name = switch (name) {
13620 .named => |tok| @enumFromInt(@intFromEnum(try astgen.identAsString(tok))),
13621 .named_test => |tok| @enumFromInt(@intFromEnum(try astgen.testNameString(tok))),
13622 .unnamed_test => .unnamed_test,
13623 .decltest => .decltest,
13624 .@"comptime" => .@"comptime",
13625 .@"usingnamespace" => .@"usingnamespace",
13626 },
13627 .line_offset = line_offset,
13628 .flags = .{
13629 .value_body_len = @intCast(value_len),
13630 .is_pub = is_pub,
13631 .is_export = is_export,
13632 .has_doc_comment = true_doc_comment != .empty,
13633 .has_align_linksection_addrspace = align_len != 0 or linksection_len != 0 or addrspace_len != 0,
13634 },
13635 };
13636 astgen.instructions.items(.data)[@intFromEnum(decl_inst)].pl_node.payload_index = try astgen.addExtra(extra);
13637 if (extra.flags.has_doc_comment) {
13638 try astgen.extra.append(gpa, @intFromEnum(true_doc_comment));
13639 }
13640 if (extra.flags.has_align_linksection_addrspace) {
13641 try astgen.extra.appendSlice(gpa, &.{
13642 align_len,
13643 linksection_len,
13644 addrspace_len,
13645 });
13646 }
13647 try astgen.extra.ensureUnusedCapacity(gpa, value_len + align_len + linksection_len + addrspace_len);
13648 astgen.appendBodyWithFixups(value_body);
13649 if (extra.flags.has_align_linksection_addrspace) {
13650 astgen.appendBodyWithFixups(align_body);
13651 astgen.appendBodyWithFixups(linksection_body);
13652 astgen.appendBodyWithFixups(addrspace_body);
13653 }
13654
13655 if (extra_gzs) |e| {
13656 e.addrspace_gz.unstack();
13657 e.linksection_gz.unstack();
13658 e.align_gz.unstack();
13659 }
13660 value_gz.unstack();
13661}
src/Autodoc.zig+1-1
...@@ -9,7 +9,7 @@ const File = Zcu.File;...@@ -9,7 +9,7 @@ const File = Zcu.File;
9const Module = @import("Package.zig").Module;9const Module = @import("Package.zig").Module;
10const Tokenizer = std.zig.Tokenizer;10const Tokenizer = std.zig.Tokenizer;
11const InternPool = @import("InternPool.zig");11const InternPool = @import("InternPool.zig");
12const Zir = @import("Zir.zig");12const Zir = std.zig.Zir;
13const Ref = Zir.Inst.Ref;13const Ref = Zir.Inst.Ref;
14const log = std.log.scoped(.autodoc);14const log = std.log.scoped(.autodoc);
15const renderer = @import("autodoc/render_source.zig");15const renderer = @import("autodoc/render_source.zig");
src/Builtin.zig+1-1
...@@ -296,7 +296,7 @@ const Allocator = std.mem.Allocator;...@@ -296,7 +296,7 @@ const Allocator = std.mem.Allocator;
296const build_options = @import("build_options");296const build_options = @import("build_options");
297const Module = @import("Package/Module.zig");297const Module = @import("Package/Module.zig");
298const assert = std.debug.assert;298const assert = std.debug.assert;
299const AstGen = @import("AstGen.zig");299const AstGen = std.zig.AstGen;
300const File = @import("Module.zig").File;300const File = @import("Module.zig").File;
301const Compilation = @import("Compilation.zig");301const Compilation = @import("Compilation.zig");
302const log = std.log.scoped(.builtin);302const log = std.log.scoped(.builtin);
src/Compilation.zig+4-79
...@@ -35,7 +35,7 @@ const InternPool = @import("InternPool.zig");...@@ -35,7 +35,7 @@ const InternPool = @import("InternPool.zig");
35const Cache = std.Build.Cache;35const Cache = std.Build.Cache;
36const c_codegen = @import("codegen/c.zig");36const c_codegen = @import("codegen/c.zig");
37const libtsan = @import("libtsan.zig");37const libtsan = @import("libtsan.zig");
38const Zir = @import("Zir.zig");38const Zir = std.zig.Zir;
39const Autodoc = @import("Autodoc.zig");39const Autodoc = @import("Autodoc.zig");
40const resinator = @import("resinator.zig");40const resinator = @import("resinator.zig");
41const Builtin = @import("Builtin.zig");41const Builtin = @import("Builtin.zig");
...@@ -3322,85 +3322,10 @@ pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void {...@@ -3322,85 +3322,10 @@ pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void {
3322 assert(file.zir_loaded);3322 assert(file.zir_loaded);
3323 assert(file.tree_loaded);3323 assert(file.tree_loaded);
3324 assert(file.source_loaded);3324 assert(file.source_loaded);
3325 const payload_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.compile_errors)];
3326 assert(payload_index != 0);
3327 const gpa = eb.gpa;3325 const gpa = eb.gpa;
33283326 const src_path = try file.fullPath(gpa);
3329 const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index);3327 defer gpa.free(src_path);
3330 const items_len = header.data.items_len;3328 return eb.addZirErrorMessages(file.zir, file.tree, file.source, src_path);
3331 var extra_index = header.end;
3332 for (0..items_len) |_| {
3333 const item = file.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index);
3334 extra_index = item.end;
3335 const err_span = blk: {
3336 if (item.data.node != 0) {
3337 break :blk Module.SrcLoc.nodeToSpan(&file.tree, item.data.node);
3338 }
3339 const token_starts = file.tree.tokens.items(.start);
3340 const start = token_starts[item.data.token] + item.data.byte_offset;
3341 const end = start + @as(u32, @intCast(file.tree.tokenSlice(item.data.token).len)) - item.data.byte_offset;
3342 break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start };
3343 };
3344 const err_loc = std.zig.findLineColumn(file.source, err_span.main);
3345
3346 {
3347 const msg = file.zir.nullTerminatedString(item.data.msg);
3348 const src_path = try file.fullPath(gpa);
3349 defer gpa.free(src_path);
3350 try eb.addRootErrorMessage(.{
3351 .msg = try eb.addString(msg),
3352 .src_loc = try eb.addSourceLocation(.{
3353 .src_path = try eb.addString(src_path),
3354 .span_start = err_span.start,
3355 .span_main = err_span.main,
3356 .span_end = err_span.end,
3357 .line = @as(u32, @intCast(err_loc.line)),
3358 .column = @as(u32, @intCast(err_loc.column)),
3359 .source_line = try eb.addString(err_loc.source_line),
3360 }),
3361 .notes_len = item.data.notesLen(file.zir),
3362 });
3363 }
3364
3365 if (item.data.notes != 0) {
3366 const notes_start = try eb.reserveNotes(item.data.notes);
3367 const block = file.zir.extraData(Zir.Inst.Block, item.data.notes);
3368 const body = file.zir.extra[block.end..][0..block.data.body_len];
3369 for (notes_start.., body) |note_i, body_elem| {
3370 const note_item = file.zir.extraData(Zir.Inst.CompileErrors.Item, body_elem);
3371 const msg = file.zir.nullTerminatedString(note_item.data.msg);
3372 const span = blk: {
3373 if (note_item.data.node != 0) {
3374 break :blk Module.SrcLoc.nodeToSpan(&file.tree, note_item.data.node);
3375 }
3376 const token_starts = file.tree.tokens.items(.start);
3377 const start = token_starts[note_item.data.token] + note_item.data.byte_offset;
3378 const end = start + @as(u32, @intCast(file.tree.tokenSlice(note_item.data.token).len)) - item.data.byte_offset;
3379 break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start };
3380 };
3381 const loc = std.zig.findLineColumn(file.source, span.main);
3382 const src_path = try file.fullPath(gpa);
3383 defer gpa.free(src_path);
3384
3385 eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{
3386 .msg = try eb.addString(msg),
3387 .src_loc = try eb.addSourceLocation(.{
3388 .src_path = try eb.addString(src_path),
3389 .span_start = span.start,
3390 .span_main = span.main,
3391 .span_end = span.end,
3392 .line = @as(u32, @intCast(loc.line)),
3393 .column = @as(u32, @intCast(loc.column)),
3394 .source_line = if (loc.eql(err_loc))
3395 0
3396 else
3397 try eb.addString(loc.source_line),
3398 }),
3399 .notes_len = 0, // TODO rework this function to be recursive
3400 }));
3401 }
3402 }
3403 }
3404}3329}
34053330
3406pub fn performAllTheWork(3331pub fn performAllTheWork(
src/InternPool.zig+4-23
...@@ -338,7 +338,7 @@ const Hash = std.hash.Wyhash;...@@ -338,7 +338,7 @@ const Hash = std.hash.Wyhash;
338const InternPool = @This();338const InternPool = @This();
339const Module = @import("Module.zig");339const Module = @import("Module.zig");
340const Zcu = Module;340const Zcu = Module;
341const Zir = @import("Zir.zig");341const Zir = std.zig.Zir;
342342
343const KeyAdapter = struct {343const KeyAdapter = struct {
344 intern_pool: *const InternPool,344 intern_pool: *const InternPool,
...@@ -383,27 +383,8 @@ pub const RuntimeIndex = enum(u32) {...@@ -383,27 +383,8 @@ pub const RuntimeIndex = enum(u32) {
383 }383 }
384};384};
385385
386pub const DeclIndex = enum(u32) {386pub const DeclIndex = std.zig.DeclIndex;
387 _,387pub const OptionalDeclIndex = std.zig.OptionalDeclIndex;
388
389 pub fn toOptional(i: DeclIndex) OptionalDeclIndex {
390 return @enumFromInt(@intFromEnum(i));
391 }
392};
393
394pub const OptionalDeclIndex = enum(u32) {
395 none = std.math.maxInt(u32),
396 _,
397
398 pub fn init(oi: ?DeclIndex) OptionalDeclIndex {
399 return @enumFromInt(@intFromEnum(oi orelse return .none));
400 }
401
402 pub fn unwrap(oi: OptionalDeclIndex) ?DeclIndex {
403 if (oi == .none) return null;
404 return @enumFromInt(@intFromEnum(oi));
405 }
406};
407388
408pub const NamespaceIndex = enum(u32) {389pub const NamespaceIndex = enum(u32) {
409 _,390 _,
...@@ -2877,7 +2858,7 @@ pub const static_keys = [_]Key{...@@ -2877,7 +2858,7 @@ pub const static_keys = [_]Key{
2877/// This is specified with an integer literal and a corresponding comptime2858/// This is specified with an integer literal and a corresponding comptime
2878/// assert below to break an unfortunate and arguably incorrect dependency loop2859/// assert below to break an unfortunate and arguably incorrect dependency loop
2879/// when compiling.2860/// when compiling.
2880pub const static_len = 84;2861pub const static_len = Zir.Inst.Index.static_len;
2881comptime {2862comptime {
2882 //@compileLog(static_keys.len);2863 //@compileLog(static_keys.len);
2883 assert(static_len == static_keys.len);2864 assert(static_len == static_keys.len);
src/Module.zig+164-556
...@@ -13,6 +13,7 @@ const BigIntConst = std.math.big.int.Const;...@@ -13,6 +13,7 @@ const BigIntConst = std.math.big.int.Const;
13const BigIntMutable = std.math.big.int.Mutable;13const BigIntMutable = std.math.big.int.Mutable;
14const Target = std.Target;14const Target = std.Target;
15const Ast = std.zig.Ast;15const Ast = std.zig.Ast;
16const LazySrcLoc = std.zig.LazySrcLoc;
1617
17/// Deprecated, use `Zcu`.18/// Deprecated, use `Zcu`.
18const Module = Zcu;19const Module = Zcu;
...@@ -25,9 +26,9 @@ const TypedValue = @import("TypedValue.zig");...@@ -25,9 +26,9 @@ const TypedValue = @import("TypedValue.zig");
25const Package = @import("Package.zig");26const Package = @import("Package.zig");
26const link = @import("link.zig");27const link = @import("link.zig");
27const Air = @import("Air.zig");28const Air = @import("Air.zig");
28const Zir = @import("Zir.zig");29const Zir = std.zig.Zir;
29const trace = @import("tracy.zig").trace;30const trace = @import("tracy.zig").trace;
30const AstGen = @import("AstGen.zig");31const AstGen = std.zig.AstGen;
31const Sema = @import("Sema.zig");32const Sema = @import("Sema.zig");
32const target_util = @import("target.zig");33const target_util = @import("target.zig");
33const build_options = @import("build_options");34const build_options = @import("build_options");
...@@ -664,6 +665,101 @@ pub const Decl = struct {...@@ -664,6 +665,101 @@ pub const Decl = struct {
664 if (decl.alignment != .none) return decl.alignment;665 if (decl.alignment != .none) return decl.alignment;
665 return decl.ty.abiAlignment(zcu);666 return decl.ty.abiAlignment(zcu);
666 }667 }
668
669 /// Upgrade a `LazySrcLoc` to a `SrcLoc` based on the `Decl` provided.
670 pub fn toSrcLoc(decl: *Decl, lazy: LazySrcLoc, mod: *Module) SrcLoc {
671 return switch (lazy) {
672 .unneeded,
673 .entire_file,
674 .byte_abs,
675 .token_abs,
676 .node_abs,
677 => .{
678 .file_scope = decl.getFileScope(mod),
679 .parent_decl_node = 0,
680 .lazy = lazy,
681 },
682
683 .byte_offset,
684 .token_offset,
685 .node_offset,
686 .node_offset_main_token,
687 .node_offset_initializer,
688 .node_offset_var_decl_ty,
689 .node_offset_var_decl_align,
690 .node_offset_var_decl_section,
691 .node_offset_var_decl_addrspace,
692 .node_offset_var_decl_init,
693 .node_offset_builtin_call_arg0,
694 .node_offset_builtin_call_arg1,
695 .node_offset_builtin_call_arg2,
696 .node_offset_builtin_call_arg3,
697 .node_offset_builtin_call_arg4,
698 .node_offset_builtin_call_arg5,
699 .node_offset_ptrcast_operand,
700 .node_offset_array_access_index,
701 .node_offset_slice_ptr,
702 .node_offset_slice_start,
703 .node_offset_slice_end,
704 .node_offset_slice_sentinel,
705 .node_offset_call_func,
706 .node_offset_field_name,
707 .node_offset_field_name_init,
708 .node_offset_deref_ptr,
709 .node_offset_asm_source,
710 .node_offset_asm_ret_ty,
711 .node_offset_if_cond,
712 .node_offset_bin_op,
713 .node_offset_bin_lhs,
714 .node_offset_bin_rhs,
715 .node_offset_switch_operand,
716 .node_offset_switch_special_prong,
717 .node_offset_switch_range,
718 .node_offset_switch_prong_capture,
719 .node_offset_switch_prong_tag_capture,
720 .node_offset_fn_type_align,
721 .node_offset_fn_type_addrspace,
722 .node_offset_fn_type_section,
723 .node_offset_fn_type_cc,
724 .node_offset_fn_type_ret_ty,
725 .node_offset_param,
726 .token_offset_param,
727 .node_offset_anyframe_type,
728 .node_offset_lib_name,
729 .node_offset_array_type_len,
730 .node_offset_array_type_sentinel,
731 .node_offset_array_type_elem,
732 .node_offset_un_op,
733 .node_offset_ptr_elem,
734 .node_offset_ptr_sentinel,
735 .node_offset_ptr_align,
736 .node_offset_ptr_addrspace,
737 .node_offset_ptr_bitoffset,
738 .node_offset_ptr_hostsize,
739 .node_offset_container_tag,
740 .node_offset_field_default,
741 .node_offset_init_ty,
742 .node_offset_store_ptr,
743 .node_offset_store_operand,
744 .node_offset_return_operand,
745 .for_input,
746 .for_capture_from_input,
747 .array_cat_lhs,
748 .array_cat_rhs,
749 => .{
750 .file_scope = decl.getFileScope(mod),
751 .parent_decl_node = decl.src_node,
752 .lazy = lazy,
753 },
754 inline .call_arg,
755 .fn_proto_param,
756 => |x| .{
757 .file_scope = decl.getFileScope(mod),
758 .parent_decl_node = mod.declPtr(x.decl).src_node,
759 .lazy = lazy,
760 },
761 };
762 }
667};763};
668764
669/// This state is attached to every Decl when Module emit_h is non-null.765/// This state is attached to every Decl when Module emit_h is non-null.
...@@ -1159,11 +1255,7 @@ pub const SrcLoc = struct {...@@ -1159,11 +1255,7 @@ pub const SrcLoc = struct {
1159 return @bitCast(offset + @as(i32, @bitCast(src_loc.parent_decl_node)));1255 return @bitCast(offset + @as(i32, @bitCast(src_loc.parent_decl_node)));
1160 }1256 }
11611257
1162 pub const Span = struct {1258 pub const Span = Ast.Span;
1163 start: u32,
1164 end: u32,
1165 main: u32,
1166 };
11671259
1168 pub fn span(src_loc: SrcLoc, gpa: Allocator) !Span {1260 pub fn span(src_loc: SrcLoc, gpa: Allocator) !Span {
1169 switch (src_loc.lazy) {1261 switch (src_loc.lazy) {
...@@ -1180,7 +1272,7 @@ pub const SrcLoc = struct {...@@ -1180,7 +1272,7 @@ pub const SrcLoc = struct {
1180 },1272 },
1181 .node_abs => |node| {1273 .node_abs => |node| {
1182 const tree = try src_loc.file_scope.getTree(gpa);1274 const tree = try src_loc.file_scope.getTree(gpa);
1183 return nodeToSpan(tree, node);1275 return tree.nodeToSpan(node);
1184 },1276 },
1185 .byte_offset => |byte_off| {1277 .byte_offset => |byte_off| {
1186 const tree = try src_loc.file_scope.getTree(gpa);1278 const tree = try src_loc.file_scope.getTree(gpa);
...@@ -1201,25 +1293,24 @@ pub const SrcLoc = struct {...@@ -1201,25 +1293,24 @@ pub const SrcLoc = struct {
1201 const tree = try src_loc.file_scope.getTree(gpa);1293 const tree = try src_loc.file_scope.getTree(gpa);
1202 const node = src_loc.declRelativeToNodeIndex(node_off);1294 const node = src_loc.declRelativeToNodeIndex(node_off);
1203 assert(src_loc.file_scope.tree_loaded);1295 assert(src_loc.file_scope.tree_loaded);
1204 return nodeToSpan(tree, node);1296 return tree.nodeToSpan(node);
1205 },1297 },
1206 .node_offset_main_token => |node_off| {1298 .node_offset_main_token => |node_off| {
1207 const tree = try src_loc.file_scope.getTree(gpa);1299 const tree = try src_loc.file_scope.getTree(gpa);
1208 const node = src_loc.declRelativeToNodeIndex(node_off);1300 const node = src_loc.declRelativeToNodeIndex(node_off);
1209 const main_token = tree.nodes.items(.main_token)[node];1301 const main_token = tree.nodes.items(.main_token)[node];
1210 return tokensToSpan(tree, main_token, main_token, main_token);1302 return tree.tokensToSpan(main_token, main_token, main_token);
1211 },1303 },
1212 .node_offset_bin_op => |node_off| {1304 .node_offset_bin_op => |node_off| {
1213 const tree = try src_loc.file_scope.getTree(gpa);1305 const tree = try src_loc.file_scope.getTree(gpa);
1214 const node = src_loc.declRelativeToNodeIndex(node_off);1306 const node = src_loc.declRelativeToNodeIndex(node_off);
1215 assert(src_loc.file_scope.tree_loaded);1307 assert(src_loc.file_scope.tree_loaded);
1216 return nodeToSpan(tree, node);1308 return tree.nodeToSpan(node);
1217 },1309 },
1218 .node_offset_initializer => |node_off| {1310 .node_offset_initializer => |node_off| {
1219 const tree = try src_loc.file_scope.getTree(gpa);1311 const tree = try src_loc.file_scope.getTree(gpa);
1220 const node = src_loc.declRelativeToNodeIndex(node_off);1312 const node = src_loc.declRelativeToNodeIndex(node_off);
1221 return tokensToSpan(1313 return tree.tokensToSpan(
1222 tree,
1223 tree.firstToken(node) - 3,1314 tree.firstToken(node) - 3,
1224 tree.lastToken(node),1315 tree.lastToken(node),
1225 tree.nodes.items(.main_token)[node] - 2,1316 tree.nodes.items(.main_token)[node] - 2,
...@@ -1237,12 +1328,12 @@ pub const SrcLoc = struct {...@@ -1237,12 +1328,12 @@ pub const SrcLoc = struct {
1237 => tree.fullVarDecl(node).?,1328 => tree.fullVarDecl(node).?,
1238 .@"usingnamespace" => {1329 .@"usingnamespace" => {
1239 const node_data = tree.nodes.items(.data);1330 const node_data = tree.nodes.items(.data);
1240 return nodeToSpan(tree, node_data[node].lhs);1331 return tree.nodeToSpan(node_data[node].lhs);
1241 },1332 },
1242 else => unreachable,1333 else => unreachable,
1243 };1334 };
1244 if (full.ast.type_node != 0) {1335 if (full.ast.type_node != 0) {
1245 return nodeToSpan(tree, full.ast.type_node);1336 return tree.nodeToSpan(full.ast.type_node);
1246 }1337 }
1247 const tok_index = full.ast.mut_token + 1; // the name token1338 const tok_index = full.ast.mut_token + 1; // the name token
1248 const start = tree.tokens.items(.start)[tok_index];1339 const start = tree.tokens.items(.start)[tok_index];
...@@ -1253,25 +1344,25 @@ pub const SrcLoc = struct {...@@ -1253,25 +1344,25 @@ pub const SrcLoc = struct {
1253 const tree = try src_loc.file_scope.getTree(gpa);1344 const tree = try src_loc.file_scope.getTree(gpa);
1254 const node = src_loc.declRelativeToNodeIndex(node_off);1345 const node = src_loc.declRelativeToNodeIndex(node_off);
1255 const full = tree.fullVarDecl(node).?;1346 const full = tree.fullVarDecl(node).?;
1256 return nodeToSpan(tree, full.ast.align_node);1347 return tree.nodeToSpan(full.ast.align_node);
1257 },1348 },
1258 .node_offset_var_decl_section => |node_off| {1349 .node_offset_var_decl_section => |node_off| {
1259 const tree = try src_loc.file_scope.getTree(gpa);1350 const tree = try src_loc.file_scope.getTree(gpa);
1260 const node = src_loc.declRelativeToNodeIndex(node_off);1351 const node = src_loc.declRelativeToNodeIndex(node_off);
1261 const full = tree.fullVarDecl(node).?;1352 const full = tree.fullVarDecl(node).?;
1262 return nodeToSpan(tree, full.ast.section_node);1353 return tree.nodeToSpan(full.ast.section_node);
1263 },1354 },
1264 .node_offset_var_decl_addrspace => |node_off| {1355 .node_offset_var_decl_addrspace => |node_off| {
1265 const tree = try src_loc.file_scope.getTree(gpa);1356 const tree = try src_loc.file_scope.getTree(gpa);
1266 const node = src_loc.declRelativeToNodeIndex(node_off);1357 const node = src_loc.declRelativeToNodeIndex(node_off);
1267 const full = tree.fullVarDecl(node).?;1358 const full = tree.fullVarDecl(node).?;
1268 return nodeToSpan(tree, full.ast.addrspace_node);1359 return tree.nodeToSpan(full.ast.addrspace_node);
1269 },1360 },
1270 .node_offset_var_decl_init => |node_off| {1361 .node_offset_var_decl_init => |node_off| {
1271 const tree = try src_loc.file_scope.getTree(gpa);1362 const tree = try src_loc.file_scope.getTree(gpa);
1272 const node = src_loc.declRelativeToNodeIndex(node_off);1363 const node = src_loc.declRelativeToNodeIndex(node_off);
1273 const full = tree.fullVarDecl(node).?;1364 const full = tree.fullVarDecl(node).?;
1274 return nodeToSpan(tree, full.ast.init_node);1365 return tree.nodeToSpan(full.ast.init_node);
1275 },1366 },
1276 .node_offset_builtin_call_arg0 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 0),1367 .node_offset_builtin_call_arg0 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 0),
1277 .node_offset_builtin_call_arg1 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 1),1368 .node_offset_builtin_call_arg1 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 1),
...@@ -1312,13 +1403,13 @@ pub const SrcLoc = struct {...@@ -1312,13 +1403,13 @@ pub const SrcLoc = struct {
1312 node = node_datas[node].lhs;1403 node = node_datas[node].lhs;
1313 }1404 }
13141405
1315 return nodeToSpan(tree, node);1406 return tree.nodeToSpan(node);
1316 },1407 },
1317 .node_offset_array_access_index => |node_off| {1408 .node_offset_array_access_index => |node_off| {
1318 const tree = try src_loc.file_scope.getTree(gpa);1409 const tree = try src_loc.file_scope.getTree(gpa);
1319 const node_datas = tree.nodes.items(.data);1410 const node_datas = tree.nodes.items(.data);
1320 const node = src_loc.declRelativeToNodeIndex(node_off);1411 const node = src_loc.declRelativeToNodeIndex(node_off);
1321 return nodeToSpan(tree, node_datas[node].rhs);1412 return tree.nodeToSpan(node_datas[node].rhs);
1322 },1413 },
1323 .node_offset_slice_ptr,1414 .node_offset_slice_ptr,
1324 .node_offset_slice_start,1415 .node_offset_slice_start,
...@@ -1335,14 +1426,14 @@ pub const SrcLoc = struct {...@@ -1335,14 +1426,14 @@ pub const SrcLoc = struct {
1335 .node_offset_slice_sentinel => full.ast.sentinel,1426 .node_offset_slice_sentinel => full.ast.sentinel,
1336 else => unreachable,1427 else => unreachable,
1337 };1428 };
1338 return nodeToSpan(tree, part_node);1429 return tree.nodeToSpan(part_node);
1339 },1430 },
1340 .node_offset_call_func => |node_off| {1431 .node_offset_call_func => |node_off| {
1341 const tree = try src_loc.file_scope.getTree(gpa);1432 const tree = try src_loc.file_scope.getTree(gpa);
1342 const node = src_loc.declRelativeToNodeIndex(node_off);1433 const node = src_loc.declRelativeToNodeIndex(node_off);
1343 var buf: [1]Ast.Node.Index = undefined;1434 var buf: [1]Ast.Node.Index = undefined;
1344 const full = tree.fullCall(&buf, node).?;1435 const full = tree.fullCall(&buf, node).?;
1345 return nodeToSpan(tree, full.ast.fn_expr);1436 return tree.nodeToSpan(full.ast.fn_expr);
1346 },1437 },
1347 .node_offset_field_name => |node_off| {1438 .node_offset_field_name => |node_off| {
1348 const tree = try src_loc.file_scope.getTree(gpa);1439 const tree = try src_loc.file_scope.getTree(gpa);
...@@ -1381,13 +1472,13 @@ pub const SrcLoc = struct {...@@ -1381,13 +1472,13 @@ pub const SrcLoc = struct {
1381 .node_offset_deref_ptr => |node_off| {1472 .node_offset_deref_ptr => |node_off| {
1382 const tree = try src_loc.file_scope.getTree(gpa);1473 const tree = try src_loc.file_scope.getTree(gpa);
1383 const node = src_loc.declRelativeToNodeIndex(node_off);1474 const node = src_loc.declRelativeToNodeIndex(node_off);
1384 return nodeToSpan(tree, node);1475 return tree.nodeToSpan(node);
1385 },1476 },
1386 .node_offset_asm_source => |node_off| {1477 .node_offset_asm_source => |node_off| {
1387 const tree = try src_loc.file_scope.getTree(gpa);1478 const tree = try src_loc.file_scope.getTree(gpa);
1388 const node = src_loc.declRelativeToNodeIndex(node_off);1479 const node = src_loc.declRelativeToNodeIndex(node_off);
1389 const full = tree.fullAsm(node).?;1480 const full = tree.fullAsm(node).?;
1390 return nodeToSpan(tree, full.ast.template);1481 return tree.nodeToSpan(full.ast.template);
1391 },1482 },
1392 .node_offset_asm_ret_ty => |node_off| {1483 .node_offset_asm_ret_ty => |node_off| {
1393 const tree = try src_loc.file_scope.getTree(gpa);1484 const tree = try src_loc.file_scope.getTree(gpa);
...@@ -1395,7 +1486,7 @@ pub const SrcLoc = struct {...@@ -1395,7 +1486,7 @@ pub const SrcLoc = struct {
1395 const full = tree.fullAsm(node).?;1486 const full = tree.fullAsm(node).?;
1396 const asm_output = full.outputs[0];1487 const asm_output = full.outputs[0];
1397 const node_datas = tree.nodes.items(.data);1488 const node_datas = tree.nodes.items(.data);
1398 return nodeToSpan(tree, node_datas[asm_output].lhs);1489 return tree.nodeToSpan(node_datas[asm_output].lhs);
1399 },1490 },
14001491
1401 .node_offset_if_cond => |node_off| {1492 .node_offset_if_cond => |node_off| {
...@@ -1418,21 +1509,21 @@ pub const SrcLoc = struct {...@@ -1418,21 +1509,21 @@ pub const SrcLoc = struct {
1418 const inputs = tree.fullFor(node).?.ast.inputs;1509 const inputs = tree.fullFor(node).?.ast.inputs;
1419 const start = tree.firstToken(inputs[0]);1510 const start = tree.firstToken(inputs[0]);
1420 const end = tree.lastToken(inputs[inputs.len - 1]);1511 const end = tree.lastToken(inputs[inputs.len - 1]);
1421 return tokensToSpan(tree, start, end, start);1512 return tree.tokensToSpan(start, end, start);
1422 },1513 },
14231514
1424 .@"orelse" => node,1515 .@"orelse" => node,
1425 .@"catch" => node,1516 .@"catch" => node,
1426 else => unreachable,1517 else => unreachable,
1427 };1518 };
1428 return nodeToSpan(tree, src_node);1519 return tree.nodeToSpan(src_node);
1429 },1520 },
1430 .for_input => |for_input| {1521 .for_input => |for_input| {
1431 const tree = try src_loc.file_scope.getTree(gpa);1522 const tree = try src_loc.file_scope.getTree(gpa);
1432 const node = src_loc.declRelativeToNodeIndex(for_input.for_node_offset);1523 const node = src_loc.declRelativeToNodeIndex(for_input.for_node_offset);
1433 const for_full = tree.fullFor(node).?;1524 const for_full = tree.fullFor(node).?;
1434 const src_node = for_full.ast.inputs[for_input.input_index];1525 const src_node = for_full.ast.inputs[for_input.input_index];
1435 return nodeToSpan(tree, src_node);1526 return tree.nodeToSpan(src_node);
1436 },1527 },
1437 .for_capture_from_input => |node_off| {1528 .for_capture_from_input => |node_off| {
1438 const tree = try src_loc.file_scope.getTree(gpa);1529 const tree = try src_loc.file_scope.getTree(gpa);
...@@ -1458,12 +1549,12 @@ pub const SrcLoc = struct {...@@ -1458,12 +1549,12 @@ pub const SrcLoc = struct {
1458 },1549 },
1459 .identifier => {1550 .identifier => {
1460 if (count == 0)1551 if (count == 0)
1461 return tokensToSpan(tree, tok, tok + 1, tok);1552 return tree.tokensToSpan(tok, tok + 1, tok);
1462 tok += 1;1553 tok += 1;
1463 },1554 },
1464 .asterisk => {1555 .asterisk => {
1465 if (count == 0)1556 if (count == 0)
1466 return tokensToSpan(tree, tok, tok + 2, tok);1557 return tree.tokensToSpan(tok, tok + 2, tok);
1467 tok += 1;1558 tok += 1;
1468 },1559 },
1469 else => unreachable,1560 else => unreachable,
...@@ -1495,7 +1586,7 @@ pub const SrcLoc = struct {...@@ -1495,7 +1586,7 @@ pub const SrcLoc = struct {
1495 .array_init_comma,1586 .array_init_comma,
1496 => {1587 => {
1497 const full = tree.fullArrayInit(&buf, call_args_node).?.ast.elements;1588 const full = tree.fullArrayInit(&buf, call_args_node).?.ast.elements;
1498 return nodeToSpan(tree, full[call_arg.arg_index]);1589 return tree.nodeToSpan(full[call_arg.arg_index]);
1499 },1590 },
1500 .struct_init_one,1591 .struct_init_one,
1501 .struct_init_one_comma,1592 .struct_init_one_comma,
...@@ -1507,12 +1598,12 @@ pub const SrcLoc = struct {...@@ -1507,12 +1598,12 @@ pub const SrcLoc = struct {
1507 .struct_init_comma,1598 .struct_init_comma,
1508 => {1599 => {
1509 const full = tree.fullStructInit(&buf, call_args_node).?.ast.fields;1600 const full = tree.fullStructInit(&buf, call_args_node).?.ast.fields;
1510 return nodeToSpan(tree, full[call_arg.arg_index]);1601 return tree.nodeToSpan(full[call_arg.arg_index]);
1511 },1602 },
1512 else => return nodeToSpan(tree, call_args_node),1603 else => return tree.nodeToSpan(call_args_node),
1513 }1604 }
1514 };1605 };
1515 return nodeToSpan(tree, call_full.ast.params[call_arg.arg_index]);1606 return tree.nodeToSpan(call_full.ast.params[call_arg.arg_index]);
1516 },1607 },
1517 .fn_proto_param => |fn_proto_param| {1608 .fn_proto_param => |fn_proto_param| {
1518 const tree = try src_loc.file_scope.getTree(gpa);1609 const tree = try src_loc.file_scope.getTree(gpa);
...@@ -1523,12 +1614,11 @@ pub const SrcLoc = struct {...@@ -1523,12 +1614,11 @@ pub const SrcLoc = struct {
1523 var i: usize = 0;1614 var i: usize = 0;
1524 while (it.next()) |param| : (i += 1) {1615 while (it.next()) |param| : (i += 1) {
1525 if (i == fn_proto_param.param_index) {1616 if (i == fn_proto_param.param_index) {
1526 if (param.anytype_ellipsis3) |token| return tokenToSpan(tree, token);1617 if (param.anytype_ellipsis3) |token| return tree.tokenToSpan(token);
1527 const first_token = param.comptime_noalias orelse1618 const first_token = param.comptime_noalias orelse
1528 param.name_token orelse1619 param.name_token orelse
1529 tree.firstToken(param.type_expr);1620 tree.firstToken(param.type_expr);
1530 return tokensToSpan(1621 return tree.tokensToSpan(
1531 tree,
1532 first_token,1622 first_token,
1533 tree.lastToken(param.type_expr),1623 tree.lastToken(param.type_expr),
1534 first_token,1624 first_token,
...@@ -1541,13 +1631,13 @@ pub const SrcLoc = struct {...@@ -1541,13 +1631,13 @@ pub const SrcLoc = struct {
1541 const tree = try src_loc.file_scope.getTree(gpa);1631 const tree = try src_loc.file_scope.getTree(gpa);
1542 const node = src_loc.declRelativeToNodeIndex(node_off);1632 const node = src_loc.declRelativeToNodeIndex(node_off);
1543 const node_datas = tree.nodes.items(.data);1633 const node_datas = tree.nodes.items(.data);
1544 return nodeToSpan(tree, node_datas[node].lhs);1634 return tree.nodeToSpan(node_datas[node].lhs);
1545 },1635 },
1546 .node_offset_bin_rhs => |node_off| {1636 .node_offset_bin_rhs => |node_off| {
1547 const tree = try src_loc.file_scope.getTree(gpa);1637 const tree = try src_loc.file_scope.getTree(gpa);
1548 const node = src_loc.declRelativeToNodeIndex(node_off);1638 const node = src_loc.declRelativeToNodeIndex(node_off);
1549 const node_datas = tree.nodes.items(.data);1639 const node_datas = tree.nodes.items(.data);
1550 return nodeToSpan(tree, node_datas[node].rhs);1640 return tree.nodeToSpan(node_datas[node].rhs);
1551 },1641 },
1552 .array_cat_lhs, .array_cat_rhs => |cat| {1642 .array_cat_lhs, .array_cat_rhs => |cat| {
1553 const tree = try src_loc.file_scope.getTree(gpa);1643 const tree = try src_loc.file_scope.getTree(gpa);
...@@ -1571,9 +1661,9 @@ pub const SrcLoc = struct {...@@ -1571,9 +1661,9 @@ pub const SrcLoc = struct {
1571 .array_init_comma,1661 .array_init_comma,
1572 => {1662 => {
1573 const full = tree.fullArrayInit(&buf, arr_node).?.ast.elements;1663 const full = tree.fullArrayInit(&buf, arr_node).?.ast.elements;
1574 return nodeToSpan(tree, full[cat.elem_index]);1664 return tree.nodeToSpan(full[cat.elem_index]);
1575 },1665 },
1576 else => return nodeToSpan(tree, arr_node),1666 else => return tree.nodeToSpan(arr_node),
1577 }1667 }
1578 },1668 },
15791669
...@@ -1581,7 +1671,7 @@ pub const SrcLoc = struct {...@@ -1581,7 +1671,7 @@ pub const SrcLoc = struct {
1581 const tree = try src_loc.file_scope.getTree(gpa);1671 const tree = try src_loc.file_scope.getTree(gpa);
1582 const node = src_loc.declRelativeToNodeIndex(node_off);1672 const node = src_loc.declRelativeToNodeIndex(node_off);
1583 const node_datas = tree.nodes.items(.data);1673 const node_datas = tree.nodes.items(.data);
1584 return nodeToSpan(tree, node_datas[node].lhs);1674 return tree.nodeToSpan(node_datas[node].lhs);
1585 },1675 },
15861676
1587 .node_offset_switch_special_prong => |node_off| {1677 .node_offset_switch_special_prong => |node_off| {
...@@ -1600,7 +1690,7 @@ pub const SrcLoc = struct {...@@ -1600,7 +1690,7 @@ pub const SrcLoc = struct {
1600 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"));1690 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"));
1601 if (!is_special) continue;1691 if (!is_special) continue;
16021692
1603 return nodeToSpan(tree, case_node);1693 return tree.nodeToSpan(case_node);
1604 } else unreachable;1694 } else unreachable;
1605 },1695 },
16061696
...@@ -1622,7 +1712,7 @@ pub const SrcLoc = struct {...@@ -1622,7 +1712,7 @@ pub const SrcLoc = struct {
16221712
1623 for (case.ast.values) |item_node| {1713 for (case.ast.values) |item_node| {
1624 if (node_tags[item_node] == .switch_range) {1714 if (node_tags[item_node] == .switch_range) {
1625 return nodeToSpan(tree, item_node);1715 return tree.nodeToSpan(item_node);
1626 }1716 }
1627 }1717 }
1628 } else unreachable;1718 } else unreachable;
...@@ -1658,28 +1748,28 @@ pub const SrcLoc = struct {...@@ -1658,28 +1748,28 @@ pub const SrcLoc = struct {
1658 const node = src_loc.declRelativeToNodeIndex(node_off);1748 const node = src_loc.declRelativeToNodeIndex(node_off);
1659 var buf: [1]Ast.Node.Index = undefined;1749 var buf: [1]Ast.Node.Index = undefined;
1660 const full = tree.fullFnProto(&buf, node).?;1750 const full = tree.fullFnProto(&buf, node).?;
1661 return nodeToSpan(tree, full.ast.align_expr);1751 return tree.nodeToSpan(full.ast.align_expr);
1662 },1752 },
1663 .node_offset_fn_type_addrspace => |node_off| {1753 .node_offset_fn_type_addrspace => |node_off| {
1664 const tree = try src_loc.file_scope.getTree(gpa);1754 const tree = try src_loc.file_scope.getTree(gpa);
1665 const node = src_loc.declRelativeToNodeIndex(node_off);1755 const node = src_loc.declRelativeToNodeIndex(node_off);
1666 var buf: [1]Ast.Node.Index = undefined;1756 var buf: [1]Ast.Node.Index = undefined;
1667 const full = tree.fullFnProto(&buf, node).?;1757 const full = tree.fullFnProto(&buf, node).?;
1668 return nodeToSpan(tree, full.ast.addrspace_expr);1758 return tree.nodeToSpan(full.ast.addrspace_expr);
1669 },1759 },
1670 .node_offset_fn_type_section => |node_off| {1760 .node_offset_fn_type_section => |node_off| {
1671 const tree = try src_loc.file_scope.getTree(gpa);1761 const tree = try src_loc.file_scope.getTree(gpa);
1672 const node = src_loc.declRelativeToNodeIndex(node_off);1762 const node = src_loc.declRelativeToNodeIndex(node_off);
1673 var buf: [1]Ast.Node.Index = undefined;1763 var buf: [1]Ast.Node.Index = undefined;
1674 const full = tree.fullFnProto(&buf, node).?;1764 const full = tree.fullFnProto(&buf, node).?;
1675 return nodeToSpan(tree, full.ast.section_expr);1765 return tree.nodeToSpan(full.ast.section_expr);
1676 },1766 },
1677 .node_offset_fn_type_cc => |node_off| {1767 .node_offset_fn_type_cc => |node_off| {
1678 const tree = try src_loc.file_scope.getTree(gpa);1768 const tree = try src_loc.file_scope.getTree(gpa);
1679 const node = src_loc.declRelativeToNodeIndex(node_off);1769 const node = src_loc.declRelativeToNodeIndex(node_off);
1680 var buf: [1]Ast.Node.Index = undefined;1770 var buf: [1]Ast.Node.Index = undefined;
1681 const full = tree.fullFnProto(&buf, node).?;1771 const full = tree.fullFnProto(&buf, node).?;
1682 return nodeToSpan(tree, full.ast.callconv_expr);1772 return tree.nodeToSpan(full.ast.callconv_expr);
1683 },1773 },
16841774
1685 .node_offset_fn_type_ret_ty => |node_off| {1775 .node_offset_fn_type_ret_ty => |node_off| {
...@@ -1687,7 +1777,7 @@ pub const SrcLoc = struct {...@@ -1687,7 +1777,7 @@ pub const SrcLoc = struct {
1687 const node = src_loc.declRelativeToNodeIndex(node_off);1777 const node = src_loc.declRelativeToNodeIndex(node_off);
1688 var buf: [1]Ast.Node.Index = undefined;1778 var buf: [1]Ast.Node.Index = undefined;
1689 const full = tree.fullFnProto(&buf, node).?;1779 const full = tree.fullFnProto(&buf, node).?;
1690 return nodeToSpan(tree, full.ast.return_type);1780 return tree.nodeToSpan(full.ast.return_type);
1691 },1781 },
1692 .node_offset_param => |node_off| {1782 .node_offset_param => |node_off| {
1693 const tree = try src_loc.file_scope.getTree(gpa);1783 const tree = try src_loc.file_scope.getTree(gpa);
...@@ -1699,8 +1789,7 @@ pub const SrcLoc = struct {...@@ -1699,8 +1789,7 @@ pub const SrcLoc = struct {
1699 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1,1789 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1,
1700 else => break,1790 else => break,
1701 };1791 };
1702 return tokensToSpan(1792 return tree.tokensToSpan(
1703 tree,
1704 first_tok,1793 first_tok,
1705 tree.lastToken(node),1794 tree.lastToken(node),
1706 first_tok,1795 first_tok,
...@@ -1717,8 +1806,7 @@ pub const SrcLoc = struct {...@@ -1717,8 +1806,7 @@ pub const SrcLoc = struct {
1717 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1,1806 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1,
1718 else => break,1807 else => break,
1719 };1808 };
1720 return tokensToSpan(1809 return tree.tokensToSpan(
1721 tree,
1722 first_tok,1810 first_tok,
1723 tok_index,1811 tok_index,
1724 first_tok,1812 first_tok,
...@@ -1729,7 +1817,7 @@ pub const SrcLoc = struct {...@@ -1729,7 +1817,7 @@ pub const SrcLoc = struct {
1729 const tree = try src_loc.file_scope.getTree(gpa);1817 const tree = try src_loc.file_scope.getTree(gpa);
1730 const node_datas = tree.nodes.items(.data);1818 const node_datas = tree.nodes.items(.data);
1731 const parent_node = src_loc.declRelativeToNodeIndex(node_off);1819 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
1732 return nodeToSpan(tree, node_datas[parent_node].rhs);1820 return tree.nodeToSpan(node_datas[parent_node].rhs);
1733 },1821 },
17341822
1735 .node_offset_lib_name => |node_off| {1823 .node_offset_lib_name => |node_off| {
...@@ -1748,70 +1836,70 @@ pub const SrcLoc = struct {...@@ -1748,70 +1836,70 @@ pub const SrcLoc = struct {
1748 const parent_node = src_loc.declRelativeToNodeIndex(node_off);1836 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
17491837
1750 const full = tree.fullArrayType(parent_node).?;1838 const full = tree.fullArrayType(parent_node).?;
1751 return nodeToSpan(tree, full.ast.elem_count);1839 return tree.nodeToSpan(full.ast.elem_count);
1752 },1840 },
1753 .node_offset_array_type_sentinel => |node_off| {1841 .node_offset_array_type_sentinel => |node_off| {
1754 const tree = try src_loc.file_scope.getTree(gpa);1842 const tree = try src_loc.file_scope.getTree(gpa);
1755 const parent_node = src_loc.declRelativeToNodeIndex(node_off);1843 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
17561844
1757 const full = tree.fullArrayType(parent_node).?;1845 const full = tree.fullArrayType(parent_node).?;
1758 return nodeToSpan(tree, full.ast.sentinel);1846 return tree.nodeToSpan(full.ast.sentinel);
1759 },1847 },
1760 .node_offset_array_type_elem => |node_off| {1848 .node_offset_array_type_elem => |node_off| {
1761 const tree = try src_loc.file_scope.getTree(gpa);1849 const tree = try src_loc.file_scope.getTree(gpa);
1762 const parent_node = src_loc.declRelativeToNodeIndex(node_off);1850 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
17631851
1764 const full = tree.fullArrayType(parent_node).?;1852 const full = tree.fullArrayType(parent_node).?;
1765 return nodeToSpan(tree, full.ast.elem_type);1853 return tree.nodeToSpan(full.ast.elem_type);
1766 },1854 },
1767 .node_offset_un_op => |node_off| {1855 .node_offset_un_op => |node_off| {
1768 const tree = try src_loc.file_scope.getTree(gpa);1856 const tree = try src_loc.file_scope.getTree(gpa);
1769 const node_datas = tree.nodes.items(.data);1857 const node_datas = tree.nodes.items(.data);
1770 const node = src_loc.declRelativeToNodeIndex(node_off);1858 const node = src_loc.declRelativeToNodeIndex(node_off);
17711859
1772 return nodeToSpan(tree, node_datas[node].lhs);1860 return tree.nodeToSpan(node_datas[node].lhs);
1773 },1861 },
1774 .node_offset_ptr_elem => |node_off| {1862 .node_offset_ptr_elem => |node_off| {
1775 const tree = try src_loc.file_scope.getTree(gpa);1863 const tree = try src_loc.file_scope.getTree(gpa);
1776 const parent_node = src_loc.declRelativeToNodeIndex(node_off);1864 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
17771865
1778 const full = tree.fullPtrType(parent_node).?;1866 const full = tree.fullPtrType(parent_node).?;
1779 return nodeToSpan(tree, full.ast.child_type);1867 return tree.nodeToSpan(full.ast.child_type);
1780 },1868 },
1781 .node_offset_ptr_sentinel => |node_off| {1869 .node_offset_ptr_sentinel => |node_off| {
1782 const tree = try src_loc.file_scope.getTree(gpa);1870 const tree = try src_loc.file_scope.getTree(gpa);
1783 const parent_node = src_loc.declRelativeToNodeIndex(node_off);1871 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
17841872
1785 const full = tree.fullPtrType(parent_node).?;1873 const full = tree.fullPtrType(parent_node).?;
1786 return nodeToSpan(tree, full.ast.sentinel);1874 return tree.nodeToSpan(full.ast.sentinel);
1787 },1875 },
1788 .node_offset_ptr_align => |node_off| {1876 .node_offset_ptr_align => |node_off| {
1789 const tree = try src_loc.file_scope.getTree(gpa);1877 const tree = try src_loc.file_scope.getTree(gpa);
1790 const parent_node = src_loc.declRelativeToNodeIndex(node_off);1878 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
17911879
1792 const full = tree.fullPtrType(parent_node).?;1880 const full = tree.fullPtrType(parent_node).?;
1793 return nodeToSpan(tree, full.ast.align_node);1881 return tree.nodeToSpan(full.ast.align_node);
1794 },1882 },
1795 .node_offset_ptr_addrspace => |node_off| {1883 .node_offset_ptr_addrspace => |node_off| {
1796 const tree = try src_loc.file_scope.getTree(gpa);1884 const tree = try src_loc.file_scope.getTree(gpa);
1797 const parent_node = src_loc.declRelativeToNodeIndex(node_off);1885 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
17981886
1799 const full = tree.fullPtrType(parent_node).?;1887 const full = tree.fullPtrType(parent_node).?;
1800 return nodeToSpan(tree, full.ast.addrspace_node);1888 return tree.nodeToSpan(full.ast.addrspace_node);
1801 },1889 },
1802 .node_offset_ptr_bitoffset => |node_off| {1890 .node_offset_ptr_bitoffset => |node_off| {
1803 const tree = try src_loc.file_scope.getTree(gpa);1891 const tree = try src_loc.file_scope.getTree(gpa);
1804 const parent_node = src_loc.declRelativeToNodeIndex(node_off);1892 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
18051893
1806 const full = tree.fullPtrType(parent_node).?;1894 const full = tree.fullPtrType(parent_node).?;
1807 return nodeToSpan(tree, full.ast.bit_range_start);1895 return tree.nodeToSpan(full.ast.bit_range_start);
1808 },1896 },
1809 .node_offset_ptr_hostsize => |node_off| {1897 .node_offset_ptr_hostsize => |node_off| {
1810 const tree = try src_loc.file_scope.getTree(gpa);1898 const tree = try src_loc.file_scope.getTree(gpa);
1811 const parent_node = src_loc.declRelativeToNodeIndex(node_off);1899 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
18121900
1813 const full = tree.fullPtrType(parent_node).?;1901 const full = tree.fullPtrType(parent_node).?;
1814 return nodeToSpan(tree, full.ast.bit_range_end);1902 return tree.nodeToSpan(full.ast.bit_range_end);
1815 },1903 },
1816 .node_offset_container_tag => |node_off| {1904 .node_offset_container_tag => |node_off| {
1817 const tree = try src_loc.file_scope.getTree(gpa);1905 const tree = try src_loc.file_scope.getTree(gpa);
...@@ -1821,13 +1909,12 @@ pub const SrcLoc = struct {...@@ -1821,13 +1909,12 @@ pub const SrcLoc = struct {
1821 switch (node_tags[parent_node]) {1909 switch (node_tags[parent_node]) {
1822 .container_decl_arg, .container_decl_arg_trailing => {1910 .container_decl_arg, .container_decl_arg_trailing => {
1823 const full = tree.containerDeclArg(parent_node);1911 const full = tree.containerDeclArg(parent_node);
1824 return nodeToSpan(tree, full.ast.arg);1912 return tree.nodeToSpan(full.ast.arg);
1825 },1913 },
1826 .tagged_union_enum_tag, .tagged_union_enum_tag_trailing => {1914 .tagged_union_enum_tag, .tagged_union_enum_tag_trailing => {
1827 const full = tree.taggedUnionEnumTag(parent_node);1915 const full = tree.taggedUnionEnumTag(parent_node);
18281916
1829 return tokensToSpan(1917 return tree.tokensToSpan(
1830 tree,
1831 tree.firstToken(full.ast.arg) - 2,1918 tree.firstToken(full.ast.arg) - 2,
1832 tree.lastToken(full.ast.arg) + 1,1919 tree.lastToken(full.ast.arg) + 1,
1833 tree.nodes.items(.main_token)[full.ast.arg],1920 tree.nodes.items(.main_token)[full.ast.arg],
...@@ -1846,7 +1933,7 @@ pub const SrcLoc = struct {...@@ -1846,7 +1933,7 @@ pub const SrcLoc = struct {
1846 .container_field_init => tree.containerFieldInit(parent_node),1933 .container_field_init => tree.containerFieldInit(parent_node),
1847 else => unreachable,1934 else => unreachable,
1848 };1935 };
1849 return nodeToSpan(tree, full.ast.value_expr);1936 return tree.nodeToSpan(full.ast.value_expr);
1850 },1937 },
1851 .node_offset_init_ty => |node_off| {1938 .node_offset_init_ty => |node_off| {
1852 const tree = try src_loc.file_scope.getTree(gpa);1939 const tree = try src_loc.file_scope.getTree(gpa);
...@@ -1854,7 +1941,7 @@ pub const SrcLoc = struct {...@@ -1854,7 +1941,7 @@ pub const SrcLoc = struct {
18541941
1855 var buf: [2]Ast.Node.Index = undefined;1942 var buf: [2]Ast.Node.Index = undefined;
1856 const full = tree.fullArrayInit(&buf, parent_node).?;1943 const full = tree.fullArrayInit(&buf, parent_node).?;
1857 return nodeToSpan(tree, full.ast.type_expr);1944 return tree.nodeToSpan(full.ast.type_expr);
1858 },1945 },
1859 .node_offset_store_ptr => |node_off| {1946 .node_offset_store_ptr => |node_off| {
1860 const tree = try src_loc.file_scope.getTree(gpa);1947 const tree = try src_loc.file_scope.getTree(gpa);
...@@ -1864,9 +1951,9 @@ pub const SrcLoc = struct {...@@ -1864,9 +1951,9 @@ pub const SrcLoc = struct {
18641951
1865 switch (node_tags[node]) {1952 switch (node_tags[node]) {
1866 .assign => {1953 .assign => {
1867 return nodeToSpan(tree, node_datas[node].lhs);1954 return tree.nodeToSpan(node_datas[node].lhs);
1868 },1955 },
1869 else => return nodeToSpan(tree, node),1956 else => return tree.nodeToSpan(node),
1870 }1957 }
1871 },1958 },
1872 .node_offset_store_operand => |node_off| {1959 .node_offset_store_operand => |node_off| {
...@@ -1877,9 +1964,9 @@ pub const SrcLoc = struct {...@@ -1877,9 +1964,9 @@ pub const SrcLoc = struct {
18771964
1878 switch (node_tags[node]) {1965 switch (node_tags[node]) {
1879 .assign => {1966 .assign => {
1880 return nodeToSpan(tree, node_datas[node].rhs);1967 return tree.nodeToSpan(node_datas[node].rhs);
1881 },1968 },
1882 else => return nodeToSpan(tree, node),1969 else => return tree.nodeToSpan(node),
1883 }1970 }
1884 },1971 },
1885 .node_offset_return_operand => |node_off| {1972 .node_offset_return_operand => |node_off| {
...@@ -1888,9 +1975,9 @@ pub const SrcLoc = struct {...@@ -1888,9 +1975,9 @@ pub const SrcLoc = struct {
1888 const node_tags = tree.nodes.items(.tag);1975 const node_tags = tree.nodes.items(.tag);
1889 const node_datas = tree.nodes.items(.data);1976 const node_datas = tree.nodes.items(.data);
1890 if (node_tags[node] == .@"return" and node_datas[node].lhs != 0) {1977 if (node_tags[node] == .@"return" and node_datas[node].lhs != 0) {
1891 return nodeToSpan(tree, node_datas[node].lhs);1978 return tree.nodeToSpan(node_datas[node].lhs);
1892 }1979 }
1893 return nodeToSpan(tree, node);1980 return tree.nodeToSpan(node);
1894 },1981 },
1895 }1982 }
1896 }1983 }
...@@ -1914,486 +2001,7 @@ pub const SrcLoc = struct {...@@ -1914,486 +2001,7 @@ pub const SrcLoc = struct {
1914 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + arg_index],2001 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + arg_index],
1915 else => unreachable,2002 else => unreachable,
1916 };2003 };
1917 return nodeToSpan(tree, param);2004 return tree.nodeToSpan(param);
1918 }
1919
1920 pub fn nodeToSpan(tree: *const Ast, node: u32) Span {
1921 return tokensToSpan(
1922 tree,
1923 tree.firstToken(node),
1924 tree.lastToken(node),
1925 tree.nodes.items(.main_token)[node],
1926 );
1927 }
1928
1929 fn tokenToSpan(tree: *const Ast, token: Ast.TokenIndex) Span {
1930 return tokensToSpan(tree, token, token, token);
1931 }
1932
1933 fn tokensToSpan(tree: *const Ast, start: Ast.TokenIndex, end: Ast.TokenIndex, main: Ast.TokenIndex) Span {
1934 const token_starts = tree.tokens.items(.start);
1935 var start_tok = start;
1936 var end_tok = end;
1937
1938 if (tree.tokensOnSameLine(start, end)) {
1939 // do nothing
1940 } else if (tree.tokensOnSameLine(start, main)) {
1941 end_tok = main;
1942 } else if (tree.tokensOnSameLine(main, end)) {
1943 start_tok = main;
1944 } else {
1945 start_tok = main;
1946 end_tok = main;
1947 }
1948 const start_off = token_starts[start_tok];
1949 const end_off = token_starts[end_tok] + @as(u32, @intCast(tree.tokenSlice(end_tok).len));
1950 return Span{ .start = start_off, .end = end_off, .main = token_starts[main] };
1951 }
1952};
1953
1954/// This wraps a simple integer in debug builds so that later on we can find out
1955/// where in semantic analysis the value got set.
1956const TracedOffset = struct {
1957 x: i32,
1958 trace: std.debug.Trace = .{},
1959
1960 const want_tracing = build_options.value_tracing;
1961};
1962
1963/// Resolving a source location into a byte offset may require doing work
1964/// that we would rather not do unless the error actually occurs.
1965/// Therefore we need a data structure that contains the information necessary
1966/// to lazily produce a `SrcLoc` as required.
1967/// Most of the offsets in this data structure are relative to the containing Decl.
1968/// This makes the source location resolve properly even when a Decl gets
1969/// shifted up or down in the file, as long as the Decl's contents itself
1970/// do not change.
1971pub const LazySrcLoc = union(enum) {
1972 /// When this tag is set, the code that constructed this `LazySrcLoc` is asserting
1973 /// that all code paths which would need to resolve the source location are
1974 /// unreachable. If you are debugging this tag incorrectly being this value,
1975 /// look into using reverse-continue with a memory watchpoint to see where the
1976 /// value is being set to this tag.
1977 unneeded,
1978 /// Means the source location points to an entire file; not any particular
1979 /// location within the file. `file_scope` union field will be active.
1980 entire_file,
1981 /// The source location points to a byte offset within a source file,
1982 /// offset from 0. The source file is determined contextually.
1983 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
1984 byte_abs: u32,
1985 /// The source location points to a token within a source file,
1986 /// offset from 0. The source file is determined contextually.
1987 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
1988 token_abs: u32,
1989 /// The source location points to an AST node within a source file,
1990 /// offset from 0. The source file is determined contextually.
1991 /// Inside a `SrcLoc`, the `file_scope` union field will be active.
1992 node_abs: u32,
1993 /// The source location points to a byte offset within a source file,
1994 /// offset from the byte offset of the Decl within the file.
1995 /// The Decl is determined contextually.
1996 byte_offset: u32,
1997 /// This data is the offset into the token list from the Decl token.
1998 /// The Decl is determined contextually.
1999 token_offset: u32,
2000 /// The source location points to an AST node, which is this value offset
2001 /// from its containing Decl node AST index.
2002 /// The Decl is determined contextually.
2003 node_offset: TracedOffset,
2004 /// The source location points to the main token of an AST node, found
2005 /// by taking this AST node index offset from the containing Decl AST node.
2006 /// The Decl is determined contextually.
2007 node_offset_main_token: i32,
2008 /// The source location points to the beginning of a struct initializer.
2009 /// The Decl is determined contextually.
2010 node_offset_initializer: i32,
2011 /// The source location points to a variable declaration type expression,
2012 /// found by taking this AST node index offset from the containing
2013 /// Decl AST node, which points to a variable declaration AST node. Next, navigate
2014 /// to the type expression.
2015 /// The Decl is determined contextually.
2016 node_offset_var_decl_ty: i32,
2017 /// The source location points to the alignment expression of a var decl.
2018 /// The Decl is determined contextually.
2019 node_offset_var_decl_align: i32,
2020 /// The source location points to the linksection expression of a var decl.
2021 /// The Decl is determined contextually.
2022 node_offset_var_decl_section: i32,
2023 /// The source location points to the addrspace expression of a var decl.
2024 /// The Decl is determined contextually.
2025 node_offset_var_decl_addrspace: i32,
2026 /// The source location points to the initializer of a var decl.
2027 /// The Decl is determined contextually.
2028 node_offset_var_decl_init: i32,
2029 /// The source location points to the first parameter of a builtin
2030 /// function call, found by taking this AST node index offset from the containing
2031 /// Decl AST node, which points to a builtin call AST node. Next, navigate
2032 /// to the first parameter.
2033 /// The Decl is determined contextually.
2034 node_offset_builtin_call_arg0: i32,
2035 /// Same as `node_offset_builtin_call_arg0` except arg index 1.
2036 node_offset_builtin_call_arg1: i32,
2037 node_offset_builtin_call_arg2: i32,
2038 node_offset_builtin_call_arg3: i32,
2039 node_offset_builtin_call_arg4: i32,
2040 node_offset_builtin_call_arg5: i32,
2041 /// Like `node_offset_builtin_call_arg0` but recurses through arbitrarily many calls
2042 /// to pointer cast builtins.
2043 node_offset_ptrcast_operand: i32,
2044 /// The source location points to the index expression of an array access
2045 /// expression, found by taking this AST node index offset from the containing
2046 /// Decl AST node, which points to an array access AST node. Next, navigate
2047 /// to the index expression.
2048 /// The Decl is determined contextually.
2049 node_offset_array_access_index: i32,
2050 /// The source location points to the LHS of a slice expression
2051 /// expression, found by taking this AST node index offset from the containing
2052 /// Decl AST node, which points to a slice AST node. Next, navigate
2053 /// to the sentinel expression.
2054 /// The Decl is determined contextually.
2055 node_offset_slice_ptr: i32,
2056 /// The source location points to start expression of a slice expression
2057 /// expression, found by taking this AST node index offset from the containing
2058 /// Decl AST node, which points to a slice AST node. Next, navigate
2059 /// to the sentinel expression.
2060 /// The Decl is determined contextually.
2061 node_offset_slice_start: i32,
2062 /// The source location points to the end expression of a slice
2063 /// expression, found by taking this AST node index offset from the containing
2064 /// Decl AST node, which points to a slice AST node. Next, navigate
2065 /// to the sentinel expression.
2066 /// The Decl is determined contextually.
2067 node_offset_slice_end: i32,
2068 /// The source location points to the sentinel expression of a slice
2069 /// expression, found by taking this AST node index offset from the containing
2070 /// Decl AST node, which points to a slice AST node. Next, navigate
2071 /// to the sentinel expression.
2072 /// The Decl is determined contextually.
2073 node_offset_slice_sentinel: i32,
2074 /// The source location points to the callee expression of a function
2075 /// call expression, found by taking this AST node index offset from the containing
2076 /// Decl AST node, which points to a function call AST node. Next, navigate
2077 /// to the callee expression.
2078 /// The Decl is determined contextually.
2079 node_offset_call_func: i32,
2080 /// The payload is offset from the containing Decl AST node.
2081 /// The source location points to the field name of:
2082 /// * a field access expression (`a.b`), or
2083 /// * the callee of a method call (`a.b()`)
2084 /// The Decl is determined contextually.
2085 node_offset_field_name: i32,
2086 /// The payload is offset from the containing Decl AST node.
2087 /// The source location points to the field name of the operand ("b" node)
2088 /// of a field initialization expression (`.a = b`)
2089 /// The Decl is determined contextually.
2090 node_offset_field_name_init: i32,
2091 /// The source location points to the pointer of a pointer deref expression,
2092 /// found by taking this AST node index offset from the containing
2093 /// Decl AST node, which points to a pointer deref AST node. Next, navigate
2094 /// to the pointer expression.
2095 /// The Decl is determined contextually.
2096 node_offset_deref_ptr: i32,
2097 /// The source location points to the assembly source code of an inline assembly
2098 /// expression, found by taking this AST node index offset from the containing
2099 /// Decl AST node, which points to inline assembly AST node. Next, navigate
2100 /// to the asm template source code.
2101 /// The Decl is determined contextually.
2102 node_offset_asm_source: i32,
2103 /// The source location points to the return type of an inline assembly
2104 /// expression, found by taking this AST node index offset from the containing
2105 /// Decl AST node, which points to inline assembly AST node. Next, navigate
2106 /// to the return type expression.
2107 /// The Decl is determined contextually.
2108 node_offset_asm_ret_ty: i32,
2109 /// The source location points to the condition expression of an if
2110 /// expression, found by taking this AST node index offset from the containing
2111 /// Decl AST node, which points to an if expression AST node. Next, navigate
2112 /// to the condition expression.
2113 /// The Decl is determined contextually.
2114 node_offset_if_cond: i32,
2115 /// The source location points to a binary expression, such as `a + b`, found
2116 /// by taking this AST node index offset from the containing Decl AST node.
2117 /// The Decl is determined contextually.
2118 node_offset_bin_op: i32,
2119 /// The source location points to the LHS of a binary expression, found
2120 /// by taking this AST node index offset from the containing Decl AST node,
2121 /// which points to a binary expression AST node. Next, navigate to the LHS.
2122 /// The Decl is determined contextually.
2123 node_offset_bin_lhs: i32,
2124 /// The source location points to the RHS of a binary expression, found
2125 /// by taking this AST node index offset from the containing Decl AST node,
2126 /// which points to a binary expression AST node. Next, navigate to the RHS.
2127 /// The Decl is determined contextually.
2128 node_offset_bin_rhs: i32,
2129 /// The source location points to the operand of a switch expression, found
2130 /// by taking this AST node index offset from the containing Decl AST node,
2131 /// which points to a switch expression AST node. Next, navigate to the operand.
2132 /// The Decl is determined contextually.
2133 node_offset_switch_operand: i32,
2134 /// The source location points to the else/`_` prong of a switch expression, found
2135 /// by taking this AST node index offset from the containing Decl AST node,
2136 /// which points to a switch expression AST node. Next, navigate to the else/`_` prong.
2137 /// The Decl is determined contextually.
2138 node_offset_switch_special_prong: i32,
2139 /// The source location points to all the ranges of a switch expression, found
2140 /// by taking this AST node index offset from the containing Decl AST node,
2141 /// which points to a switch expression AST node. Next, navigate to any of the
2142 /// range nodes. The error applies to all of them.
2143 /// The Decl is determined contextually.
2144 node_offset_switch_range: i32,
2145 /// The source location points to the capture of a switch_prong.
2146 /// The Decl is determined contextually.
2147 node_offset_switch_prong_capture: i32,
2148 /// The source location points to the tag capture of a switch_prong.
2149 /// The Decl is determined contextually.
2150 node_offset_switch_prong_tag_capture: i32,
2151 /// The source location points to the align expr of a function type
2152 /// expression, found by taking this AST node index offset from the containing
2153 /// Decl AST node, which points to a function type AST node. Next, navigate to
2154 /// the calling convention node.
2155 /// The Decl is determined contextually.
2156 node_offset_fn_type_align: i32,
2157 /// The source location points to the addrspace expr of a function type
2158 /// expression, found by taking this AST node index offset from the containing
2159 /// Decl AST node, which points to a function type AST node. Next, navigate to
2160 /// the calling convention node.
2161 /// The Decl is determined contextually.
2162 node_offset_fn_type_addrspace: i32,
2163 /// The source location points to the linksection expr of a function type
2164 /// expression, found by taking this AST node index offset from the containing
2165 /// Decl AST node, which points to a function type AST node. Next, navigate to
2166 /// the calling convention node.
2167 /// The Decl is determined contextually.
2168 node_offset_fn_type_section: i32,
2169 /// The source location points to the calling convention of a function type
2170 /// expression, found by taking this AST node index offset from the containing
2171 /// Decl AST node, which points to a function type AST node. Next, navigate to
2172 /// the calling convention node.
2173 /// The Decl is determined contextually.
2174 node_offset_fn_type_cc: i32,
2175 /// The source location points to the return type of a function type
2176 /// expression, found by taking this AST node index offset from the containing
2177 /// Decl AST node, which points to a function type AST node. Next, navigate to
2178 /// the return type node.
2179 /// The Decl is determined contextually.
2180 node_offset_fn_type_ret_ty: i32,
2181 node_offset_param: i32,
2182 token_offset_param: i32,
2183 /// The source location points to the type expression of an `anyframe->T`
2184 /// expression, found by taking this AST node index offset from the containing
2185 /// Decl AST node, which points to a `anyframe->T` expression AST node. Next, navigate
2186 /// to the type expression.
2187 /// The Decl is determined contextually.
2188 node_offset_anyframe_type: i32,
2189 /// The source location points to the string literal of `extern "foo"`, found
2190 /// by taking this AST node index offset from the containing
2191 /// Decl AST node, which points to a function prototype or variable declaration
2192 /// expression AST node. Next, navigate to the string literal of the `extern "foo"`.
2193 /// The Decl is determined contextually.
2194 node_offset_lib_name: i32,
2195 /// The source location points to the len expression of an `[N:S]T`
2196 /// expression, found by taking this AST node index offset from the containing
2197 /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate
2198 /// to the len expression.
2199 /// The Decl is determined contextually.
2200 node_offset_array_type_len: i32,
2201 /// The source location points to the sentinel expression of an `[N:S]T`
2202 /// expression, found by taking this AST node index offset from the containing
2203 /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate
2204 /// to the sentinel expression.
2205 /// The Decl is determined contextually.
2206 node_offset_array_type_sentinel: i32,
2207 /// The source location points to the elem expression of an `[N:S]T`
2208 /// expression, found by taking this AST node index offset from the containing
2209 /// Decl AST node, which points to an `[N:S]T` expression AST node. Next, navigate
2210 /// to the elem expression.
2211 /// The Decl is determined contextually.
2212 node_offset_array_type_elem: i32,
2213 /// The source location points to the operand of an unary expression.
2214 /// The Decl is determined contextually.
2215 node_offset_un_op: i32,
2216 /// The source location points to the elem type of a pointer.
2217 /// The Decl is determined contextually.
2218 node_offset_ptr_elem: i32,
2219 /// The source location points to the sentinel of a pointer.
2220 /// The Decl is determined contextually.
2221 node_offset_ptr_sentinel: i32,
2222 /// The source location points to the align expr of a pointer.
2223 /// The Decl is determined contextually.
2224 node_offset_ptr_align: i32,
2225 /// The source location points to the addrspace expr of a pointer.
2226 /// The Decl is determined contextually.
2227 node_offset_ptr_addrspace: i32,
2228 /// The source location points to the bit-offset of a pointer.
2229 /// The Decl is determined contextually.
2230 node_offset_ptr_bitoffset: i32,
2231 /// The source location points to the host size of a pointer.
2232 /// The Decl is determined contextually.
2233 node_offset_ptr_hostsize: i32,
2234 /// The source location points to the tag type of an union or an enum.
2235 /// The Decl is determined contextually.
2236 node_offset_container_tag: i32,
2237 /// The source location points to the default value of a field.
2238 /// The Decl is determined contextually.
2239 node_offset_field_default: i32,
2240 /// The source location points to the type of an array or struct initializer.
2241 /// The Decl is determined contextually.
2242 node_offset_init_ty: i32,
2243 /// The source location points to the LHS of an assignment.
2244 /// The Decl is determined contextually.
2245 node_offset_store_ptr: i32,
2246 /// The source location points to the RHS of an assignment.
2247 /// The Decl is determined contextually.
2248 node_offset_store_operand: i32,
2249 /// The source location points to the operand of a `return` statement, or
2250 /// the `return` itself if there is no explicit operand.
2251 /// The Decl is determined contextually.
2252 node_offset_return_operand: i32,
2253 /// The source location points to a for loop input.
2254 /// The Decl is determined contextually.
2255 for_input: struct {
2256 /// Points to the for loop AST node.
2257 for_node_offset: i32,
2258 /// Picks one of the inputs from the condition.
2259 input_index: u32,
2260 },
2261 /// The source location points to one of the captures of a for loop, found
2262 /// by taking this AST node index offset from the containing
2263 /// Decl AST node, which points to one of the input nodes of a for loop.
2264 /// Next, navigate to the corresponding capture.
2265 /// The Decl is determined contextually.
2266 for_capture_from_input: i32,
2267 /// The source location points to the argument node of a function call.
2268 call_arg: struct {
2269 decl: Decl.Index,
2270 /// Points to the function call AST node.
2271 call_node_offset: i32,
2272 /// The index of the argument the source location points to.
2273 arg_index: u32,
2274 },
2275 fn_proto_param: struct {
2276 decl: Decl.Index,
2277 /// Points to the function prototype AST node.
2278 fn_proto_node_offset: i32,
2279 /// The index of the parameter the source location points to.
2280 param_index: u32,
2281 },
2282 array_cat_lhs: ArrayCat,
2283 array_cat_rhs: ArrayCat,
2284
2285 const ArrayCat = struct {
2286 /// Points to the array concat AST node.
2287 array_cat_offset: i32,
2288 /// The index of the element the source location points to.
2289 elem_index: u32,
2290 };
2291
2292 pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease;
2293
2294 noinline fn nodeOffsetDebug(node_offset: i32) LazySrcLoc {
2295 var result: LazySrcLoc = .{ .node_offset = .{ .x = node_offset } };
2296 result.node_offset.trace.addAddr(@returnAddress(), "init");
2297 return result;
2298 }
2299
2300 fn nodeOffsetRelease(node_offset: i32) LazySrcLoc {
2301 return .{ .node_offset = .{ .x = node_offset } };
2302 }
2303
2304 /// Upgrade to a `SrcLoc` based on the `Decl` provided.
2305 pub fn toSrcLoc(lazy: LazySrcLoc, decl: *Decl, mod: *Module) SrcLoc {
2306 return switch (lazy) {
2307 .unneeded,
2308 .entire_file,
2309 .byte_abs,
2310 .token_abs,
2311 .node_abs,
2312 => .{
2313 .file_scope = decl.getFileScope(mod),
2314 .parent_decl_node = 0,
2315 .lazy = lazy,
2316 },
2317
2318 .byte_offset,
2319 .token_offset,
2320 .node_offset,
2321 .node_offset_main_token,
2322 .node_offset_initializer,
2323 .node_offset_var_decl_ty,
2324 .node_offset_var_decl_align,
2325 .node_offset_var_decl_section,
2326 .node_offset_var_decl_addrspace,
2327 .node_offset_var_decl_init,
2328 .node_offset_builtin_call_arg0,
2329 .node_offset_builtin_call_arg1,
2330 .node_offset_builtin_call_arg2,
2331 .node_offset_builtin_call_arg3,
2332 .node_offset_builtin_call_arg4,
2333 .node_offset_builtin_call_arg5,
2334 .node_offset_ptrcast_operand,
2335 .node_offset_array_access_index,
2336 .node_offset_slice_ptr,
2337 .node_offset_slice_start,
2338 .node_offset_slice_end,
2339 .node_offset_slice_sentinel,
2340 .node_offset_call_func,
2341 .node_offset_field_name,
2342 .node_offset_field_name_init,
2343 .node_offset_deref_ptr,
2344 .node_offset_asm_source,
2345 .node_offset_asm_ret_ty,
2346 .node_offset_if_cond,
2347 .node_offset_bin_op,
2348 .node_offset_bin_lhs,
2349 .node_offset_bin_rhs,
2350 .node_offset_switch_operand,
2351 .node_offset_switch_special_prong,
2352 .node_offset_switch_range,
2353 .node_offset_switch_prong_capture,
2354 .node_offset_switch_prong_tag_capture,
2355 .node_offset_fn_type_align,
2356 .node_offset_fn_type_addrspace,
2357 .node_offset_fn_type_section,
2358 .node_offset_fn_type_cc,
2359 .node_offset_fn_type_ret_ty,
2360 .node_offset_param,
2361 .token_offset_param,
2362 .node_offset_anyframe_type,
2363 .node_offset_lib_name,
2364 .node_offset_array_type_len,
2365 .node_offset_array_type_sentinel,
2366 .node_offset_array_type_elem,
2367 .node_offset_un_op,
2368 .node_offset_ptr_elem,
2369 .node_offset_ptr_sentinel,
2370 .node_offset_ptr_align,
2371 .node_offset_ptr_addrspace,
2372 .node_offset_ptr_bitoffset,
2373 .node_offset_ptr_hostsize,
2374 .node_offset_container_tag,
2375 .node_offset_field_default,
2376 .node_offset_init_ty,
2377 .node_offset_store_ptr,
2378 .node_offset_store_operand,
2379 .node_offset_return_operand,
2380 .for_input,
2381 .for_capture_from_input,
2382 .array_cat_lhs,
2383 .array_cat_rhs,
2384 => .{
2385 .file_scope = decl.getFileScope(mod),
2386 .parent_decl_node = decl.src_node,
2387 .lazy = lazy,
2388 },
2389 inline .call_arg,
2390 .fn_proto_param,
2391 => |x| .{
2392 .file_scope = decl.getFileScope(mod),
2393 .parent_decl_node = mod.declPtr(x.decl).src_node,
2394 .lazy = lazy,
2395 },
2396 };
2397 }2005 }
2398};2006};
23992007
src/Package.zig+1-1
...@@ -126,7 +126,7 @@ pub const Path = struct {...@@ -126,7 +126,7 @@ pub const Path = struct {
126 ) !void {126 ) !void {
127 if (fmt_string.len == 1) {127 if (fmt_string.len == 1) {
128 // Quote-escape the string.128 // Quote-escape the string.
129 const stringEscape = std.zig.fmt.stringEscape;129 const stringEscape = std.zig.stringEscape;
130 const f = switch (fmt_string[0]) {130 const f = switch (fmt_string[0]) {
131 'q' => "",131 'q' => "",
132 '\'' => '\'',132 '\'' => '\'',
src/Package/Fetch.zig+1-2
...@@ -592,7 +592,7 @@ fn loadManifest(f: *Fetch, pkg_root: Package.Path) RunError!void {...@@ -592,7 +592,7 @@ fn loadManifest(f: *Fetch, pkg_root: Package.Path) RunError!void {
592592
593 if (ast.errors.len > 0) {593 if (ast.errors.len > 0) {
594 const file_path = try std.fmt.allocPrint(arena, "{}" ++ Manifest.basename, .{pkg_root});594 const file_path = try std.fmt.allocPrint(arena, "{}" ++ Manifest.basename, .{pkg_root});
595 try main.putAstErrorsIntoBundle(arena, ast.*, file_path, eb);595 try std.zig.putAstErrorsIntoBundle(arena, ast.*, file_path, eb);
596 return error.FetchFailed;596 return error.FetchFailed;
597 }597 }
598598
...@@ -1690,7 +1690,6 @@ const Cache = std.Build.Cache;...@@ -1690,7 +1690,6 @@ const Cache = std.Build.Cache;
1690const ThreadPool = std.Thread.Pool;1690const ThreadPool = std.Thread.Pool;
1691const WaitGroup = std.Thread.WaitGroup;1691const WaitGroup = std.Thread.WaitGroup;
1692const Fetch = @This();1692const Fetch = @This();
1693const main = @import("../main.zig");
1694const git = @import("Fetch/git.zig");1693const git = @import("Fetch/git.zig");
1695const Package = @import("../Package.zig");1694const Package = @import("../Package.zig");
1696const Manifest = Package.Manifest;1695const Manifest = Package.Manifest;
src/Sema.zig+40-40
...@@ -148,7 +148,7 @@ const Value = @import("Value.zig");...@@ -148,7 +148,7 @@ const Value = @import("Value.zig");
148const Type = @import("type.zig").Type;148const Type = @import("type.zig").Type;
149const TypedValue = @import("TypedValue.zig");149const TypedValue = @import("TypedValue.zig");
150const Air = @import("Air.zig");150const Air = @import("Air.zig");
151const Zir = @import("Zir.zig");151const Zir = std.zig.Zir;
152const Module = @import("Module.zig");152const Module = @import("Module.zig");
153const trace = @import("tracy.zig").trace;153const trace = @import("tracy.zig").trace;
154const Namespace = Module.Namespace;154const Namespace = Module.Namespace;
...@@ -156,7 +156,7 @@ const CompileError = Module.CompileError;...@@ -156,7 +156,7 @@ const CompileError = Module.CompileError;
156const SemaError = Module.SemaError;156const SemaError = Module.SemaError;
157const Decl = Module.Decl;157const Decl = Module.Decl;
158const CaptureScope = Module.CaptureScope;158const CaptureScope = Module.CaptureScope;
159const LazySrcLoc = Module.LazySrcLoc;159const LazySrcLoc = std.zig.LazySrcLoc;
160const RangeSet = @import("RangeSet.zig");160const RangeSet = @import("RangeSet.zig");
161const target_util = @import("target.zig");161const target_util = @import("target.zig");
162const Package = @import("Package.zig");162const Package = @import("Package.zig");
...@@ -397,7 +397,7 @@ pub const Block = struct {...@@ -397,7 +397,7 @@ pub const Block = struct {
397 break :blk src_loc;397 break :blk src_loc;
398 } else blk: {398 } else blk: {
399 const src_decl = mod.declPtr(rt.block.src_decl);399 const src_decl = mod.declPtr(rt.block.src_decl);
400 break :blk rt.func_src.toSrcLoc(src_decl, mod);400 break :blk src_decl.toSrcLoc(rt.func_src, mod);
401 };401 };
402 if (rt.return_ty.isGenericPoison()) {402 if (rt.return_ty.isGenericPoison()) {
403 return mod.errNoteNonLazy(src_loc, parent, prefix ++ "the generic function was instantiated with a comptime-only return type", .{});403 return mod.errNoteNonLazy(src_loc, parent, prefix ++ "the generic function was instantiated with a comptime-only return type", .{});
...@@ -2421,7 +2421,7 @@ fn errNote(...@@ -2421,7 +2421,7 @@ fn errNote(
2421) error{OutOfMemory}!void {2421) error{OutOfMemory}!void {
2422 const mod = sema.mod;2422 const mod = sema.mod;
2423 const src_decl = mod.declPtr(block.src_decl);2423 const src_decl = mod.declPtr(block.src_decl);
2424 return mod.errNoteNonLazy(src.toSrcLoc(src_decl, mod), parent, format, args);2424 return mod.errNoteNonLazy(src_decl.toSrcLoc(src, mod), parent, format, args);
2425}2425}
24262426
2427fn addFieldErrNote(2427fn addFieldErrNote(
...@@ -2478,7 +2478,7 @@ fn errMsg(...@@ -2478,7 +2478,7 @@ fn errMsg(
2478 const mod = sema.mod;2478 const mod = sema.mod;
2479 if (src == .unneeded) return error.NeededSourceLocation;2479 if (src == .unneeded) return error.NeededSourceLocation;
2480 const src_decl = mod.declPtr(block.src_decl);2480 const src_decl = mod.declPtr(block.src_decl);
2481 return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(src_decl, mod), format, args);2481 return Module.ErrorMsg.create(sema.gpa, src_decl.toSrcLoc(src, mod), format, args);
2482}2482}
24832483
2484pub fn fail(2484pub fn fail(
...@@ -2556,7 +2556,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Module.ErrorMsg)...@@ -2556,7 +2556,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Module.ErrorMsg)
2556 const decl = mod.declPtr(ref.referencer);2556 const decl = mod.declPtr(ref.referencer);
2557 try reference_stack.append(.{2557 try reference_stack.append(.{
2558 .decl = decl.name,2558 .decl = decl.name,
2559 .src_loc = ref.src.toSrcLoc(decl, mod),2559 .src_loc = decl.toSrcLoc(ref.src, mod),
2560 });2560 });
2561 }2561 }
2562 referenced_by = ref.referencer;2562 referenced_by = ref.referencer;
...@@ -2599,7 +2599,7 @@ fn reparentOwnedErrorMsg(...@@ -2599,7 +2599,7 @@ fn reparentOwnedErrorMsg(
2599) !void {2599) !void {
2600 const mod = sema.mod;2600 const mod = sema.mod;
2601 const src_decl = mod.declPtr(block.src_decl);2601 const src_decl = mod.declPtr(block.src_decl);
2602 const resolved_src = src.toSrcLoc(src_decl, mod);2602 const resolved_src = src_decl.toSrcLoc(src, mod);
2603 const msg_str = try std.fmt.allocPrint(mod.gpa, format, args);2603 const msg_str = try std.fmt.allocPrint(mod.gpa, format, args);
26042604
2605 const orig_notes = msg.notes.len;2605 const orig_notes = msg.notes.len;
...@@ -5252,7 +5252,7 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr...@@ -5252,7 +5252,7 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
5252 errdefer msg.destroy(sema.gpa);5252 errdefer msg.destroy(sema.gpa);
52535253
5254 const src_decl = mod.declPtr(block.src_decl);5254 const src_decl = mod.declPtr(block.src_decl);
5255 try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), elem_ty);5255 try sema.explainWhyTypeIsComptime(msg, src_decl.toSrcLoc(src, mod), elem_ty);
5256 break :msg msg;5256 break :msg msg;
5257 };5257 };
5258 return sema.failWithOwnedErrorMsg(block, msg);5258 return sema.failWithOwnedErrorMsg(block, msg);
...@@ -5716,7 +5716,7 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError...@@ -5716,7 +5716,7 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError
5716 var child_block = parent_block.makeSubBlock();5716 var child_block = parent_block.makeSubBlock();
5717 child_block.label = &label;5717 child_block.label = &label;
5718 child_block.runtime_cond = null;5718 child_block.runtime_cond = null;
5719 child_block.runtime_loop = src.toSrcLoc(mod.declPtr(child_block.src_decl), mod);5719 child_block.runtime_loop = mod.declPtr(child_block.src_decl).toSrcLoc(src, mod);
5720 child_block.runtime_index.increment();5720 child_block.runtime_index.increment();
5721 const merges = &child_block.label.?.merges;5721 const merges = &child_block.label.?.merges;
57225722
...@@ -6058,7 +6058,7 @@ fn analyzeBlockBody(...@@ -6058,7 +6058,7 @@ fn analyzeBlockBody(
6058 try mod.errNoteNonLazy(runtime_src, msg, "runtime control flow here", .{});6058 try mod.errNoteNonLazy(runtime_src, msg, "runtime control flow here", .{});
60596059
6060 const child_src_decl = mod.declPtr(child_block.src_decl);6060 const child_src_decl = mod.declPtr(child_block.src_decl);
6061 try sema.explainWhyTypeIsComptime(msg, type_src.toSrcLoc(child_src_decl, mod), resolved_ty);6061 try sema.explainWhyTypeIsComptime(msg, child_src_decl.toSrcLoc(type_src, mod), resolved_ty);
60626062
6063 break :msg msg;6063 break :msg msg;
6064 };6064 };
...@@ -6213,7 +6213,7 @@ pub fn analyzeExport(...@@ -6213,7 +6213,7 @@ pub fn analyzeExport(
6213 errdefer msg.destroy(gpa);6213 errdefer msg.destroy(gpa);
62146214
6215 const src_decl = mod.declPtr(block.src_decl);6215 const src_decl = mod.declPtr(block.src_decl);
6216 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), exported_decl.ty, .other);6216 try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(src, mod), exported_decl.ty, .other);
62176217
6218 try sema.addDeclaredHereNote(msg, exported_decl.ty);6218 try sema.addDeclaredHereNote(msg, exported_decl.ty);
6219 break :msg msg;6219 break :msg msg;
...@@ -8082,7 +8082,7 @@ fn instantiateGenericCall(...@@ -8082,7 +8082,7 @@ fn instantiateGenericCall(
8082 };8082 };
8083 try child_sema.errNote(&child_block, param_src, msg, "declared here", .{});8083 try child_sema.errNote(&child_block, param_src, msg, "declared here", .{});
8084 const src_decl = mod.declPtr(block.src_decl);8084 const src_decl = mod.declPtr(block.src_decl);
8085 try sema.explainWhyTypeIsComptime(msg, arg_src.toSrcLoc(src_decl, mod), arg_ty);8085 try sema.explainWhyTypeIsComptime(msg, src_decl.toSrcLoc(arg_src, mod), arg_ty);
8086 break :msg msg;8086 break :msg msg;
8087 }),8087 }),
80888088
...@@ -9387,7 +9387,7 @@ fn funcCommon(...@@ -9387,7 +9387,7 @@ fn funcCommon(
9387 errdefer msg.destroy(sema.gpa);9387 errdefer msg.destroy(sema.gpa);
93889388
9389 const src_decl = mod.declPtr(block.src_decl);9389 const src_decl = mod.declPtr(block.src_decl);
9390 try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl, mod), param_ty, .param_ty);9390 try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(param_src, mod), param_ty, .param_ty);
93919391
9392 try sema.addDeclaredHereNote(msg, param_ty);9392 try sema.addDeclaredHereNote(msg, param_ty);
9393 break :msg msg;9393 break :msg msg;
...@@ -9402,7 +9402,7 @@ fn funcCommon(...@@ -9402,7 +9402,7 @@ fn funcCommon(
9402 errdefer msg.destroy(sema.gpa);9402 errdefer msg.destroy(sema.gpa);
94039403
9404 const src_decl = mod.declPtr(block.src_decl);9404 const src_decl = mod.declPtr(block.src_decl);
9405 try sema.explainWhyTypeIsComptime(msg, param_src.toSrcLoc(src_decl, mod), param_ty);9405 try sema.explainWhyTypeIsComptime(msg, src_decl.toSrcLoc(param_src, mod), param_ty);
94069406
9407 try sema.addDeclaredHereNote(msg, param_ty);9407 try sema.addDeclaredHereNote(msg, param_ty);
9408 break :msg msg;9408 break :msg msg;
...@@ -9671,7 +9671,7 @@ fn finishFunc(...@@ -9671,7 +9671,7 @@ fn finishFunc(
9671 errdefer msg.destroy(gpa);9671 errdefer msg.destroy(gpa);
96729672
9673 const src_decl = mod.declPtr(block.src_decl);9673 const src_decl = mod.declPtr(block.src_decl);
9674 try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl, mod), return_type, .ret_ty);9674 try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(ret_ty_src, mod), return_type, .ret_ty);
96759675
9676 try sema.addDeclaredHereNote(msg, return_type);9676 try sema.addDeclaredHereNote(msg, return_type);
9677 break :msg msg;9677 break :msg msg;
...@@ -9692,7 +9692,7 @@ fn finishFunc(...@@ -9692,7 +9692,7 @@ fn finishFunc(
9692 "function with comptime-only return type '{}' requires all parameters to be comptime",9692 "function with comptime-only return type '{}' requires all parameters to be comptime",
9693 .{return_type.fmt(mod)},9693 .{return_type.fmt(mod)},
9694 );9694 );
9695 try sema.explainWhyTypeIsComptime(msg, ret_ty_src.toSrcLoc(sema.owner_decl, mod), return_type);9695 try sema.explainWhyTypeIsComptime(msg, sema.owner_decl.toSrcLoc(ret_ty_src, mod), return_type);
96969696
9697 const tags = sema.code.instructions.items(.tag);9697 const tags = sema.code.instructions.items(.tag);
9698 const data = sema.code.instructions.items(.data);9698 const data = sema.code.instructions.items(.data);
...@@ -9965,7 +9965,7 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -9965,7 +9965,7 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
9965 const msg = try sema.errMsg(block, ptr_src, "comptime-only type '{}' has no pointer address", .{pointee_ty.fmt(mod)});9965 const msg = try sema.errMsg(block, ptr_src, "comptime-only type '{}' has no pointer address", .{pointee_ty.fmt(mod)});
9966 errdefer msg.destroy(sema.gpa);9966 errdefer msg.destroy(sema.gpa);
9967 const src_decl = mod.declPtr(block.src_decl);9967 const src_decl = mod.declPtr(block.src_decl);
9968 try sema.explainWhyTypeIsComptime(msg, ptr_src.toSrcLoc(src_decl, mod), pointee_ty);9968 try sema.explainWhyTypeIsComptime(msg, src_decl.toSrcLoc(ptr_src, mod), pointee_ty);
9969 break :msg msg;9969 break :msg msg;
9970 };9970 };
9971 return sema.failWithOwnedErrorMsg(block, msg);9971 return sema.failWithOwnedErrorMsg(block, msg);
...@@ -11492,7 +11492,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -11492,7 +11492,7 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
1149211492
11493 var sub_block = child_block.makeSubBlock();11493 var sub_block = child_block.makeSubBlock();
11494 sub_block.runtime_loop = null;11494 sub_block.runtime_loop = null;
11495 sub_block.runtime_cond = main_operand_src.toSrcLoc(mod.declPtr(child_block.src_decl), mod);11495 sub_block.runtime_cond = mod.declPtr(child_block.src_decl).toSrcLoc(main_operand_src, mod);
11496 sub_block.runtime_index.increment();11496 sub_block.runtime_index.increment();
11497 defer sub_block.instructions.deinit(gpa);11497 defer sub_block.instructions.deinit(gpa);
1149811498
...@@ -12227,7 +12227,7 @@ fn analyzeSwitchRuntimeBlock(...@@ -12227,7 +12227,7 @@ fn analyzeSwitchRuntimeBlock(
1222712227
12228 var case_block = child_block.makeSubBlock();12228 var case_block = child_block.makeSubBlock();
12229 case_block.runtime_loop = null;12229 case_block.runtime_loop = null;
12230 case_block.runtime_cond = operand_src.toSrcLoc(mod.declPtr(child_block.src_decl), mod);12230 case_block.runtime_cond = mod.declPtr(child_block.src_decl).toSrcLoc(operand_src, mod);
12231 case_block.runtime_index.increment();12231 case_block.runtime_index.increment();
12232 defer case_block.instructions.deinit(gpa);12232 defer case_block.instructions.deinit(gpa);
1223312233
...@@ -13663,7 +13663,7 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -13663,7 +13663,7 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
13663 return sema.fail(block, operand_src, "file path name cannot be empty", .{});13663 return sema.fail(block, operand_src, "file path name cannot be empty", .{});
13664 }13664 }
1366513665
13666 const src_loc = operand_src.toSrcLoc(mod.declPtr(block.src_decl), mod);13666 const src_loc = mod.declPtr(block.src_decl).toSrcLoc(operand_src, mod);
13667 const val = mod.embedFile(block.getFileScope(mod), name, src_loc) catch |err| switch (err) {13667 const val = mod.embedFile(block.getFileScope(mod), name, src_loc) catch |err| switch (err) {
13668 error.ImportOutsideModulePath => {13668 error.ImportOutsideModulePath => {
13669 return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name});13669 return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name});
...@@ -18766,7 +18766,7 @@ fn zirBoolBr(...@@ -18766,7 +18766,7 @@ fn zirBoolBr(
1876618766
18767 var child_block = parent_block.makeSubBlock();18767 var child_block = parent_block.makeSubBlock();
18768 child_block.runtime_loop = null;18768 child_block.runtime_loop = null;
18769 child_block.runtime_cond = lhs_src.toSrcLoc(mod.declPtr(child_block.src_decl), mod);18769 child_block.runtime_cond = mod.declPtr(child_block.src_decl).toSrcLoc(lhs_src, mod);
18770 child_block.runtime_index.increment();18770 child_block.runtime_index.increment();
18771 defer child_block.instructions.deinit(gpa);18771 defer child_block.instructions.deinit(gpa);
1877218772
...@@ -18963,7 +18963,7 @@ fn zirCondbr(...@@ -18963,7 +18963,7 @@ fn zirCondbr(
18963 // instructions array in between using it for the then block and else block.18963 // instructions array in between using it for the then block and else block.
18964 var sub_block = parent_block.makeSubBlock();18964 var sub_block = parent_block.makeSubBlock();
18965 sub_block.runtime_loop = null;18965 sub_block.runtime_loop = null;
18966 sub_block.runtime_cond = cond_src.toSrcLoc(mod.declPtr(parent_block.src_decl), mod);18966 sub_block.runtime_cond = mod.declPtr(parent_block.src_decl).toSrcLoc(cond_src, mod);
18967 sub_block.runtime_index.increment();18967 sub_block.runtime_index.increment();
18968 defer sub_block.instructions.deinit(gpa);18968 defer sub_block.instructions.deinit(gpa);
1896918969
...@@ -19503,7 +19503,7 @@ fn analyzeRet(...@@ -19503,7 +19503,7 @@ fn analyzeRet(
1950319503
19504 if (sema.fn_ret_ty.isError(mod) and ret_val.getErrorName(mod) != .none) {19504 if (sema.fn_ret_ty.isError(mod) and ret_val.getErrorName(mod) != .none) {
19505 const src_decl = mod.declPtr(block.src_decl);19505 const src_decl = mod.declPtr(block.src_decl);
19506 const src_loc = src.toSrcLoc(src_decl, mod);19506 const src_loc = src_decl.toSrcLoc(src, mod);
19507 try sema.comptime_err_ret_trace.append(src_loc);19507 try sema.comptime_err_ret_trace.append(src_loc);
19508 }19508 }
19509 return error.ComptimeReturn;19509 return error.ComptimeReturn;
...@@ -19660,7 +19660,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19660,7 +19660,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
19660 errdefer msg.destroy(sema.gpa);19660 errdefer msg.destroy(sema.gpa);
1966119661
19662 const src_decl = mod.declPtr(block.src_decl);19662 const src_decl = mod.declPtr(block.src_decl);
19663 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl, mod), elem_ty, .other);19663 try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(elem_ty_src, mod), elem_ty, .other);
1966419664
19665 try sema.addDeclaredHereNote(msg, elem_ty);19665 try sema.addDeclaredHereNote(msg, elem_ty);
19666 break :msg msg;19666 break :msg msg;
...@@ -21128,7 +21128,7 @@ fn zirReify(...@@ -21128,7 +21128,7 @@ fn zirReify(
21128 errdefer msg.destroy(gpa);21128 errdefer msg.destroy(gpa);
2112921129
21130 const src_decl = mod.declPtr(block.src_decl);21130 const src_decl = mod.declPtr(block.src_decl);
21131 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), elem_ty, .other);21131 try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(src, mod), elem_ty, .other);
2113221132
21133 try sema.addDeclaredHereNote(msg, elem_ty);21133 try sema.addDeclaredHereNote(msg, elem_ty);
21134 break :msg msg;21134 break :msg msg;
...@@ -21572,7 +21572,7 @@ fn zirReify(...@@ -21572,7 +21572,7 @@ fn zirReify(
21572 errdefer msg.destroy(gpa);21572 errdefer msg.destroy(gpa);
2157321573
21574 const src_decl = mod.declPtr(block.src_decl);21574 const src_decl = mod.declPtr(block.src_decl);
21575 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .union_field);21575 try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(src, mod), field_ty, .union_field);
2157621576
21577 try sema.addDeclaredHereNote(msg, field_ty);21577 try sema.addDeclaredHereNote(msg, field_ty);
21578 break :msg msg;21578 break :msg msg;
...@@ -21584,7 +21584,7 @@ fn zirReify(...@@ -21584,7 +21584,7 @@ fn zirReify(
21584 errdefer msg.destroy(gpa);21584 errdefer msg.destroy(gpa);
2158521585
21586 const src_decl = mod.declPtr(block.src_decl);21586 const src_decl = mod.declPtr(block.src_decl);
21587 try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty);21587 try sema.explainWhyTypeIsNotPacked(msg, src_decl.toSrcLoc(src, mod), field_ty);
2158821588
21589 try sema.addDeclaredHereNote(msg, field_ty);21589 try sema.addDeclaredHereNote(msg, field_ty);
21590 break :msg msg;21590 break :msg msg;
...@@ -21939,7 +21939,7 @@ fn reifyStruct(...@@ -21939,7 +21939,7 @@ fn reifyStruct(
21939 errdefer msg.destroy(gpa);21939 errdefer msg.destroy(gpa);
2194021940
21941 const src_decl = sema.mod.declPtr(block.src_decl);21941 const src_decl = sema.mod.declPtr(block.src_decl);
21942 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .struct_field);21942 try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(src, mod), field_ty, .struct_field);
2194321943
21944 try sema.addDeclaredHereNote(msg, field_ty);21944 try sema.addDeclaredHereNote(msg, field_ty);
21945 break :msg msg;21945 break :msg msg;
...@@ -21951,7 +21951,7 @@ fn reifyStruct(...@@ -21951,7 +21951,7 @@ fn reifyStruct(
21951 errdefer msg.destroy(gpa);21951 errdefer msg.destroy(gpa);
2195221952
21953 const src_decl = sema.mod.declPtr(block.src_decl);21953 const src_decl = sema.mod.declPtr(block.src_decl);
21954 try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty);21954 try sema.explainWhyTypeIsNotPacked(msg, src_decl.toSrcLoc(src, mod), field_ty);
2195521955
21956 try sema.addDeclaredHereNote(msg, field_ty);21956 try sema.addDeclaredHereNote(msg, field_ty);
21957 break :msg msg;21957 break :msg msg;
...@@ -22018,7 +22018,7 @@ fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C...@@ -22018,7 +22018,7 @@ fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C
22018 errdefer msg.destroy(sema.gpa);22018 errdefer msg.destroy(sema.gpa);
2201922019
22020 const src_decl = sema.mod.declPtr(block.src_decl);22020 const src_decl = sema.mod.declPtr(block.src_decl);
22021 try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), arg_ty, .param_ty);22021 try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(ty_src, mod), arg_ty, .param_ty);
2202222022
22023 try sema.addDeclaredHereNote(msg, arg_ty);22023 try sema.addDeclaredHereNote(msg, arg_ty);
22024 break :msg msg;22024 break :msg msg;
...@@ -25859,7 +25859,7 @@ fn zirBuiltinExtern(...@@ -25859,7 +25859,7 @@ fn zirBuiltinExtern(
25859 const msg = try sema.errMsg(block, ty_src, "extern symbol cannot have type '{}'", .{ty.fmt(mod)});25859 const msg = try sema.errMsg(block, ty_src, "extern symbol cannot have type '{}'", .{ty.fmt(mod)});
25860 errdefer msg.destroy(sema.gpa);25860 errdefer msg.destroy(sema.gpa);
25861 const src_decl = sema.mod.declPtr(block.src_decl);25861 const src_decl = sema.mod.declPtr(block.src_decl);
25862 try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), ty, .other);25862 try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(ty_src, mod), ty, .other);
25863 break :msg msg;25863 break :msg msg;
25864 };25864 };
25865 return sema.failWithOwnedErrorMsg(block, msg);25865 return sema.failWithOwnedErrorMsg(block, msg);
...@@ -26003,7 +26003,7 @@ fn validateVarType(...@@ -26003,7 +26003,7 @@ fn validateVarType(
26003 const msg = try sema.errMsg(block, src, "extern variable cannot have type '{}'", .{var_ty.fmt(mod)});26003 const msg = try sema.errMsg(block, src, "extern variable cannot have type '{}'", .{var_ty.fmt(mod)});
26004 errdefer msg.destroy(sema.gpa);26004 errdefer msg.destroy(sema.gpa);
26005 const src_decl = mod.declPtr(block.src_decl);26005 const src_decl = mod.declPtr(block.src_decl);
26006 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), var_ty, .other);26006 try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(src, mod), var_ty, .other);
26007 break :msg msg;26007 break :msg msg;
26008 };26008 };
26009 return sema.failWithOwnedErrorMsg(block, msg);26009 return sema.failWithOwnedErrorMsg(block, msg);
...@@ -26026,7 +26026,7 @@ fn validateVarType(...@@ -26026,7 +26026,7 @@ fn validateVarType(
26026 errdefer msg.destroy(sema.gpa);26026 errdefer msg.destroy(sema.gpa);
2602726027
26028 const src_decl = mod.declPtr(block.src_decl);26028 const src_decl = mod.declPtr(block.src_decl);
26029 try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), var_ty);26029 try sema.explainWhyTypeIsComptime(msg, src_decl.toSrcLoc(src, mod), var_ty);
26030 if (var_ty.zigTypeTag(mod) == .ComptimeInt or var_ty.zigTypeTag(mod) == .ComptimeFloat) {26030 if (var_ty.zigTypeTag(mod) == .ComptimeInt or var_ty.zigTypeTag(mod) == .ComptimeFloat) {
26031 try sema.errNote(block, src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{});26031 try sema.errNote(block, src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{});
26032 }26032 }
...@@ -28093,7 +28093,7 @@ fn validateRuntimeElemAccess(...@@ -28093,7 +28093,7 @@ fn validateRuntimeElemAccess(
28093 errdefer msg.destroy(sema.gpa);28093 errdefer msg.destroy(sema.gpa);
2809428094
28095 const src_decl = mod.declPtr(block.src_decl);28095 const src_decl = mod.declPtr(block.src_decl);
28096 try sema.explainWhyTypeIsComptime(msg, parent_src.toSrcLoc(src_decl, mod), parent_ty);28096 try sema.explainWhyTypeIsComptime(msg, src_decl.toSrcLoc(parent_src, mod), parent_ty);
2809728097
28098 break :msg msg;28098 break :msg msg;
28099 };28099 };
...@@ -28492,7 +28492,7 @@ const CoerceOpts = struct {...@@ -28492,7 +28492,7 @@ const CoerceOpts = struct {
28492 .lazy = LazySrcLoc.nodeOffset(param_src.node_offset_param),28492 .lazy = LazySrcLoc.nodeOffset(param_src.node_offset_param),
28493 };28493 };
28494 }28494 }
28495 return param_src.toSrcLoc(fn_decl, mod);28495 return fn_decl.toSrcLoc(param_src, mod);
28496 }28496 }
28497 } = .{},28497 } = .{},
28498};28498};
...@@ -29110,7 +29110,7 @@ fn coerceExtra(...@@ -29110,7 +29110,7 @@ fn coerceExtra(
2911029110
29111 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };29111 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
29112 const src_decl = mod.funcOwnerDeclPtr(sema.func_index);29112 const src_decl = mod.funcOwnerDeclPtr(sema.func_index);
29113 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "'noreturn' declared here", .{});29113 try mod.errNoteNonLazy(src_decl.toSrcLoc(ret_ty_src, mod), msg, "'noreturn' declared here", .{});
29114 break :msg msg;29114 break :msg msg;
29115 };29115 };
29116 return sema.failWithOwnedErrorMsg(block, msg);29116 return sema.failWithOwnedErrorMsg(block, msg);
...@@ -29145,9 +29145,9 @@ fn coerceExtra(...@@ -29145,9 +29145,9 @@ fn coerceExtra(
29145 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };29145 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
29146 const src_decl = mod.funcOwnerDeclPtr(sema.func_index);29146 const src_decl = mod.funcOwnerDeclPtr(sema.func_index);
29147 if (inst_ty.isError(mod) and !dest_ty.isError(mod)) {29147 if (inst_ty.isError(mod) and !dest_ty.isError(mod)) {
29148 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function cannot return an error", .{});29148 try mod.errNoteNonLazy(src_decl.toSrcLoc(ret_ty_src, mod), msg, "function cannot return an error", .{});
29149 } else {29149 } else {
29150 try mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function return type declared here", .{});29150 try mod.errNoteNonLazy(src_decl.toSrcLoc(ret_ty_src, mod), msg, "function return type declared here", .{});
29151 }29151 }
29152 }29152 }
2915329153
...@@ -30165,7 +30165,7 @@ fn coerceVarArgParam(...@@ -30165,7 +30165,7 @@ fn coerceVarArgParam(
30165 errdefer msg.destroy(sema.gpa);30165 errdefer msg.destroy(sema.gpa);
3016630166
30167 const src_decl = sema.mod.declPtr(block.src_decl);30167 const src_decl = sema.mod.declPtr(block.src_decl);
30168 try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl, mod), coerced_ty, .param_ty);30168 try sema.explainWhyTypeIsNotExtern(msg, src_decl.toSrcLoc(inst_src, mod), coerced_ty, .param_ty);
3016930169
30170 try sema.addDeclaredHereNote(msg, coerced_ty);30170 try sema.addDeclaredHereNote(msg, coerced_ty);
30171 break :msg msg;30171 break :msg msg;
...@@ -37180,7 +37180,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un...@@ -37180,7 +37180,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un
37180 });37180 });
37181 errdefer msg.destroy(sema.gpa);37181 errdefer msg.destroy(sema.gpa);
37182 const decl_ptr = mod.declPtr(tag_info.decl);37182 const decl_ptr = mod.declPtr(tag_info.decl);
37183 try mod.errNoteNonLazy(enum_field_src.toSrcLoc(decl_ptr, mod), msg, "enum field here", .{});37183 try mod.errNoteNonLazy(decl_ptr.toSrcLoc(enum_field_src, mod), msg, "enum field here", .{});
37184 break :msg msg;37184 break :msg msg;
37185 };37185 };
37186 return sema.failWithOwnedErrorMsg(&block_scope, msg);37186 return sema.failWithOwnedErrorMsg(&block_scope, msg);
src/Zir.zig deleted-4089
...@@ -1,4089 +0,0 @@
1//! Zig Intermediate Representation. Astgen.zig converts AST nodes to these
2//! untyped IR instructions. Next, Sema.zig processes these into AIR.
3//! The minimum amount of information needed to represent a list of ZIR instructions.
4//! Once this structure is completed, it can be used to generate AIR, followed by
5//! machine code, without any memory access into the AST tree token list, node list,
6//! or source bytes. Exceptions include:
7//! * Compile errors, which may need to reach into these data structures to
8//! create a useful report.
9//! * In the future, possibly inline assembly, which needs to get parsed and
10//! handled by the codegen backend, and errors reported there. However for now,
11//! inline assembly is not an exception.
12
13const std = @import("std");
14const builtin = @import("builtin");
15const mem = std.mem;
16const Allocator = std.mem.Allocator;
17const assert = std.debug.assert;
18const BigIntConst = std.math.big.int.Const;
19const BigIntMutable = std.math.big.int.Mutable;
20const Ast = std.zig.Ast;
21
22const InternPool = @import("InternPool.zig");
23const Zir = @This();
24const Module = @import("Module.zig");
25const LazySrcLoc = Module.LazySrcLoc;
26
27instructions: std.MultiArrayList(Inst).Slice,
28/// In order to store references to strings in fewer bytes, we copy all
29/// string bytes into here. String bytes can be null. It is up to whomever
30/// is referencing the data here whether they want to store both index and length,
31/// thus allowing null bytes, or store only index, and use null-termination. The
32/// `string_bytes` array is agnostic to either usage.
33/// Index 0 is reserved for special cases.
34string_bytes: []u8,
35/// The meaning of this data is determined by `Inst.Tag` value.
36/// The first few indexes are reserved. See `ExtraIndex` for the values.
37extra: []u32,
38
39/// The data stored at byte offset 0 when ZIR is stored in a file.
40pub const Header = extern struct {
41 instructions_len: u32,
42 string_bytes_len: u32,
43 extra_len: u32,
44 /// We could leave this as padding, however it triggers a Valgrind warning because
45 /// we read and write undefined bytes to the file system. This is harmless, but
46 /// it's essentially free to have a zero field here and makes the warning go away,
47 /// making it more likely that following Valgrind warnings will be taken seriously.
48 unused: u32 = 0,
49 stat_inode: std.fs.File.INode,
50 stat_size: u64,
51 stat_mtime: i128,
52};
53
54pub const ExtraIndex = enum(u32) {
55 /// If this is 0, no compile errors. Otherwise there is a `CompileErrors`
56 /// payload at this index.
57 compile_errors,
58 /// If this is 0, this file contains no imports. Otherwise there is a `Imports`
59 /// payload at this index.
60 imports,
61
62 _,
63};
64
65fn ExtraData(comptime T: type) type {
66 return struct { data: T, end: usize };
67}
68
69/// Returns the requested data, as well as the new index which is at the start of the
70/// trailers for the object.
71pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) {
72 const fields = @typeInfo(T).Struct.fields;
73 var i: usize = index;
74 var result: T = undefined;
75 inline for (fields) |field| {
76 @field(result, field.name) = switch (field.type) {
77 u32 => code.extra[i],
78
79 Inst.Ref,
80 Inst.Index,
81 Inst.Declaration.Name,
82 NullTerminatedString,
83 => @enumFromInt(code.extra[i]),
84
85 i32,
86 Inst.Call.Flags,
87 Inst.BuiltinCall.Flags,
88 Inst.SwitchBlock.Bits,
89 Inst.SwitchBlockErrUnion.Bits,
90 Inst.FuncFancy.Bits,
91 Inst.Declaration.Flags,
92 => @bitCast(code.extra[i]),
93
94 else => @compileError("bad field type"),
95 };
96 i += 1;
97 }
98 return .{
99 .data = result,
100 .end = i,
101 };
102}
103
104pub const NullTerminatedString = enum(u32) {
105 empty = 0,
106 _,
107};
108
109/// Given an index into `string_bytes` returns the null-terminated string found there.
110pub fn nullTerminatedString(code: Zir, index: NullTerminatedString) [:0]const u8 {
111 const start = @intFromEnum(index);
112 var end: u32 = start;
113 while (code.string_bytes[end] != 0) {
114 end += 1;
115 }
116 return code.string_bytes[start..end :0];
117}
118
119pub fn refSlice(code: Zir, start: usize, len: usize) []Inst.Ref {
120 return @ptrCast(code.extra[start..][0..len]);
121}
122
123pub fn bodySlice(zir: Zir, start: usize, len: usize) []Inst.Index {
124 return @ptrCast(zir.extra[start..][0..len]);
125}
126
127pub fn hasCompileErrors(code: Zir) bool {
128 return code.extra[@intFromEnum(ExtraIndex.compile_errors)] != 0;
129}
130
131pub fn deinit(code: *Zir, gpa: Allocator) void {
132 code.instructions.deinit(gpa);
133 gpa.free(code.string_bytes);
134 gpa.free(code.extra);
135 code.* = undefined;
136}
137
138/// These are untyped instructions generated from an Abstract Syntax Tree.
139/// The data here is immutable because it is possible to have multiple
140/// analyses on the same ZIR happening at the same time.
141pub const Inst = struct {
142 tag: Tag,
143 data: Data,
144
145 /// These names are used directly as the instruction names in the text format.
146 /// See `data_field_map` for a list of which `Data` fields are used by each `Tag`.
147 pub const Tag = enum(u8) {
148 /// Arithmetic addition, asserts no integer overflow.
149 /// Uses the `pl_node` union field. Payload is `Bin`.
150 add,
151 /// Twos complement wrapping integer addition.
152 /// Uses the `pl_node` union field. Payload is `Bin`.
153 addwrap,
154 /// Saturating addition.
155 /// Uses the `pl_node` union field. Payload is `Bin`.
156 add_sat,
157 /// The same as `add` except no safety check.
158 add_unsafe,
159 /// Arithmetic subtraction. Asserts no integer overflow.
160 /// Uses the `pl_node` union field. Payload is `Bin`.
161 sub,
162 /// Twos complement wrapping integer subtraction.
163 /// Uses the `pl_node` union field. Payload is `Bin`.
164 subwrap,
165 /// Saturating subtraction.
166 /// Uses the `pl_node` union field. Payload is `Bin`.
167 sub_sat,
168 /// Arithmetic multiplication. Asserts no integer overflow.
169 /// Uses the `pl_node` union field. Payload is `Bin`.
170 mul,
171 /// Twos complement wrapping integer multiplication.
172 /// Uses the `pl_node` union field. Payload is `Bin`.
173 mulwrap,
174 /// Saturating multiplication.
175 /// Uses the `pl_node` union field. Payload is `Bin`.
176 mul_sat,
177 /// Implements the `@divExact` builtin.
178 /// Uses the `pl_node` union field with payload `Bin`.
179 div_exact,
180 /// Implements the `@divFloor` builtin.
181 /// Uses the `pl_node` union field with payload `Bin`.
182 div_floor,
183 /// Implements the `@divTrunc` builtin.
184 /// Uses the `pl_node` union field with payload `Bin`.
185 div_trunc,
186 /// Implements the `@mod` builtin.
187 /// Uses the `pl_node` union field with payload `Bin`.
188 mod,
189 /// Implements the `@rem` builtin.
190 /// Uses the `pl_node` union field with payload `Bin`.
191 rem,
192 /// Ambiguously remainder division or modulus. If the computation would possibly have
193 /// a different value depending on whether the operation is remainder division or modulus,
194 /// a compile error is emitted. Otherwise the computation is performed.
195 /// Uses the `pl_node` union field. Payload is `Bin`.
196 mod_rem,
197 /// Integer shift-left. Zeroes are shifted in from the right hand side.
198 /// Uses the `pl_node` union field. Payload is `Bin`.
199 shl,
200 /// Implements the `@shlExact` builtin.
201 /// Uses the `pl_node` union field with payload `Bin`.
202 shl_exact,
203 /// Saturating shift-left.
204 /// Uses the `pl_node` union field. Payload is `Bin`.
205 shl_sat,
206 /// Integer shift-right. Arithmetic or logical depending on the signedness of
207 /// the integer type.
208 /// Uses the `pl_node` union field. Payload is `Bin`.
209 shr,
210 /// Implements the `@shrExact` builtin.
211 /// Uses the `pl_node` union field with payload `Bin`.
212 shr_exact,
213
214 /// Declares a parameter of the current function. Used for:
215 /// * debug info
216 /// * checking shadowing against declarations in the current namespace
217 /// * parameter type expressions referencing other parameters
218 /// These occur in the block outside a function body (the same block as
219 /// contains the func instruction).
220 /// Uses the `pl_tok` field. Token is the parameter name, payload is a `Param`.
221 param,
222 /// Same as `param` except the parameter is marked comptime.
223 param_comptime,
224 /// Same as `param` except the parameter is marked anytype.
225 /// Uses the `str_tok` field. Token is the parameter name. String is the parameter name.
226 param_anytype,
227 /// Same as `param` except the parameter is marked both comptime and anytype.
228 /// Uses the `str_tok` field. Token is the parameter name. String is the parameter name.
229 param_anytype_comptime,
230 /// Array concatenation. `a ++ b`
231 /// Uses the `pl_node` union field. Payload is `Bin`.
232 array_cat,
233 /// Array multiplication `a ** b`
234 /// Uses the `pl_node` union field. Payload is `ArrayMul`.
235 array_mul,
236 /// `[N]T` syntax. No source location provided.
237 /// Uses the `pl_node` union field. Payload is `Bin`. lhs is length, rhs is element type.
238 array_type,
239 /// `[N:S]T` syntax. Source location is the array type expression node.
240 /// Uses the `pl_node` union field. Payload is `ArrayTypeSentinel`.
241 array_type_sentinel,
242 /// `@Vector` builtin.
243 /// Uses the `pl_node` union field with `Bin` payload.
244 /// lhs is length, rhs is element type.
245 vector_type,
246 /// Given a pointer type, returns its element type. Reaches through any optional or error
247 /// union types wrapping the pointer. Asserts that the underlying type is a pointer type.
248 /// Returns generic poison if the element type is `anyopaque`.
249 /// Uses the `un_node` field.
250 elem_type,
251 /// Given an indexable pointer (slice, many-ptr, single-ptr-to-array), returns its
252 /// element type. Emits a compile error if the type is not an indexable pointer.
253 /// Uses the `un_node` field.
254 indexable_ptr_elem_type,
255 /// Given a vector type, returns its element type.
256 /// Uses the `un_node` field.
257 vector_elem_type,
258 /// Given a pointer to an indexable object, returns the len property. This is
259 /// used by for loops. This instruction also emits a for-loop specific compile
260 /// error if the indexable object is not indexable.
261 /// Uses the `un_node` field. The AST node is the for loop node.
262 indexable_ptr_len,
263 /// Create a `anyframe->T` type.
264 /// Uses the `un_node` field.
265 anyframe_type,
266 /// Type coercion to the function's return type.
267 /// Uses the `pl_node` field. Payload is `As`. AST node could be many things.
268 as_node,
269 /// Same as `as_node` but ignores runtime to comptime int error.
270 as_shift_operand,
271 /// Bitwise AND. `&`
272 bit_and,
273 /// Reinterpret the memory representation of a value as a different type.
274 /// Uses the pl_node field with payload `Bin`.
275 bitcast,
276 /// Bitwise NOT. `~`
277 /// Uses `un_node`.
278 bit_not,
279 /// Bitwise OR. `|`
280 bit_or,
281 /// A labeled block of code, which can return a value.
282 /// Uses the `pl_node` union field. Payload is `Block`.
283 block,
284 /// Like `block`, but forces full evaluation of its contents at compile-time.
285 /// Uses the `pl_node` union field. Payload is `Block`.
286 block_comptime,
287 /// A list of instructions which are analyzed in the parent context, without
288 /// generating a runtime block. Must terminate with an "inline" variant of
289 /// a noreturn instruction.
290 /// Uses the `pl_node` union field. Payload is `Block`.
291 block_inline,
292 /// This instruction may only ever appear in the list of declarations for a
293 /// namespace type, e.g. within a `struct_decl` instruction. It represents a
294 /// single source declaration (`const`/`var`/`fn`), containing the name,
295 /// attributes, type, and value of the declaration.
296 /// Uses the `pl_node` union field. Payload is `Declaration`.
297 declaration,
298 /// Implements `suspend {...}`.
299 /// Uses the `pl_node` union field. Payload is `Block`.
300 suspend_block,
301 /// Boolean NOT. See also `bit_not`.
302 /// Uses the `un_node` field.
303 bool_not,
304 /// Short-circuiting boolean `and`. `lhs` is a boolean `Ref` and the other operand
305 /// is a block, which is evaluated if `lhs` is `true`.
306 /// Uses the `pl_node` union field. Payload is `BoolBr`.
307 bool_br_and,
308 /// Short-circuiting boolean `or`. `lhs` is a boolean `Ref` and the other operand
309 /// is a block, which is evaluated if `lhs` is `false`.
310 /// Uses the `pl_node` union field. Payload is `BoolBr`.
311 bool_br_or,
312 /// Return a value from a block.
313 /// Uses the `break` union field.
314 /// Uses the source information from previous instruction.
315 @"break",
316 /// Return a value from a block. This instruction is used as the terminator
317 /// of a `block_inline`. It allows using the return value from `Sema.analyzeBody`.
318 /// This instruction may also be used when it is known that there is only one
319 /// break instruction in a block, and the target block is the parent.
320 /// Uses the `break` union field.
321 break_inline,
322 /// Checks that comptime control flow does not happen inside a runtime block.
323 /// Uses the `un_node` union field.
324 check_comptime_control_flow,
325 /// Function call.
326 /// Uses the `pl_node` union field with payload `Call`.
327 /// AST node is the function call.
328 call,
329 /// Function call using `a.b()` syntax.
330 /// Uses the named field as the callee. If there is no such field, searches in the type for
331 /// a decl matching the field name. The decl is resolved and we ensure that it's a function
332 /// which can accept the object as the first parameter, with one pointer fixup. This
333 /// function is then used as the callee, with the object as an implicit first parameter.
334 /// Uses the `pl_node` union field with payload `FieldCall`.
335 /// AST node is the function call.
336 field_call,
337 /// Implements the `@call` builtin.
338 /// Uses the `pl_node` union field with payload `BuiltinCall`.
339 /// AST node is the builtin call.
340 builtin_call,
341 /// `<`
342 /// Uses the `pl_node` union field. Payload is `Bin`.
343 cmp_lt,
344 /// `<=`
345 /// Uses the `pl_node` union field. Payload is `Bin`.
346 cmp_lte,
347 /// `==`
348 /// Uses the `pl_node` union field. Payload is `Bin`.
349 cmp_eq,
350 /// `>=`
351 /// Uses the `pl_node` union field. Payload is `Bin`.
352 cmp_gte,
353 /// `>`
354 /// Uses the `pl_node` union field. Payload is `Bin`.
355 cmp_gt,
356 /// `!=`
357 /// Uses the `pl_node` union field. Payload is `Bin`.
358 cmp_neq,
359 /// Conditional branch. Splits control flow based on a boolean condition value.
360 /// Uses the `pl_node` union field. AST node is an if, while, for, etc.
361 /// Payload is `CondBr`.
362 condbr,
363 /// Same as `condbr`, except the condition is coerced to a comptime value, and
364 /// only the taken branch is analyzed. The then block and else block must
365 /// terminate with an "inline" variant of a noreturn instruction.
366 condbr_inline,
367 /// Given an operand which is an error union, splits control flow. In
368 /// case of error, control flow goes into the block that is part of this
369 /// instruction, which is guaranteed to end with a return instruction
370 /// and never breaks out of the block.
371 /// In the case of non-error, control flow proceeds to the next instruction
372 /// after the `try`, with the result of this instruction being the unwrapped
373 /// payload value, as if `err_union_payload_unsafe` was executed on the operand.
374 /// Uses the `pl_node` union field. Payload is `Try`.
375 @"try",
376 /// Same as `try` except the operand is a pointer and the result is a pointer.
377 try_ptr,
378 /// An error set type definition. Contains a list of field names.
379 /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`.
380 error_set_decl,
381 error_set_decl_anon,
382 error_set_decl_func,
383 /// Declares the beginning of a statement. Used for debug info.
384 /// Uses the `dbg_stmt` union field. The line and column are offset
385 /// from the parent declaration.
386 dbg_stmt,
387 /// Marks a variable declaration. Used for debug info.
388 /// Uses the `str_op` union field. The string is the local variable name,
389 /// and the operand is the pointer to the variable's location. The local
390 /// may be a const or a var.
391 dbg_var_ptr,
392 /// Same as `dbg_var_ptr` but the local is always a const and the operand
393 /// is the local's value.
394 dbg_var_val,
395 /// Uses a name to identify a Decl and takes a pointer to it.
396 /// Uses the `str_tok` union field.
397 decl_ref,
398 /// Uses a name to identify a Decl and uses it as a value.
399 /// Uses the `str_tok` union field.
400 decl_val,
401 /// Load the value from a pointer. Assumes `x.*` syntax.
402 /// Uses `un_node` field. AST node is the `x.*` syntax.
403 load,
404 /// Arithmetic division. Asserts no integer overflow.
405 /// Uses the `pl_node` union field. Payload is `Bin`.
406 div,
407 /// Given a pointer to an array, slice, or pointer, returns a pointer to the element at
408 /// the provided index.
409 /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
410 elem_ptr_node,
411 /// Same as `elem_ptr_node` but used only for for loop.
412 /// Uses the `pl_node` union field. AST node is the condition of a for loop.
413 /// Payload is `Bin`.
414 /// No OOB safety check is emitted.
415 elem_ptr,
416 /// Given an array, slice, or pointer, returns the element at the provided index.
417 /// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
418 elem_val_node,
419 /// Same as `elem_val_node` but used only for for loop.
420 /// Uses the `pl_node` union field. AST node is the condition of a for loop.
421 /// Payload is `Bin`.
422 /// No OOB safety check is emitted.
423 elem_val,
424 /// Same as `elem_val` but takes the index as an immediate value.
425 /// No OOB safety check is emitted. A prior instruction must validate this operation.
426 /// Uses the `elem_val_imm` union field.
427 elem_val_imm,
428 /// Emits a compile error if the operand is not `void`.
429 /// Uses the `un_node` field.
430 ensure_result_used,
431 /// Emits a compile error if an error is ignored.
432 /// Uses the `un_node` field.
433 ensure_result_non_error,
434 /// Emits a compile error error union payload is not void.
435 ensure_err_union_payload_void,
436 /// Create a `E!T` type.
437 /// Uses the `pl_node` field with `Bin` payload.
438 error_union_type,
439 /// `error.Foo` syntax. Uses the `str_tok` field of the Data union.
440 error_value,
441 /// Implements the `@export` builtin function, based on either an identifier to a Decl,
442 /// or field access of a Decl. The thing being exported is the Decl.
443 /// Uses the `pl_node` union field. Payload is `Export`.
444 @"export",
445 /// Implements the `@export` builtin function, based on a comptime-known value.
446 /// The thing being exported is the comptime-known value which is the operand.
447 /// Uses the `pl_node` union field. Payload is `ExportValue`.
448 export_value,
449 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
450 /// to the named field. The field name is stored in string_bytes. Used by a.b syntax.
451 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
452 field_ptr,
453 /// Given a struct or object that contains virtual fields, returns the named field.
454 /// The field name is stored in string_bytes. Used by a.b syntax.
455 /// This instruction also accepts a pointer.
456 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
457 field_val,
458 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
459 /// to the named field. The field name is a comptime instruction. Used by @field.
460 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
461 field_ptr_named,
462 /// Given a struct or object that contains virtual fields, returns the named field.
463 /// The field name is a comptime instruction. Used by @field.
464 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
465 field_val_named,
466 /// Returns a function type, or a function instance, depending on whether
467 /// the body_len is 0. Calling convention is auto.
468 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.
469 func,
470 /// Same as `func` but has an inferred error set.
471 func_inferred,
472 /// Represents a function declaration or function prototype, depending on
473 /// whether body_len is 0.
474 /// Uses the `pl_node` union field. `payload_index` points to a `FuncFancy`.
475 func_fancy,
476 /// Implements the `@import` builtin.
477 /// Uses the `str_tok` field.
478 import,
479 /// Integer literal that fits in a u64. Uses the `int` union field.
480 int,
481 /// Arbitrary sized integer literal. Uses the `str` union field.
482 int_big,
483 /// A float literal that fits in a f64. Uses the float union value.
484 float,
485 /// A float literal that fits in a f128. Uses the `pl_node` union value.
486 /// Payload is `Float128`.
487 float128,
488 /// Make an integer type out of signedness and bit count.
489 /// Payload is `int_type`
490 int_type,
491 /// Return a boolean false if an optional is null. `x != null`
492 /// Uses the `un_node` field.
493 is_non_null,
494 /// Return a boolean false if an optional is null. `x.* != null`
495 /// Uses the `un_node` field.
496 is_non_null_ptr,
497 /// Return a boolean false if value is an error
498 /// Uses the `un_node` field.
499 is_non_err,
500 /// Return a boolean false if dereferenced pointer is an error
501 /// Uses the `un_node` field.
502 is_non_err_ptr,
503 /// Same as `is_non_er` but doesn't validate that the type can be an error.
504 /// Uses the `un_node` field.
505 ret_is_non_err,
506 /// A labeled block of code that loops forever. At the end of the body will have either
507 /// a `repeat` instruction or a `repeat_inline` instruction.
508 /// Uses the `pl_node` field. The AST node is either a for loop or while loop.
509 /// This ZIR instruction is needed because AIR does not (yet?) match ZIR, and Sema
510 /// needs to emit more than 1 AIR block for this instruction.
511 /// The payload is `Block`.
512 loop,
513 /// Sends runtime control flow back to the beginning of the current block.
514 /// Uses the `node` field.
515 repeat,
516 /// Sends comptime control flow back to the beginning of the current block.
517 /// Uses the `node` field.
518 repeat_inline,
519 /// Asserts that all the lengths provided match. Used to build a for loop.
520 /// Return value is the length as a usize.
521 /// Uses the `pl_node` field with payload `MultiOp`.
522 /// There is exactly one item corresponding to each AST node inside the for
523 /// loop condition. Any item may be `none`, indicating an unbounded range.
524 /// Illegal behaviors:
525 /// * If all lengths are unbounded ranges (always a compile error).
526 /// * If any two lengths do not match each other.
527 for_len,
528 /// Merge two error sets into one, `E1 || E2`.
529 /// Uses the `pl_node` field with payload `Bin`.
530 merge_error_sets,
531 /// Turns an R-Value into a const L-Value. In other words, it takes a value,
532 /// stores it in a memory location, and returns a const pointer to it. If the value
533 /// is `comptime`, the memory location is global static constant data. Otherwise,
534 /// the memory location is in the stack frame, local to the scope containing the
535 /// instruction.
536 /// Uses the `un_tok` union field.
537 ref,
538 /// Sends control flow back to the function's callee.
539 /// Includes an operand as the return value.
540 /// Includes an AST node source location.
541 /// Uses the `un_node` union field.
542 ret_node,
543 /// Sends control flow back to the function's callee.
544 /// The operand is a `ret_ptr` instruction, where the return value can be found.
545 /// Includes an AST node source location.
546 /// Uses the `un_node` union field.
547 ret_load,
548 /// Sends control flow back to the function's callee.
549 /// Includes an operand as the return value.
550 /// Includes a token source location.
551 /// Uses the `un_tok` union field.
552 ret_implicit,
553 /// Sends control flow back to the function's callee.
554 /// The return operand is `error.foo` where `foo` is given by the string.
555 /// If the current function has an inferred error set, the error given by the
556 /// name is added to it.
557 /// Uses the `str_tok` union field.
558 ret_err_value,
559 /// A string name is provided which is an anonymous error set value.
560 /// If the current function has an inferred error set, the error given by the
561 /// name is added to it.
562 /// Results in the error code. Note that control flow is not diverted with
563 /// this instruction; a following 'ret' instruction will do the diversion.
564 /// Uses the `str_tok` union field.
565 ret_err_value_code,
566 /// Obtains a pointer to the return value.
567 /// Uses the `node` union field.
568 ret_ptr,
569 /// Obtains the return type of the in-scope function.
570 /// Uses the `node` union field.
571 ret_type,
572 /// Create a pointer type which can have a sentinel, alignment, address space, and/or bit range.
573 /// Uses the `ptr_type` union field.
574 ptr_type,
575 /// Slice operation `lhs[rhs..]`. No sentinel and no end offset.
576 /// Returns a pointer to the subslice.
577 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceStart`.
578 slice_start,
579 /// Slice operation `array_ptr[start..end]`. No sentinel.
580 /// Returns a pointer to the subslice.
581 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceEnd`.
582 slice_end,
583 /// Slice operation `array_ptr[start..end:sentinel]`.
584 /// Returns a pointer to the subslice.
585 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.
586 slice_sentinel,
587 /// Slice operation `array_ptr[start..][0..len]`. Optional sentinel.
588 /// Returns a pointer to the subslice.
589 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.
590 slice_length,
591 /// Same as `store` except provides a source location.
592 /// Uses the `pl_node` union field. Payload is `Bin`.
593 store_node,
594 /// Same as `store_node` but the type of the value being stored will be
595 /// used to infer the pointer type of an `alloc_inferred`.
596 /// Uses the `pl_node` union field. Payload is `Bin`.
597 store_to_inferred_ptr,
598 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
599 /// Uses the `str` union field.
600 str,
601 /// Arithmetic negation. Asserts no integer overflow.
602 /// Same as sub with a lhs of 0, split into a separate instruction to save memory.
603 /// Uses `un_node`.
604 negate,
605 /// Twos complement wrapping integer negation.
606 /// Same as subwrap with a lhs of 0, split into a separate instruction to save memory.
607 /// Uses `un_node`.
608 negate_wrap,
609 /// Returns the type of a value.
610 /// Uses the `un_node` field.
611 typeof,
612 /// Implements `@TypeOf` for one operand.
613 /// Uses the `pl_node` field.
614 typeof_builtin,
615 /// Given a value, look at the type of it, which must be an integer type.
616 /// Returns the integer type for the RHS of a shift operation.
617 /// Uses the `un_node` field.
618 typeof_log2_int_type,
619 /// Asserts control-flow will not reach this instruction (`unreachable`).
620 /// Uses the `@"unreachable"` union field.
621 @"unreachable",
622 /// Bitwise XOR. `^`
623 /// Uses the `pl_node` union field. Payload is `Bin`.
624 xor,
625 /// Create an optional type '?T'
626 /// Uses the `un_node` field.
627 optional_type,
628 /// ?T => T with safety.
629 /// Given an optional value, returns the payload value, with a safety check that
630 /// the value is non-null. Used for `orelse`, `if` and `while`.
631 /// Uses the `un_node` field.
632 optional_payload_safe,
633 /// ?T => T without safety.
634 /// Given an optional value, returns the payload value. No safety checks.
635 /// Uses the `un_node` field.
636 optional_payload_unsafe,
637 /// *?T => *T with safety.
638 /// Given a pointer to an optional value, returns a pointer to the payload value,
639 /// with a safety check that the value is non-null. Used for `orelse`, `if` and `while`.
640 /// Uses the `un_node` field.
641 optional_payload_safe_ptr,
642 /// *?T => *T without safety.
643 /// Given a pointer to an optional value, returns a pointer to the payload value.
644 /// No safety checks.
645 /// Uses the `un_node` field.
646 optional_payload_unsafe_ptr,
647 /// E!T => T without safety.
648 /// Given an error union value, returns the payload value. No safety checks.
649 /// Uses the `un_node` field.
650 err_union_payload_unsafe,
651 /// *E!T => *T without safety.
652 /// Given a pointer to a error union value, returns a pointer to the payload value.
653 /// No safety checks.
654 /// Uses the `un_node` field.
655 err_union_payload_unsafe_ptr,
656 /// E!T => E without safety.
657 /// Given an error union value, returns the error code. No safety checks.
658 /// Uses the `un_node` field.
659 err_union_code,
660 /// *E!T => E without safety.
661 /// Given a pointer to an error union value, returns the error code. No safety checks.
662 /// Uses the `un_node` field.
663 err_union_code_ptr,
664 /// An enum literal. Uses the `str_tok` union field.
665 enum_literal,
666 /// A switch expression. Uses the `pl_node` union field.
667 /// AST node is the switch, payload is `SwitchBlock`.
668 switch_block,
669 /// A switch expression. Uses the `pl_node` union field.
670 /// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer.
671 switch_block_ref,
672 /// A switch on an error union `a catch |err| switch (err) {...}`.
673 /// Uses the `pl_node` union field. AST node is the `catch`, payload is `SwitchBlockErrUnion`.
674 switch_block_err_union,
675 /// Check that operand type supports the dereference operand (.*).
676 /// Uses the `un_node` field.
677 validate_deref,
678 /// Check that the operand's type is an array or tuple with the given number of elements.
679 /// Uses the `pl_node` field. Payload is `ValidateDestructure`.
680 validate_destructure,
681 /// Given a struct or union, and a field name as a Ref,
682 /// returns the field type. Uses the `pl_node` field. Payload is `FieldTypeRef`.
683 field_type_ref,
684 /// Given a pointer, initializes all error unions and optionals in the pointee to payloads,
685 /// returning the base payload pointer. For instance, converts *E!?T into a valid *T
686 /// (clobbering any existing error or null value).
687 /// Uses the `un_node` field.
688 opt_eu_base_ptr_init,
689 /// Coerce a given value such that when a reference is taken, the resulting pointer will be
690 /// coercible to the given type. For instance, given a value of type 'u32' and the pointer
691 /// type '*u64', coerces the value to a 'u64'. Asserts that the type is a pointer type.
692 /// Uses the `pl_node` field. Payload is `Bin`.
693 /// LHS is the pointer type, RHS is the value.
694 coerce_ptr_elem_ty,
695 /// Given a type, validate that it is a pointer type suitable for return from the address-of
696 /// operator. Emit a compile error if not.
697 /// Uses the `un_tok` union field. Token is the `&` operator. Operand is the type.
698 validate_ref_ty,
699
700 // The following tags all relate to struct initialization expressions.
701
702 /// A struct literal with a specified explicit type, with no fields.
703 /// Uses the `un_node` field.
704 struct_init_empty,
705 /// An anonymous struct literal with a known result type, with no fields.
706 /// Uses the `un_node` field.
707 struct_init_empty_result,
708 /// An anonymous struct literal with no fields, returned by reference, with a known result
709 /// type for the pointer. Asserts that the type is a pointer.
710 /// Uses the `un_node` field.
711 struct_init_empty_ref_result,
712 /// Struct initialization without a type. Creates a value of an anonymous struct type.
713 /// Uses the `pl_node` field. Payload is `StructInitAnon`.
714 struct_init_anon,
715 /// Finalizes a typed struct or union initialization, performs validation, and returns the
716 /// struct or union value. The given type must be validated prior to this instruction, using
717 /// `validate_struct_init_ty` or `validate_struct_init_result_ty`. If the given type is
718 /// generic poison, this is downgraded to an anonymous initialization.
719 /// Uses the `pl_node` field. Payload is `StructInit`.
720 struct_init,
721 /// Struct initialization syntax, make the result a pointer. Equivalent to `struct_init`
722 /// followed by `ref` - this ZIR tag exists as an optimization for a common pattern.
723 /// Uses the `pl_node` field. Payload is `StructInit`.
724 struct_init_ref,
725 /// Checks that the type supports struct init syntax. Always returns void.
726 /// Uses the `un_node` field.
727 validate_struct_init_ty,
728 /// Like `validate_struct_init_ty`, but additionally accepts types which structs coerce to.
729 /// Used on the known result type of a struct init expression. Always returns void.
730 /// Uses the `un_node` field.
731 validate_struct_init_result_ty,
732 /// Given a set of `struct_init_field_ptr` instructions, assumes they are all part of a
733 /// struct initialization expression, and emits compile errors for duplicate fields as well
734 /// as missing fields, if applicable.
735 /// This instruction asserts that there is at least one struct_init_field_ptr instruction,
736 /// because it must use one of them to find out the struct type.
737 /// Uses the `pl_node` field. Payload is `Block`.
738 validate_ptr_struct_init,
739 /// Given a type being used for a struct initialization expression, returns the type of the
740 /// field with the given name.
741 /// Uses the `pl_node` field. Payload is `FieldType`.
742 struct_init_field_type,
743 /// Given a pointer being used as the result pointer of a struct initialization expression,
744 /// return a pointer to the field of the given name.
745 /// Uses the `pl_node` field. The AST node is the field initializer. Payload is Field.
746 struct_init_field_ptr,
747
748 // The following tags all relate to array initialization expressions.
749
750 /// Array initialization without a type. Creates a value of a tuple type.
751 /// Uses the `pl_node` field. Payload is `MultiOp`.
752 array_init_anon,
753 /// Array initialization syntax with a known type. The given type must be validated prior to
754 /// this instruction, using some `validate_array_init_*_ty` instruction.
755 /// Uses the `pl_node` field. Payload is `MultiOp`, where the first operand is the type.
756 array_init,
757 /// Array initialization syntax, make the result a pointer. Equivalent to `array_init`
758 /// followed by `ref`- this ZIR tag exists as an optimization for a common pattern.
759 /// Uses the `pl_node` field. Payload is `MultiOp`, where the first operand is the type.
760 array_init_ref,
761 /// Checks that the type supports array init syntax. Always returns void.
762 /// Uses the `pl_node` field. Payload is `ArrayInit`.
763 validate_array_init_ty,
764 /// Like `validate_array_init_ty`, but additionally accepts types which arrays coerce to.
765 /// Used on the known result type of an array init expression. Always returns void.
766 /// Uses the `pl_node` field. Payload is `ArrayInit`.
767 validate_array_init_result_ty,
768 /// Given a pointer or slice type and an element count, return the expected type of an array
769 /// initializer such that a pointer to the initializer has the given pointer type, checking
770 /// that this type supports array init syntax and emitting a compile error if not. Preserves
771 /// error union and optional wrappers on the array type, if any.
772 /// Asserts that the given type is a pointer or slice type.
773 /// Uses the `pl_node` field. Payload is `ArrayInitRefTy`.
774 validate_array_init_ref_ty,
775 /// Given a set of `array_init_elem_ptr` instructions, assumes they are all part of an array
776 /// initialization expression, and emits a compile error if the number of elements does not
777 /// match the array type.
778 /// This instruction asserts that there is at least one `array_init_elem_ptr` instruction,
779 /// because it must use one of them to find out the array type.
780 /// Uses the `pl_node` field. Payload is `Block`.
781 validate_ptr_array_init,
782 /// Given a type being used for an array initialization expression, returns the type of the
783 /// element at the given index.
784 /// Uses the `bin` union field. lhs is the indexable type, rhs is the index.
785 array_init_elem_type,
786 /// Given a pointer being used as the result pointer of an array initialization expression,
787 /// return a pointer to the element at the given index.
788 /// Uses the `pl_node` union field. AST node is an element inside array initialization
789 /// syntax. Payload is `ElemPtrImm`.
790 array_init_elem_ptr,
791
792 /// Implements the `@unionInit` builtin.
793 /// Uses the `pl_node` field. Payload is `UnionInit`.
794 union_init,
795 /// Implements the `@typeInfo` builtin. Uses `un_node`.
796 type_info,
797 /// Implements the `@sizeOf` builtin. Uses `un_node`.
798 size_of,
799 /// Implements the `@bitSizeOf` builtin. Uses `un_node`.
800 bit_size_of,
801
802 /// Implement builtin `@intFromPtr`. Uses `un_node`.
803 /// Convert a pointer to a `usize` integer.
804 int_from_ptr,
805 /// Emit an error message and fail compilation.
806 /// Uses the `un_node` field.
807 compile_error,
808 /// Changes the maximum number of backwards branches that compile-time
809 /// code execution can use before giving up and making a compile error.
810 /// Uses the `un_node` union field.
811 set_eval_branch_quota,
812 /// Converts an enum value into an integer. Resulting type will be the tag type
813 /// of the enum. Uses `un_node`.
814 int_from_enum,
815 /// Implement builtin `@alignOf`. Uses `un_node`.
816 align_of,
817 /// Implement builtin `@intFromBool`. Uses `un_node`.
818 int_from_bool,
819 /// Implement builtin `@embedFile`. Uses `un_node`.
820 embed_file,
821 /// Implement builtin `@errorName`. Uses `un_node`.
822 error_name,
823 /// Implement builtin `@panic`. Uses `un_node`.
824 panic,
825 /// Implements `@trap`.
826 /// Uses the `node` field.
827 trap,
828 /// Implement builtin `@setRuntimeSafety`. Uses `un_node`.
829 set_runtime_safety,
830 /// Implement builtin `@sqrt`. Uses `un_node`.
831 sqrt,
832 /// Implement builtin `@sin`. Uses `un_node`.
833 sin,
834 /// Implement builtin `@cos`. Uses `un_node`.
835 cos,
836 /// Implement builtin `@tan`. Uses `un_node`.
837 tan,
838 /// Implement builtin `@exp`. Uses `un_node`.
839 exp,
840 /// Implement builtin `@exp2`. Uses `un_node`.
841 exp2,
842 /// Implement builtin `@log`. Uses `un_node`.
843 log,
844 /// Implement builtin `@log2`. Uses `un_node`.
845 log2,
846 /// Implement builtin `@log10`. Uses `un_node`.
847 log10,
848 /// Implement builtin `@abs`. Uses `un_node`.
849 abs,
850 /// Implement builtin `@floor`. Uses `un_node`.
851 floor,
852 /// Implement builtin `@ceil`. Uses `un_node`.
853 ceil,
854 /// Implement builtin `@trunc`. Uses `un_node`.
855 trunc,
856 /// Implement builtin `@round`. Uses `un_node`.
857 round,
858 /// Implement builtin `@tagName`. Uses `un_node`.
859 tag_name,
860 /// Implement builtin `@typeName`. Uses `un_node`.
861 type_name,
862 /// Implement builtin `@Frame`. Uses `un_node`.
863 frame_type,
864 /// Implement builtin `@frameSize`. Uses `un_node`.
865 frame_size,
866
867 /// Implements the `@intFromFloat` builtin.
868 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
869 int_from_float,
870 /// Implements the `@floatFromInt` builtin.
871 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
872 float_from_int,
873 /// Implements the `@ptrFromInt` builtin.
874 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
875 ptr_from_int,
876 /// Converts an integer into an enum value.
877 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
878 enum_from_int,
879 /// Convert a larger float type to any other float type, possibly causing
880 /// a loss of precision.
881 /// Uses the `pl_node` field. AST is the `@floatCast` syntax.
882 /// Payload is `Bin` with lhs as the dest type, rhs the operand.
883 float_cast,
884 /// Implements the `@intCast` builtin.
885 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
886 /// Convert an integer value to another integer type, asserting that the destination type
887 /// can hold the same mathematical value.
888 int_cast,
889 /// Implements the `@ptrCast` builtin.
890 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
891 /// Not every `@ptrCast` will correspond to this instruction - see also
892 /// `ptr_cast_full` in `Extended`.
893 ptr_cast,
894 /// Implements the `@truncate` builtin.
895 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
896 truncate,
897
898 /// Implements the `@hasDecl` builtin.
899 /// Uses the `pl_node` union field. Payload is `Bin`.
900 has_decl,
901 /// Implements the `@hasField` builtin.
902 /// Uses the `pl_node` union field. Payload is `Bin`.
903 has_field,
904
905 /// Implements the `@clz` builtin. Uses the `un_node` union field.
906 clz,
907 /// Implements the `@ctz` builtin. Uses the `un_node` union field.
908 ctz,
909 /// Implements the `@popCount` builtin. Uses the `un_node` union field.
910 pop_count,
911 /// Implements the `@byteSwap` builtin. Uses the `un_node` union field.
912 byte_swap,
913 /// Implements the `@bitReverse` builtin. Uses the `un_node` union field.
914 bit_reverse,
915
916 /// Implements the `@bitOffsetOf` builtin.
917 /// Uses the `pl_node` union field with payload `Bin`.
918 bit_offset_of,
919 /// Implements the `@offsetOf` builtin.
920 /// Uses the `pl_node` union field with payload `Bin`.
921 offset_of,
922 /// Implements the `@splat` builtin.
923 /// Uses the `pl_node` union field with payload `Bin`.
924 splat,
925 /// Implements the `@reduce` builtin.
926 /// Uses the `pl_node` union field with payload `Bin`.
927 reduce,
928 /// Implements the `@shuffle` builtin.
929 /// Uses the `pl_node` union field with payload `Shuffle`.
930 shuffle,
931 /// Implements the `@atomicLoad` builtin.
932 /// Uses the `pl_node` union field with payload `AtomicLoad`.
933 atomic_load,
934 /// Implements the `@atomicRmw` builtin.
935 /// Uses the `pl_node` union field with payload `AtomicRmw`.
936 atomic_rmw,
937 /// Implements the `@atomicStore` builtin.
938 /// Uses the `pl_node` union field with payload `AtomicStore`.
939 atomic_store,
940 /// Implements the `@mulAdd` builtin.
941 /// Uses the `pl_node` union field with payload `MulAdd`.
942 /// The addend communicates the type of the builtin.
943 /// The mulends need to be coerced to the same type.
944 mul_add,
945 /// Implements the `@fieldParentPtr` builtin.
946 /// Uses the `pl_node` union field with payload `FieldParentPtr`.
947 field_parent_ptr,
948 /// Implements the `@memcpy` builtin.
949 /// Uses the `pl_node` union field with payload `Bin`.
950 memcpy,
951 /// Implements the `@memset` builtin.
952 /// Uses the `pl_node` union field with payload `Bin`.
953 memset,
954 /// Implements the `@min` builtin for 2 args.
955 /// Uses the `pl_node` union field with payload `Bin`
956 min,
957 /// Implements the `@max` builtin for 2 args.
958 /// Uses the `pl_node` union field with payload `Bin`
959 max,
960 /// Implements the `@cImport` builtin.
961 /// Uses the `pl_node` union field with payload `Block`.
962 c_import,
963
964 /// Allocates stack local memory.
965 /// Uses the `un_node` union field. The operand is the type of the allocated object.
966 /// The node source location points to a var decl node.
967 /// A `make_ptr_const` instruction should be used once the value has
968 /// been stored to the allocation. To ensure comptime value detection
969 /// functions, there are some restrictions on how this pointer should be
970 /// used prior to the `make_ptr_const` instruction: no pointer derived
971 /// from this `alloc` may be returned from a block or stored to another
972 /// address. In other words, it must be trivial to determine whether any
973 /// given pointer derives from this one.
974 alloc,
975 /// Same as `alloc` except mutable. As such, `make_ptr_const` need not be used,
976 /// and there are no restrictions on the usage of the pointer.
977 alloc_mut,
978 /// Allocates comptime-mutable memory.
979 /// Uses the `un_node` union field. The operand is the type of the allocated object.
980 /// The node source location points to a var decl node.
981 alloc_comptime_mut,
982 /// Same as `alloc` except the type is inferred.
983 /// Uses the `node` union field.
984 alloc_inferred,
985 /// Same as `alloc_inferred` except mutable.
986 alloc_inferred_mut,
987 /// Allocates comptime const memory.
988 /// Uses the `node` union field. The type of the allocated object is inferred.
989 /// The node source location points to a var decl node.
990 alloc_inferred_comptime,
991 /// Same as `alloc_comptime_mut` except the type is inferred.
992 alloc_inferred_comptime_mut,
993 /// Each `store_to_inferred_ptr` puts the type of the stored value into a set,
994 /// and then `resolve_inferred_alloc` triggers peer type resolution on the set.
995 /// The operand is a `alloc_inferred` or `alloc_inferred_mut` instruction, which
996 /// is the allocation that needs to have its type inferred.
997 /// Uses the `un_node` field. The AST node is the var decl.
998 resolve_inferred_alloc,
999 /// Turns a pointer coming from an `alloc` or `Extended.alloc` into a constant
1000 /// version of the same pointer. For inferred allocations this is instead implicitly
1001 /// handled by the `resolve_inferred_alloc` instruction.
1002 /// Uses the `un_node` union field.
1003 make_ptr_const,
1004
1005 /// Implements `resume` syntax. Uses `un_node` field.
1006 @"resume",
1007 @"await",
1008
1009 /// When a type or function refers to a comptime value from an outer
1010 /// scope, that forms a closure over comptime value. The outer scope
1011 /// will record a capture of that value, which encodes its current state
1012 /// and marks it to persist. Uses `un_tok` field. Operand is the
1013 /// instruction value to capture.
1014 closure_capture,
1015 /// The inner scope of a closure uses closure_get to retrieve the value
1016 /// stored by the outer scope. Uses `inst_node` field. Operand is the
1017 /// closure_capture instruction ref.
1018 closure_get,
1019
1020 /// A defer statement.
1021 /// Uses the `defer` union field.
1022 @"defer",
1023 /// An errdefer statement with a code.
1024 /// Uses the `err_defer_code` union field.
1025 defer_err_code,
1026
1027 /// Requests that Sema update the saved error return trace index for the enclosing
1028 /// block, if the operand is .none or of an error/error-union type.
1029 /// Uses the `save_err_ret_index` field.
1030 save_err_ret_index,
1031 /// Specialized form of `Extended.restore_err_ret_index`.
1032 /// Unconditionally restores the error return index to its last saved state
1033 /// in the block referred to by `operand`. If `operand` is `none`, restores
1034 /// to the point of function entry.
1035 /// Uses the `un_node` field.
1036 restore_err_ret_index_unconditional,
1037 /// Specialized form of `Extended.restore_err_ret_index`.
1038 /// Restores the error return index to its state at the entry of
1039 /// the current function conditional on `operand` being a non-error.
1040 /// If `operand` is `none`, restores unconditionally.
1041 /// Uses the `un_node` field.
1042 restore_err_ret_index_fn_entry,
1043
1044 /// The ZIR instruction tag is one of the `Extended` ones.
1045 /// Uses the `extended` union field.
1046 extended,
1047
1048 /// Returns whether the instruction is one of the control flow "noreturn" types.
1049 /// Function calls do not count.
1050 pub fn isNoReturn(tag: Tag) bool {
1051 return switch (tag) {
1052 .param,
1053 .param_comptime,
1054 .param_anytype,
1055 .param_anytype_comptime,
1056 .add,
1057 .addwrap,
1058 .add_sat,
1059 .add_unsafe,
1060 .alloc,
1061 .alloc_mut,
1062 .alloc_comptime_mut,
1063 .alloc_inferred,
1064 .alloc_inferred_mut,
1065 .alloc_inferred_comptime,
1066 .alloc_inferred_comptime_mut,
1067 .make_ptr_const,
1068 .array_cat,
1069 .array_mul,
1070 .array_type,
1071 .array_type_sentinel,
1072 .vector_type,
1073 .elem_type,
1074 .indexable_ptr_elem_type,
1075 .vector_elem_type,
1076 .indexable_ptr_len,
1077 .anyframe_type,
1078 .as_node,
1079 .as_shift_operand,
1080 .bit_and,
1081 .bitcast,
1082 .bit_or,
1083 .block,
1084 .block_comptime,
1085 .block_inline,
1086 .declaration,
1087 .suspend_block,
1088 .loop,
1089 .bool_br_and,
1090 .bool_br_or,
1091 .bool_not,
1092 .call,
1093 .field_call,
1094 .cmp_lt,
1095 .cmp_lte,
1096 .cmp_eq,
1097 .cmp_gte,
1098 .cmp_gt,
1099 .cmp_neq,
1100 .error_set_decl,
1101 .error_set_decl_anon,
1102 .error_set_decl_func,
1103 .dbg_stmt,
1104 .dbg_var_ptr,
1105 .dbg_var_val,
1106 .decl_ref,
1107 .decl_val,
1108 .load,
1109 .div,
1110 .elem_ptr,
1111 .elem_val,
1112 .elem_ptr_node,
1113 .elem_val_node,
1114 .elem_val_imm,
1115 .ensure_result_used,
1116 .ensure_result_non_error,
1117 .ensure_err_union_payload_void,
1118 .@"export",
1119 .export_value,
1120 .field_ptr,
1121 .field_val,
1122 .field_ptr_named,
1123 .field_val_named,
1124 .func,
1125 .func_inferred,
1126 .func_fancy,
1127 .has_decl,
1128 .int,
1129 .int_big,
1130 .float,
1131 .float128,
1132 .int_type,
1133 .is_non_null,
1134 .is_non_null_ptr,
1135 .is_non_err,
1136 .is_non_err_ptr,
1137 .ret_is_non_err,
1138 .mod_rem,
1139 .mul,
1140 .mulwrap,
1141 .mul_sat,
1142 .ref,
1143 .shl,
1144 .shl_sat,
1145 .shr,
1146 .store_node,
1147 .store_to_inferred_ptr,
1148 .str,
1149 .sub,
1150 .subwrap,
1151 .sub_sat,
1152 .negate,
1153 .negate_wrap,
1154 .typeof,
1155 .typeof_builtin,
1156 .xor,
1157 .optional_type,
1158 .optional_payload_safe,
1159 .optional_payload_unsafe,
1160 .optional_payload_safe_ptr,
1161 .optional_payload_unsafe_ptr,
1162 .err_union_payload_unsafe,
1163 .err_union_payload_unsafe_ptr,
1164 .err_union_code,
1165 .err_union_code_ptr,
1166 .ptr_type,
1167 .enum_literal,
1168 .merge_error_sets,
1169 .error_union_type,
1170 .bit_not,
1171 .error_value,
1172 .slice_start,
1173 .slice_end,
1174 .slice_sentinel,
1175 .slice_length,
1176 .import,
1177 .typeof_log2_int_type,
1178 .resolve_inferred_alloc,
1179 .set_eval_branch_quota,
1180 .switch_block,
1181 .switch_block_ref,
1182 .switch_block_err_union,
1183 .validate_deref,
1184 .validate_destructure,
1185 .union_init,
1186 .field_type_ref,
1187 .enum_from_int,
1188 .int_from_enum,
1189 .type_info,
1190 .size_of,
1191 .bit_size_of,
1192 .int_from_ptr,
1193 .align_of,
1194 .int_from_bool,
1195 .embed_file,
1196 .error_name,
1197 .set_runtime_safety,
1198 .sqrt,
1199 .sin,
1200 .cos,
1201 .tan,
1202 .exp,
1203 .exp2,
1204 .log,
1205 .log2,
1206 .log10,
1207 .abs,
1208 .floor,
1209 .ceil,
1210 .trunc,
1211 .round,
1212 .tag_name,
1213 .type_name,
1214 .frame_type,
1215 .frame_size,
1216 .int_from_float,
1217 .float_from_int,
1218 .ptr_from_int,
1219 .float_cast,
1220 .int_cast,
1221 .ptr_cast,
1222 .truncate,
1223 .has_field,
1224 .clz,
1225 .ctz,
1226 .pop_count,
1227 .byte_swap,
1228 .bit_reverse,
1229 .div_exact,
1230 .div_floor,
1231 .div_trunc,
1232 .mod,
1233 .rem,
1234 .shl_exact,
1235 .shr_exact,
1236 .bit_offset_of,
1237 .offset_of,
1238 .splat,
1239 .reduce,
1240 .shuffle,
1241 .atomic_load,
1242 .atomic_rmw,
1243 .atomic_store,
1244 .mul_add,
1245 .builtin_call,
1246 .field_parent_ptr,
1247 .max,
1248 .memcpy,
1249 .memset,
1250 .min,
1251 .c_import,
1252 .@"resume",
1253 .@"await",
1254 .ret_err_value_code,
1255 .extended,
1256 .closure_get,
1257 .closure_capture,
1258 .ret_ptr,
1259 .ret_type,
1260 .@"try",
1261 .try_ptr,
1262 .@"defer",
1263 .defer_err_code,
1264 .save_err_ret_index,
1265 .for_len,
1266 .opt_eu_base_ptr_init,
1267 .coerce_ptr_elem_ty,
1268 .struct_init_empty,
1269 .struct_init_empty_result,
1270 .struct_init_empty_ref_result,
1271 .struct_init_anon,
1272 .struct_init,
1273 .struct_init_ref,
1274 .validate_struct_init_ty,
1275 .validate_struct_init_result_ty,
1276 .validate_ptr_struct_init,
1277 .struct_init_field_type,
1278 .struct_init_field_ptr,
1279 .array_init_anon,
1280 .array_init,
1281 .array_init_ref,
1282 .validate_array_init_ty,
1283 .validate_array_init_result_ty,
1284 .validate_array_init_ref_ty,
1285 .validate_ptr_array_init,
1286 .array_init_elem_type,
1287 .array_init_elem_ptr,
1288 .validate_ref_ty,
1289 .restore_err_ret_index_unconditional,
1290 .restore_err_ret_index_fn_entry,
1291 => false,
1292
1293 .@"break",
1294 .break_inline,
1295 .condbr,
1296 .condbr_inline,
1297 .compile_error,
1298 .ret_node,
1299 .ret_load,
1300 .ret_implicit,
1301 .ret_err_value,
1302 .@"unreachable",
1303 .repeat,
1304 .repeat_inline,
1305 .panic,
1306 .trap,
1307 .check_comptime_control_flow,
1308 => true,
1309 };
1310 }
1311
1312 pub fn isParam(tag: Tag) bool {
1313 return switch (tag) {
1314 .param,
1315 .param_comptime,
1316 .param_anytype,
1317 .param_anytype_comptime,
1318 => true,
1319
1320 else => false,
1321 };
1322 }
1323
1324 /// AstGen uses this to find out if `Ref.void_value` should be used in place
1325 /// of the result of a given instruction. This allows Sema to forego adding
1326 /// the instruction to the map after analysis.
1327 pub fn isAlwaysVoid(tag: Tag, data: Data) bool {
1328 return switch (tag) {
1329 .dbg_stmt,
1330 .dbg_var_ptr,
1331 .dbg_var_val,
1332 .ensure_result_used,
1333 .ensure_result_non_error,
1334 .ensure_err_union_payload_void,
1335 .set_eval_branch_quota,
1336 .atomic_store,
1337 .store_node,
1338 .store_to_inferred_ptr,
1339 .resolve_inferred_alloc,
1340 .validate_deref,
1341 .validate_destructure,
1342 .@"export",
1343 .export_value,
1344 .set_runtime_safety,
1345 .memcpy,
1346 .memset,
1347 .check_comptime_control_flow,
1348 .@"defer",
1349 .defer_err_code,
1350 .save_err_ret_index,
1351 .restore_err_ret_index_unconditional,
1352 .restore_err_ret_index_fn_entry,
1353 .validate_struct_init_ty,
1354 .validate_struct_init_result_ty,
1355 .validate_ptr_struct_init,
1356 .validate_array_init_ty,
1357 .validate_array_init_result_ty,
1358 .validate_ptr_array_init,
1359 .validate_ref_ty,
1360 => true,
1361
1362 .param,
1363 .param_comptime,
1364 .param_anytype,
1365 .param_anytype_comptime,
1366 .add,
1367 .addwrap,
1368 .add_sat,
1369 .add_unsafe,
1370 .alloc,
1371 .alloc_mut,
1372 .alloc_comptime_mut,
1373 .alloc_inferred,
1374 .alloc_inferred_mut,
1375 .alloc_inferred_comptime,
1376 .alloc_inferred_comptime_mut,
1377 .make_ptr_const,
1378 .array_cat,
1379 .array_mul,
1380 .array_type,
1381 .array_type_sentinel,
1382 .vector_type,
1383 .elem_type,
1384 .indexable_ptr_elem_type,
1385 .vector_elem_type,
1386 .indexable_ptr_len,
1387 .anyframe_type,
1388 .as_node,
1389 .as_shift_operand,
1390 .bit_and,
1391 .bitcast,
1392 .bit_or,
1393 .block,
1394 .block_comptime,
1395 .block_inline,
1396 .declaration,
1397 .suspend_block,
1398 .loop,
1399 .bool_br_and,
1400 .bool_br_or,
1401 .bool_not,
1402 .call,
1403 .field_call,
1404 .cmp_lt,
1405 .cmp_lte,
1406 .cmp_eq,
1407 .cmp_gte,
1408 .cmp_gt,
1409 .cmp_neq,
1410 .error_set_decl,
1411 .error_set_decl_anon,
1412 .error_set_decl_func,
1413 .decl_ref,
1414 .decl_val,
1415 .load,
1416 .div,
1417 .elem_ptr,
1418 .elem_val,
1419 .elem_ptr_node,
1420 .elem_val_node,
1421 .elem_val_imm,
1422 .field_ptr,
1423 .field_val,
1424 .field_ptr_named,
1425 .field_val_named,
1426 .func,
1427 .func_inferred,
1428 .func_fancy,
1429 .has_decl,
1430 .int,
1431 .int_big,
1432 .float,
1433 .float128,
1434 .int_type,
1435 .is_non_null,
1436 .is_non_null_ptr,
1437 .is_non_err,
1438 .is_non_err_ptr,
1439 .ret_is_non_err,
1440 .mod_rem,
1441 .mul,
1442 .mulwrap,
1443 .mul_sat,
1444 .ref,
1445 .shl,
1446 .shl_sat,
1447 .shr,
1448 .str,
1449 .sub,
1450 .subwrap,
1451 .sub_sat,
1452 .negate,
1453 .negate_wrap,
1454 .typeof,
1455 .typeof_builtin,
1456 .xor,
1457 .optional_type,
1458 .optional_payload_safe,
1459 .optional_payload_unsafe,
1460 .optional_payload_safe_ptr,
1461 .optional_payload_unsafe_ptr,
1462 .err_union_payload_unsafe,
1463 .err_union_payload_unsafe_ptr,
1464 .err_union_code,
1465 .err_union_code_ptr,
1466 .ptr_type,
1467 .enum_literal,
1468 .merge_error_sets,
1469 .error_union_type,
1470 .bit_not,
1471 .error_value,
1472 .slice_start,
1473 .slice_end,
1474 .slice_sentinel,
1475 .slice_length,
1476 .import,
1477 .typeof_log2_int_type,
1478 .switch_block,
1479 .switch_block_ref,
1480 .switch_block_err_union,
1481 .union_init,
1482 .field_type_ref,
1483 .enum_from_int,
1484 .int_from_enum,
1485 .type_info,
1486 .size_of,
1487 .bit_size_of,
1488 .int_from_ptr,
1489 .align_of,
1490 .int_from_bool,
1491 .embed_file,
1492 .error_name,
1493 .sqrt,
1494 .sin,
1495 .cos,
1496 .tan,
1497 .exp,
1498 .exp2,
1499 .log,
1500 .log2,
1501 .log10,
1502 .abs,
1503 .floor,
1504 .ceil,
1505 .trunc,
1506 .round,
1507 .tag_name,
1508 .type_name,
1509 .frame_type,
1510 .frame_size,
1511 .int_from_float,
1512 .float_from_int,
1513 .ptr_from_int,
1514 .float_cast,
1515 .int_cast,
1516 .ptr_cast,
1517 .truncate,
1518 .has_field,
1519 .clz,
1520 .ctz,
1521 .pop_count,
1522 .byte_swap,
1523 .bit_reverse,
1524 .div_exact,
1525 .div_floor,
1526 .div_trunc,
1527 .mod,
1528 .rem,
1529 .shl_exact,
1530 .shr_exact,
1531 .bit_offset_of,
1532 .offset_of,
1533 .splat,
1534 .reduce,
1535 .shuffle,
1536 .atomic_load,
1537 .atomic_rmw,
1538 .mul_add,
1539 .builtin_call,
1540 .field_parent_ptr,
1541 .max,
1542 .min,
1543 .c_import,
1544 .@"resume",
1545 .@"await",
1546 .ret_err_value_code,
1547 .closure_get,
1548 .closure_capture,
1549 .@"break",
1550 .break_inline,
1551 .condbr,
1552 .condbr_inline,
1553 .compile_error,
1554 .ret_node,
1555 .ret_load,
1556 .ret_implicit,
1557 .ret_err_value,
1558 .ret_ptr,
1559 .ret_type,
1560 .@"unreachable",
1561 .repeat,
1562 .repeat_inline,
1563 .panic,
1564 .trap,
1565 .for_len,
1566 .@"try",
1567 .try_ptr,
1568 .opt_eu_base_ptr_init,
1569 .coerce_ptr_elem_ty,
1570 .struct_init_empty,
1571 .struct_init_empty_result,
1572 .struct_init_empty_ref_result,
1573 .struct_init_anon,
1574 .struct_init,
1575 .struct_init_ref,
1576 .struct_init_field_type,
1577 .struct_init_field_ptr,
1578 .array_init_anon,
1579 .array_init,
1580 .array_init_ref,
1581 .validate_array_init_ref_ty,
1582 .array_init_elem_type,
1583 .array_init_elem_ptr,
1584 => false,
1585
1586 .extended => switch (data.extended.opcode) {
1587 .fence, .set_cold, .breakpoint => true,
1588 else => false,
1589 },
1590 };
1591 }
1592
1593 /// Used by debug safety-checking code.
1594 pub const data_tags = list: {
1595 @setEvalBranchQuota(2000);
1596 break :list std.enums.directEnumArray(Tag, Data.FieldEnum, 0, .{
1597 .add = .pl_node,
1598 .addwrap = .pl_node,
1599 .add_sat = .pl_node,
1600 .add_unsafe = .pl_node,
1601 .sub = .pl_node,
1602 .subwrap = .pl_node,
1603 .sub_sat = .pl_node,
1604 .mul = .pl_node,
1605 .mulwrap = .pl_node,
1606 .mul_sat = .pl_node,
1607
1608 .param = .pl_tok,
1609 .param_comptime = .pl_tok,
1610 .param_anytype = .str_tok,
1611 .param_anytype_comptime = .str_tok,
1612 .array_cat = .pl_node,
1613 .array_mul = .pl_node,
1614 .array_type = .pl_node,
1615 .array_type_sentinel = .pl_node,
1616 .vector_type = .pl_node,
1617 .elem_type = .un_node,
1618 .indexable_ptr_elem_type = .un_node,
1619 .vector_elem_type = .un_node,
1620 .indexable_ptr_len = .un_node,
1621 .anyframe_type = .un_node,
1622 .as_node = .pl_node,
1623 .as_shift_operand = .pl_node,
1624 .bit_and = .pl_node,
1625 .bitcast = .pl_node,
1626 .bit_not = .un_node,
1627 .bit_or = .pl_node,
1628 .block = .pl_node,
1629 .block_comptime = .pl_node,
1630 .block_inline = .pl_node,
1631 .declaration = .pl_node,
1632 .suspend_block = .pl_node,
1633 .bool_not = .un_node,
1634 .bool_br_and = .pl_node,
1635 .bool_br_or = .pl_node,
1636 .@"break" = .@"break",
1637 .break_inline = .@"break",
1638 .check_comptime_control_flow = .un_node,
1639 .for_len = .pl_node,
1640 .call = .pl_node,
1641 .field_call = .pl_node,
1642 .cmp_lt = .pl_node,
1643 .cmp_lte = .pl_node,
1644 .cmp_eq = .pl_node,
1645 .cmp_gte = .pl_node,
1646 .cmp_gt = .pl_node,
1647 .cmp_neq = .pl_node,
1648 .condbr = .pl_node,
1649 .condbr_inline = .pl_node,
1650 .@"try" = .pl_node,
1651 .try_ptr = .pl_node,
1652 .error_set_decl = .pl_node,
1653 .error_set_decl_anon = .pl_node,
1654 .error_set_decl_func = .pl_node,
1655 .dbg_stmt = .dbg_stmt,
1656 .dbg_var_ptr = .str_op,
1657 .dbg_var_val = .str_op,
1658 .decl_ref = .str_tok,
1659 .decl_val = .str_tok,
1660 .load = .un_node,
1661 .div = .pl_node,
1662 .elem_ptr = .pl_node,
1663 .elem_ptr_node = .pl_node,
1664 .elem_val = .pl_node,
1665 .elem_val_node = .pl_node,
1666 .elem_val_imm = .elem_val_imm,
1667 .ensure_result_used = .un_node,
1668 .ensure_result_non_error = .un_node,
1669 .ensure_err_union_payload_void = .un_node,
1670 .error_union_type = .pl_node,
1671 .error_value = .str_tok,
1672 .@"export" = .pl_node,
1673 .export_value = .pl_node,
1674 .field_ptr = .pl_node,
1675 .field_val = .pl_node,
1676 .field_ptr_named = .pl_node,
1677 .field_val_named = .pl_node,
1678 .func = .pl_node,
1679 .func_inferred = .pl_node,
1680 .func_fancy = .pl_node,
1681 .import = .str_tok,
1682 .int = .int,
1683 .int_big = .str,
1684 .float = .float,
1685 .float128 = .pl_node,
1686 .int_type = .int_type,
1687 .is_non_null = .un_node,
1688 .is_non_null_ptr = .un_node,
1689 .is_non_err = .un_node,
1690 .is_non_err_ptr = .un_node,
1691 .ret_is_non_err = .un_node,
1692 .loop = .pl_node,
1693 .repeat = .node,
1694 .repeat_inline = .node,
1695 .merge_error_sets = .pl_node,
1696 .mod_rem = .pl_node,
1697 .ref = .un_tok,
1698 .ret_node = .un_node,
1699 .ret_load = .un_node,
1700 .ret_implicit = .un_tok,
1701 .ret_err_value = .str_tok,
1702 .ret_err_value_code = .str_tok,
1703 .ret_ptr = .node,
1704 .ret_type = .node,
1705 .ptr_type = .ptr_type,
1706 .slice_start = .pl_node,
1707 .slice_end = .pl_node,
1708 .slice_sentinel = .pl_node,
1709 .slice_length = .pl_node,
1710 .store_node = .pl_node,
1711 .store_to_inferred_ptr = .pl_node,
1712 .str = .str,
1713 .negate = .un_node,
1714 .negate_wrap = .un_node,
1715 .typeof = .un_node,
1716 .typeof_log2_int_type = .un_node,
1717 .@"unreachable" = .@"unreachable",
1718 .xor = .pl_node,
1719 .optional_type = .un_node,
1720 .optional_payload_safe = .un_node,
1721 .optional_payload_unsafe = .un_node,
1722 .optional_payload_safe_ptr = .un_node,
1723 .optional_payload_unsafe_ptr = .un_node,
1724 .err_union_payload_unsafe = .un_node,
1725 .err_union_payload_unsafe_ptr = .un_node,
1726 .err_union_code = .un_node,
1727 .err_union_code_ptr = .un_node,
1728 .enum_literal = .str_tok,
1729 .switch_block = .pl_node,
1730 .switch_block_ref = .pl_node,
1731 .switch_block_err_union = .pl_node,
1732 .validate_deref = .un_node,
1733 .validate_destructure = .pl_node,
1734 .field_type_ref = .pl_node,
1735 .union_init = .pl_node,
1736 .type_info = .un_node,
1737 .size_of = .un_node,
1738 .bit_size_of = .un_node,
1739 .opt_eu_base_ptr_init = .un_node,
1740 .coerce_ptr_elem_ty = .pl_node,
1741 .validate_ref_ty = .un_tok,
1742
1743 .int_from_ptr = .un_node,
1744 .compile_error = .un_node,
1745 .set_eval_branch_quota = .un_node,
1746 .int_from_enum = .un_node,
1747 .align_of = .un_node,
1748 .int_from_bool = .un_node,
1749 .embed_file = .un_node,
1750 .error_name = .un_node,
1751 .panic = .un_node,
1752 .trap = .node,
1753 .set_runtime_safety = .un_node,
1754 .sqrt = .un_node,
1755 .sin = .un_node,
1756 .cos = .un_node,
1757 .tan = .un_node,
1758 .exp = .un_node,
1759 .exp2 = .un_node,
1760 .log = .un_node,
1761 .log2 = .un_node,
1762 .log10 = .un_node,
1763 .abs = .un_node,
1764 .floor = .un_node,
1765 .ceil = .un_node,
1766 .trunc = .un_node,
1767 .round = .un_node,
1768 .tag_name = .un_node,
1769 .type_name = .un_node,
1770 .frame_type = .un_node,
1771 .frame_size = .un_node,
1772
1773 .int_from_float = .pl_node,
1774 .float_from_int = .pl_node,
1775 .ptr_from_int = .pl_node,
1776 .enum_from_int = .pl_node,
1777 .float_cast = .pl_node,
1778 .int_cast = .pl_node,
1779 .ptr_cast = .pl_node,
1780 .truncate = .pl_node,
1781 .typeof_builtin = .pl_node,
1782
1783 .has_decl = .pl_node,
1784 .has_field = .pl_node,
1785
1786 .clz = .un_node,
1787 .ctz = .un_node,
1788 .pop_count = .un_node,
1789 .byte_swap = .un_node,
1790 .bit_reverse = .un_node,
1791
1792 .div_exact = .pl_node,
1793 .div_floor = .pl_node,
1794 .div_trunc = .pl_node,
1795 .mod = .pl_node,
1796 .rem = .pl_node,
1797
1798 .shl = .pl_node,
1799 .shl_exact = .pl_node,
1800 .shl_sat = .pl_node,
1801 .shr = .pl_node,
1802 .shr_exact = .pl_node,
1803
1804 .bit_offset_of = .pl_node,
1805 .offset_of = .pl_node,
1806 .splat = .pl_node,
1807 .reduce = .pl_node,
1808 .shuffle = .pl_node,
1809 .atomic_load = .pl_node,
1810 .atomic_rmw = .pl_node,
1811 .atomic_store = .pl_node,
1812 .mul_add = .pl_node,
1813 .builtin_call = .pl_node,
1814 .field_parent_ptr = .pl_node,
1815 .max = .pl_node,
1816 .memcpy = .pl_node,
1817 .memset = .pl_node,
1818 .min = .pl_node,
1819 .c_import = .pl_node,
1820
1821 .alloc = .un_node,
1822 .alloc_mut = .un_node,
1823 .alloc_comptime_mut = .un_node,
1824 .alloc_inferred = .node,
1825 .alloc_inferred_mut = .node,
1826 .alloc_inferred_comptime = .node,
1827 .alloc_inferred_comptime_mut = .node,
1828 .resolve_inferred_alloc = .un_node,
1829 .make_ptr_const = .un_node,
1830
1831 .@"resume" = .un_node,
1832 .@"await" = .un_node,
1833
1834 .closure_capture = .un_tok,
1835 .closure_get = .inst_node,
1836
1837 .@"defer" = .@"defer",
1838 .defer_err_code = .defer_err_code,
1839
1840 .save_err_ret_index = .save_err_ret_index,
1841 .restore_err_ret_index_unconditional = .un_node,
1842 .restore_err_ret_index_fn_entry = .un_node,
1843
1844 .struct_init_empty = .un_node,
1845 .struct_init_empty_result = .un_node,
1846 .struct_init_empty_ref_result = .un_node,
1847 .struct_init_anon = .pl_node,
1848 .struct_init = .pl_node,
1849 .struct_init_ref = .pl_node,
1850 .validate_struct_init_ty = .un_node,
1851 .validate_struct_init_result_ty = .un_node,
1852 .validate_ptr_struct_init = .pl_node,
1853 .struct_init_field_type = .pl_node,
1854 .struct_init_field_ptr = .pl_node,
1855 .array_init_anon = .pl_node,
1856 .array_init = .pl_node,
1857 .array_init_ref = .pl_node,
1858 .validate_array_init_ty = .pl_node,
1859 .validate_array_init_result_ty = .pl_node,
1860 .validate_array_init_ref_ty = .pl_node,
1861 .validate_ptr_array_init = .pl_node,
1862 .array_init_elem_type = .bin,
1863 .array_init_elem_ptr = .pl_node,
1864
1865 .extended = .extended,
1866 });
1867 };
1868
1869 // Uncomment to view how many tag slots are available.
1870 //comptime {
1871 // @compileLog("ZIR tags left: ", 256 - @typeInfo(Tag).Enum.fields.len);
1872 //}
1873 };
1874
1875 /// Rarer instructions are here; ones that do not fit in the 8-bit `Tag` enum.
1876 /// `noreturn` instructions may not go here; they must be part of the main `Tag` enum.
1877 pub const Extended = enum(u16) {
1878 /// Declares a global variable.
1879 /// `operand` is payload index to `ExtendedVar`.
1880 /// `small` is `ExtendedVar.Small`.
1881 variable,
1882 /// A struct type definition. Contains references to ZIR instructions for
1883 /// the field types, defaults, and alignments.
1884 /// `operand` is payload index to `StructDecl`.
1885 /// `small` is `StructDecl.Small`.
1886 struct_decl,
1887 /// An enum type definition. Contains references to ZIR instructions for
1888 /// the field value expressions and optional type tag expression.
1889 /// `operand` is payload index to `EnumDecl`.
1890 /// `small` is `EnumDecl.Small`.
1891 enum_decl,
1892 /// A union type definition. Contains references to ZIR instructions for
1893 /// the field types and optional type tag expression.
1894 /// `operand` is payload index to `UnionDecl`.
1895 /// `small` is `UnionDecl.Small`.
1896 union_decl,
1897 /// An opaque type definition. Contains references to decls and captures.
1898 /// `operand` is payload index to `OpaqueDecl`.
1899 /// `small` is `OpaqueDecl.Small`.
1900 opaque_decl,
1901 /// Implements the `@This` builtin.
1902 /// `operand` is `src_node: i32`.
1903 this,
1904 /// Implements the `@returnAddress` builtin.
1905 /// `operand` is `src_node: i32`.
1906 ret_addr,
1907 /// Implements the `@src` builtin.
1908 /// `operand` is payload index to `LineColumn`.
1909 builtin_src,
1910 /// Implements the `@errorReturnTrace` builtin.
1911 /// `operand` is `src_node: i32`.
1912 error_return_trace,
1913 /// Implements the `@frame` builtin.
1914 /// `operand` is `src_node: i32`.
1915 frame,
1916 /// Implements the `@frameAddress` builtin.
1917 /// `operand` is `src_node: i32`.
1918 frame_address,
1919 /// Same as `alloc` from `Tag` but may contain an alignment instruction.
1920 /// `operand` is payload index to `AllocExtended`.
1921 /// `small`:
1922 /// * 0b000X - has type
1923 /// * 0b00X0 - has alignment
1924 /// * 0b0X00 - 1=const, 0=var
1925 /// * 0bX000 - is comptime
1926 alloc,
1927 /// The `@extern` builtin.
1928 /// `operand` is payload index to `BinNode`.
1929 builtin_extern,
1930 /// Inline assembly.
1931 /// `small`:
1932 /// * 0b00000000_000XXXXX - `outputs_len`.
1933 /// * 0b000000XX_XXX00000 - `inputs_len`.
1934 /// * 0b0XXXXX00_00000000 - `clobbers_len`.
1935 /// * 0bX0000000_00000000 - is volatile
1936 /// `operand` is payload index to `Asm`.
1937 @"asm",
1938 /// Same as `asm` except the assembly template is not a string literal but a comptime
1939 /// expression.
1940 /// The `asm_source` field of the Asm is not a null-terminated string
1941 /// but instead a Ref.
1942 asm_expr,
1943 /// Log compile time variables and emit an error message.
1944 /// `operand` is payload index to `NodeMultiOp`.
1945 /// `small` is `operands_len`.
1946 /// The AST node is the compile log builtin call.
1947 compile_log,
1948 /// The builtin `@TypeOf` which returns the type after Peer Type Resolution
1949 /// of one or more params.
1950 /// `operand` is payload index to `TypeOfPeer`.
1951 /// `small` is `operands_len`.
1952 /// The AST node is the builtin call.
1953 typeof_peer,
1954 /// Implements the `@min` builtin for more than 2 args.
1955 /// `operand` is payload index to `NodeMultiOp`.
1956 /// `small` is `operands_len`.
1957 /// The AST node is the builtin call.
1958 min_multi,
1959 /// Implements the `@max` builtin for more than 2 args.
1960 /// `operand` is payload index to `NodeMultiOp`.
1961 /// `small` is `operands_len`.
1962 /// The AST node is the builtin call.
1963 max_multi,
1964 /// Implements the `@addWithOverflow` builtin.
1965 /// `operand` is payload index to `BinNode`.
1966 /// `small` is unused.
1967 add_with_overflow,
1968 /// Implements the `@subWithOverflow` builtin.
1969 /// `operand` is payload index to `BinNode`.
1970 /// `small` is unused.
1971 sub_with_overflow,
1972 /// Implements the `@mulWithOverflow` builtin.
1973 /// `operand` is payload index to `BinNode`.
1974 /// `small` is unused.
1975 mul_with_overflow,
1976 /// Implements the `@shlWithOverflow` builtin.
1977 /// `operand` is payload index to `BinNode`.
1978 /// `small` is unused.
1979 shl_with_overflow,
1980 /// `operand` is payload index to `UnNode`.
1981 c_undef,
1982 /// `operand` is payload index to `UnNode`.
1983 c_include,
1984 /// `operand` is payload index to `BinNode`.
1985 c_define,
1986 /// `operand` is payload index to `UnNode`.
1987 wasm_memory_size,
1988 /// `operand` is payload index to `BinNode`.
1989 wasm_memory_grow,
1990 /// The `@prefetch` builtin.
1991 /// `operand` is payload index to `BinNode`.
1992 prefetch,
1993 /// Implements the `@fence` builtin.
1994 /// `operand` is payload index to `UnNode`.
1995 fence,
1996 /// Implement builtin `@setFloatMode`.
1997 /// `operand` is payload index to `UnNode`.
1998 set_float_mode,
1999 /// Implement builtin `@setAlignStack`.
2000 /// `operand` is payload index to `UnNode`.
2001 set_align_stack,
2002 /// Implements `@setCold`.
2003 /// `operand` is payload index to `UnNode`.
2004 set_cold,
2005 /// Implements the `@errorCast` builtin.
2006 /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand.
2007 error_cast,
2008 /// `operand` is payload index to `UnNode`.
2009 await_nosuspend,
2010 /// Implements `@breakpoint`.
2011 /// `operand` is `src_node: i32`.
2012 breakpoint,
2013 /// Implements the `@select` builtin.
2014 /// `operand` is payload index to `Select`.
2015 select,
2016 /// Implement builtin `@errToInt`.
2017 /// `operand` is payload index to `UnNode`.
2018 int_from_error,
2019 /// Implement builtin `@errorFromInt`.
2020 /// `operand` is payload index to `UnNode`.
2021 error_from_int,
2022 /// Implement builtin `@Type`.
2023 /// `operand` is payload index to `UnNode`.
2024 /// `small` contains `NameStrategy`.
2025 reify,
2026 /// Implements the `@asyncCall` builtin.
2027 /// `operand` is payload index to `AsyncCall`.
2028 builtin_async_call,
2029 /// Implements the `@cmpxchgStrong` and `@cmpxchgWeak` builtins.
2030 /// `small` 0=>weak 1=>strong
2031 /// `operand` is payload index to `Cmpxchg`.
2032 cmpxchg,
2033 /// Implement builtin `@cVaArg`.
2034 /// `operand` is payload index to `BinNode`.
2035 c_va_arg,
2036 /// Implement builtin `@cVaCopy`.
2037 /// `operand` is payload index to `UnNode`.
2038 c_va_copy,
2039 /// Implement builtin `@cVaEnd`.
2040 /// `operand` is payload index to `UnNode`.
2041 c_va_end,
2042 /// Implement builtin `@cVaStart`.
2043 /// `operand` is `src_node: i32`.
2044 c_va_start,
2045 /// Implements the following builtins:
2046 /// `@ptrCast`, `@alignCast`, `@addrSpaceCast`, `@constCast`, `@volatileCast`.
2047 /// Represents an arbitrary nesting of the above builtins. Such a nesting is treated as a
2048 /// single operation which can modify multiple components of a pointer type.
2049 /// `operand` is payload index to `BinNode`.
2050 /// `small` contains `FullPtrCastFlags`.
2051 /// AST node is the root of the nested casts.
2052 /// `lhs` is dest type, `rhs` is operand.
2053 ptr_cast_full,
2054 /// `operand` is payload index to `UnNode`.
2055 /// `small` contains `FullPtrCastFlags`.
2056 /// Guaranteed to only have flags where no explicit destination type is
2057 /// required (const_cast and volatile_cast).
2058 /// AST node is the root of the nested casts.
2059 ptr_cast_no_dest,
2060 /// Implements the `@workItemId` builtin.
2061 /// `operand` is payload index to `UnNode`.
2062 work_item_id,
2063 /// Implements the `@workGroupSize` builtin.
2064 /// `operand` is payload index to `UnNode`.
2065 work_group_size,
2066 /// Implements the `@workGroupId` builtin.
2067 /// `operand` is payload index to `UnNode`.
2068 work_group_id,
2069 /// Implements the `@inComptime` builtin.
2070 /// `operand` is `src_node: i32`.
2071 in_comptime,
2072 /// Restores the error return index to its last saved state in a given
2073 /// block. If the block is `.none`, restores to the state from the point
2074 /// of function entry. If the operand is not `.none`, the restore is
2075 /// conditional on the operand value not being an error.
2076 /// `operand` is payload index to `RestoreErrRetIndex`.
2077 /// `small` is undefined.
2078 restore_err_ret_index,
2079 /// Used as a placeholder instruction which is just a dummy index for Sema to replace
2080 /// with a specific value. For instance, this is used for the capture of an `errdefer`.
2081 /// This should never appear in a body.
2082 value_placeholder,
2083
2084 pub const InstData = struct {
2085 opcode: Extended,
2086 small: u16,
2087 operand: u32,
2088 };
2089 };
2090
2091 /// The position of a ZIR instruction within the `Zir` instructions array.
2092 pub const Index = enum(u32) {
2093 /// ZIR is structured so that the outermost "main" struct of any file
2094 /// is always at index 0.
2095 main_struct_inst = 0,
2096 ref_start_index = InternPool.static_len,
2097 _,
2098
2099 pub fn toRef(i: Index) Inst.Ref {
2100 return @enumFromInt(@intFromEnum(Index.ref_start_index) + @intFromEnum(i));
2101 }
2102
2103 pub fn toOptional(i: Index) OptionalIndex {
2104 return @enumFromInt(@intFromEnum(i));
2105 }
2106 };
2107
2108 pub const OptionalIndex = enum(u32) {
2109 /// ZIR is structured so that the outermost "main" struct of any file
2110 /// is always at index 0.
2111 main_struct_inst = 0,
2112 ref_start_index = InternPool.static_len,
2113 none = std.math.maxInt(u32),
2114 _,
2115
2116 pub fn unwrap(oi: OptionalIndex) ?Index {
2117 return if (oi == .none) null else @enumFromInt(@intFromEnum(oi));
2118 }
2119 };
2120
2121 /// A reference to ZIR instruction, or to an InternPool index, or neither.
2122 ///
2123 /// If the integer tag value is < InternPool.static_len, then it
2124 /// corresponds to an InternPool index. Otherwise, this refers to a ZIR
2125 /// instruction.
2126 ///
2127 /// The tag type is specified so that it is safe to bitcast between `[]u32`
2128 /// and `[]Ref`.
2129 pub const Ref = enum(u32) {
2130 u0_type = @intFromEnum(InternPool.Index.u0_type),
2131 i0_type = @intFromEnum(InternPool.Index.i0_type),
2132 u1_type = @intFromEnum(InternPool.Index.u1_type),
2133 u8_type = @intFromEnum(InternPool.Index.u8_type),
2134 i8_type = @intFromEnum(InternPool.Index.i8_type),
2135 u16_type = @intFromEnum(InternPool.Index.u16_type),
2136 i16_type = @intFromEnum(InternPool.Index.i16_type),
2137 u29_type = @intFromEnum(InternPool.Index.u29_type),
2138 u32_type = @intFromEnum(InternPool.Index.u32_type),
2139 i32_type = @intFromEnum(InternPool.Index.i32_type),
2140 u64_type = @intFromEnum(InternPool.Index.u64_type),
2141 i64_type = @intFromEnum(InternPool.Index.i64_type),
2142 u80_type = @intFromEnum(InternPool.Index.u80_type),
2143 u128_type = @intFromEnum(InternPool.Index.u128_type),
2144 i128_type = @intFromEnum(InternPool.Index.i128_type),
2145 usize_type = @intFromEnum(InternPool.Index.usize_type),
2146 isize_type = @intFromEnum(InternPool.Index.isize_type),
2147 c_char_type = @intFromEnum(InternPool.Index.c_char_type),
2148 c_short_type = @intFromEnum(InternPool.Index.c_short_type),
2149 c_ushort_type = @intFromEnum(InternPool.Index.c_ushort_type),
2150 c_int_type = @intFromEnum(InternPool.Index.c_int_type),
2151 c_uint_type = @intFromEnum(InternPool.Index.c_uint_type),
2152 c_long_type = @intFromEnum(InternPool.Index.c_long_type),
2153 c_ulong_type = @intFromEnum(InternPool.Index.c_ulong_type),
2154 c_longlong_type = @intFromEnum(InternPool.Index.c_longlong_type),
2155 c_ulonglong_type = @intFromEnum(InternPool.Index.c_ulonglong_type),
2156 c_longdouble_type = @intFromEnum(InternPool.Index.c_longdouble_type),
2157 f16_type = @intFromEnum(InternPool.Index.f16_type),
2158 f32_type = @intFromEnum(InternPool.Index.f32_type),
2159 f64_type = @intFromEnum(InternPool.Index.f64_type),
2160 f80_type = @intFromEnum(InternPool.Index.f80_type),
2161 f128_type = @intFromEnum(InternPool.Index.f128_type),
2162 anyopaque_type = @intFromEnum(InternPool.Index.anyopaque_type),
2163 bool_type = @intFromEnum(InternPool.Index.bool_type),
2164 void_type = @intFromEnum(InternPool.Index.void_type),
2165 type_type = @intFromEnum(InternPool.Index.type_type),
2166 anyerror_type = @intFromEnum(InternPool.Index.anyerror_type),
2167 comptime_int_type = @intFromEnum(InternPool.Index.comptime_int_type),
2168 comptime_float_type = @intFromEnum(InternPool.Index.comptime_float_type),
2169 noreturn_type = @intFromEnum(InternPool.Index.noreturn_type),
2170 anyframe_type = @intFromEnum(InternPool.Index.anyframe_type),
2171 null_type = @intFromEnum(InternPool.Index.null_type),
2172 undefined_type = @intFromEnum(InternPool.Index.undefined_type),
2173 enum_literal_type = @intFromEnum(InternPool.Index.enum_literal_type),
2174 atomic_order_type = @intFromEnum(InternPool.Index.atomic_order_type),
2175 atomic_rmw_op_type = @intFromEnum(InternPool.Index.atomic_rmw_op_type),
2176 calling_convention_type = @intFromEnum(InternPool.Index.calling_convention_type),
2177 address_space_type = @intFromEnum(InternPool.Index.address_space_type),
2178 float_mode_type = @intFromEnum(InternPool.Index.float_mode_type),
2179 reduce_op_type = @intFromEnum(InternPool.Index.reduce_op_type),
2180 call_modifier_type = @intFromEnum(InternPool.Index.call_modifier_type),
2181 prefetch_options_type = @intFromEnum(InternPool.Index.prefetch_options_type),
2182 export_options_type = @intFromEnum(InternPool.Index.export_options_type),
2183 extern_options_type = @intFromEnum(InternPool.Index.extern_options_type),
2184 type_info_type = @intFromEnum(InternPool.Index.type_info_type),
2185 manyptr_u8_type = @intFromEnum(InternPool.Index.manyptr_u8_type),
2186 manyptr_const_u8_type = @intFromEnum(InternPool.Index.manyptr_const_u8_type),
2187 manyptr_const_u8_sentinel_0_type = @intFromEnum(InternPool.Index.manyptr_const_u8_sentinel_0_type),
2188 single_const_pointer_to_comptime_int_type = @intFromEnum(InternPool.Index.single_const_pointer_to_comptime_int_type),
2189 slice_const_u8_type = @intFromEnum(InternPool.Index.slice_const_u8_type),
2190 slice_const_u8_sentinel_0_type = @intFromEnum(InternPool.Index.slice_const_u8_sentinel_0_type),
2191 optional_noreturn_type = @intFromEnum(InternPool.Index.optional_noreturn_type),
2192 anyerror_void_error_union_type = @intFromEnum(InternPool.Index.anyerror_void_error_union_type),
2193 adhoc_inferred_error_set_type = @intFromEnum(InternPool.Index.adhoc_inferred_error_set_type),
2194 generic_poison_type = @intFromEnum(InternPool.Index.generic_poison_type),
2195 empty_struct_type = @intFromEnum(InternPool.Index.empty_struct_type),
2196 undef = @intFromEnum(InternPool.Index.undef),
2197 zero = @intFromEnum(InternPool.Index.zero),
2198 zero_usize = @intFromEnum(InternPool.Index.zero_usize),
2199 zero_u8 = @intFromEnum(InternPool.Index.zero_u8),
2200 one = @intFromEnum(InternPool.Index.one),
2201 one_usize = @intFromEnum(InternPool.Index.one_usize),
2202 one_u8 = @intFromEnum(InternPool.Index.one_u8),
2203 four_u8 = @intFromEnum(InternPool.Index.four_u8),
2204 negative_one = @intFromEnum(InternPool.Index.negative_one),
2205 calling_convention_c = @intFromEnum(InternPool.Index.calling_convention_c),
2206 calling_convention_inline = @intFromEnum(InternPool.Index.calling_convention_inline),
2207 void_value = @intFromEnum(InternPool.Index.void_value),
2208 unreachable_value = @intFromEnum(InternPool.Index.unreachable_value),
2209 null_value = @intFromEnum(InternPool.Index.null_value),
2210 bool_true = @intFromEnum(InternPool.Index.bool_true),
2211 bool_false = @intFromEnum(InternPool.Index.bool_false),
2212 empty_struct = @intFromEnum(InternPool.Index.empty_struct),
2213 generic_poison = @intFromEnum(InternPool.Index.generic_poison),
2214
2215 /// This tag is here to match Air and InternPool, however it is unused
2216 /// for ZIR purposes.
2217 var_args_param_type = @intFromEnum(InternPool.Index.var_args_param_type),
2218 /// This Ref does not correspond to any ZIR instruction or constant
2219 /// value and may instead be used as a sentinel to indicate null.
2220 none = @intFromEnum(InternPool.Index.none),
2221 _,
2222
2223 pub fn toIndex(inst: Ref) ?Index {
2224 assert(inst != .none);
2225 const ref_int = @intFromEnum(inst);
2226 if (ref_int >= @intFromEnum(Index.ref_start_index)) {
2227 return @enumFromInt(ref_int - @intFromEnum(Index.ref_start_index));
2228 } else {
2229 return null;
2230 }
2231 }
2232
2233 pub fn toIndexAllowNone(inst: Ref) ?Index {
2234 if (inst == .none) return null;
2235 return toIndex(inst);
2236 }
2237 };
2238
2239 /// All instructions have an 8-byte payload, which is contained within
2240 /// this union. `Tag` determines which union field is active, as well as
2241 /// how to interpret the data within.
2242 pub const Data = union {
2243 /// Used for `Tag.extended`. The extended opcode determines the meaning
2244 /// of the `small` and `operand` fields.
2245 extended: Extended.InstData,
2246 /// Used for unary operators, with an AST node source location.
2247 un_node: struct {
2248 /// Offset from Decl AST node index.
2249 src_node: i32,
2250 /// The meaning of this operand depends on the corresponding `Tag`.
2251 operand: Ref,
2252
2253 pub fn src(self: @This()) LazySrcLoc {
2254 return LazySrcLoc.nodeOffset(self.src_node);
2255 }
2256 },
2257 /// Used for unary operators, with a token source location.
2258 un_tok: struct {
2259 /// Offset from Decl AST token index.
2260 src_tok: Ast.TokenIndex,
2261 /// The meaning of this operand depends on the corresponding `Tag`.
2262 operand: Ref,
2263
2264 pub fn src(self: @This()) LazySrcLoc {
2265 return .{ .token_offset = self.src_tok };
2266 }
2267 },
2268 pl_node: struct {
2269 /// Offset from Decl AST node index.
2270 /// `Tag` determines which kind of AST node this points to.
2271 src_node: i32,
2272 /// index into extra.
2273 /// `Tag` determines what lives there.
2274 payload_index: u32,
2275
2276 pub fn src(self: @This()) LazySrcLoc {
2277 return LazySrcLoc.nodeOffset(self.src_node);
2278 }
2279 },
2280 pl_tok: struct {
2281 /// Offset from Decl AST token index.
2282 src_tok: Ast.TokenIndex,
2283 /// index into extra.
2284 /// `Tag` determines what lives there.
2285 payload_index: u32,
2286
2287 pub fn src(self: @This()) LazySrcLoc {
2288 return .{ .token_offset = self.src_tok };
2289 }
2290 },
2291 bin: Bin,
2292 /// For strings which may contain null bytes.
2293 str: struct {
2294 /// Offset into `string_bytes`.
2295 start: NullTerminatedString,
2296 /// Number of bytes in the string.
2297 len: u32,
2298
2299 pub fn get(self: @This(), code: Zir) []const u8 {
2300 return code.string_bytes[@intFromEnum(self.start)..][0..self.len];
2301 }
2302 },
2303 str_tok: struct {
2304 /// Offset into `string_bytes`. Null-terminated.
2305 start: NullTerminatedString,
2306 /// Offset from Decl AST token index.
2307 src_tok: u32,
2308
2309 pub fn get(self: @This(), code: Zir) [:0]const u8 {
2310 return code.nullTerminatedString(self.start);
2311 }
2312
2313 pub fn src(self: @This()) LazySrcLoc {
2314 return .{ .token_offset = self.src_tok };
2315 }
2316 },
2317 /// Offset from Decl AST token index.
2318 tok: Ast.TokenIndex,
2319 /// Offset from Decl AST node index.
2320 node: i32,
2321 int: u64,
2322 float: f64,
2323 ptr_type: struct {
2324 flags: packed struct {
2325 is_allowzero: bool,
2326 is_mutable: bool,
2327 is_volatile: bool,
2328 has_sentinel: bool,
2329 has_align: bool,
2330 has_addrspace: bool,
2331 has_bit_range: bool,
2332 _: u1 = undefined,
2333 },
2334 size: std.builtin.Type.Pointer.Size,
2335 /// Index into extra. See `PtrType`.
2336 payload_index: u32,
2337 },
2338 int_type: struct {
2339 /// Offset from Decl AST node index.
2340 /// `Tag` determines which kind of AST node this points to.
2341 src_node: i32,
2342 signedness: std.builtin.Signedness,
2343 bit_count: u16,
2344
2345 pub fn src(self: @This()) LazySrcLoc {
2346 return LazySrcLoc.nodeOffset(self.src_node);
2347 }
2348 },
2349 @"unreachable": struct {
2350 /// Offset from Decl AST node index.
2351 /// `Tag` determines which kind of AST node this points to.
2352 src_node: i32,
2353
2354 pub fn src(self: @This()) LazySrcLoc {
2355 return LazySrcLoc.nodeOffset(self.src_node);
2356 }
2357 },
2358 @"break": struct {
2359 operand: Ref,
2360 payload_index: u32,
2361 },
2362 dbg_stmt: LineColumn,
2363 /// Used for unary operators which reference an inst,
2364 /// with an AST node source location.
2365 inst_node: struct {
2366 /// Offset from Decl AST node index.
2367 src_node: i32,
2368 /// The meaning of this operand depends on the corresponding `Tag`.
2369 inst: Index,
2370
2371 pub fn src(self: @This()) LazySrcLoc {
2372 return LazySrcLoc.nodeOffset(self.src_node);
2373 }
2374 },
2375 str_op: struct {
2376 /// Offset into `string_bytes`. Null-terminated.
2377 str: NullTerminatedString,
2378 operand: Ref,
2379
2380 pub fn getStr(self: @This(), zir: Zir) [:0]const u8 {
2381 return zir.nullTerminatedString(self.str);
2382 }
2383 },
2384 @"defer": struct {
2385 index: u32,
2386 len: u32,
2387 },
2388 defer_err_code: struct {
2389 err_code: Ref,
2390 payload_index: u32,
2391 },
2392 save_err_ret_index: struct {
2393 operand: Ref, // If error type (or .none), save new trace index
2394 },
2395 elem_val_imm: struct {
2396 /// The indexable value being accessed.
2397 operand: Ref,
2398 /// The index being accessed.
2399 idx: u32,
2400 },
2401
2402 // Make sure we don't accidentally add a field to make this union
2403 // bigger than expected. Note that in Debug builds, Zig is allowed
2404 // to insert a secret field for safety checks.
2405 comptime {
2406 if (builtin.mode != .Debug and builtin.mode != .ReleaseSafe) {
2407 assert(@sizeOf(Data) == 8);
2408 }
2409 }
2410
2411 /// TODO this has to be kept in sync with `Data` which we want to be an untagged
2412 /// union. There is some kind of language awkwardness here and it has to do with
2413 /// deserializing an untagged union (in this case `Data`) from a file, and trying
2414 /// to preserve the hidden safety field.
2415 pub const FieldEnum = enum {
2416 extended,
2417 un_node,
2418 un_tok,
2419 pl_node,
2420 pl_tok,
2421 bin,
2422 str,
2423 str_tok,
2424 tok,
2425 node,
2426 int,
2427 float,
2428 ptr_type,
2429 int_type,
2430 @"unreachable",
2431 @"break",
2432 dbg_stmt,
2433 inst_node,
2434 str_op,
2435 @"defer",
2436 defer_err_code,
2437 save_err_ret_index,
2438 elem_val_imm,
2439 };
2440 };
2441
2442 pub const Break = struct {
2443 pub const no_src_node = std.math.maxInt(i32);
2444
2445 operand_src_node: i32,
2446 block_inst: Index,
2447 };
2448
2449 /// Trailing:
2450 /// 0. Output for every outputs_len
2451 /// 1. Input for every inputs_len
2452 /// 2. clobber: NullTerminatedString // index into string_bytes (null terminated) for every clobbers_len.
2453 pub const Asm = struct {
2454 src_node: i32,
2455 // null-terminated string index
2456 asm_source: NullTerminatedString,
2457 /// 1 bit for each outputs_len: whether it uses `-> T` or not.
2458 /// 0b0 - operand is a pointer to where to store the output.
2459 /// 0b1 - operand is a type; asm expression has the output as the result.
2460 /// 0b0X is the first output, 0bX0 is the second, etc.
2461 output_type_bits: u32,
2462
2463 pub const Output = struct {
2464 /// index into string_bytes (null terminated)
2465 name: NullTerminatedString,
2466 /// index into string_bytes (null terminated)
2467 constraint: NullTerminatedString,
2468 /// How to interpret this is determined by `output_type_bits`.
2469 operand: Ref,
2470 };
2471
2472 pub const Input = struct {
2473 /// index into string_bytes (null terminated)
2474 name: NullTerminatedString,
2475 /// index into string_bytes (null terminated)
2476 constraint: NullTerminatedString,
2477 operand: Ref,
2478 };
2479 };
2480
2481 /// Trailing:
2482 /// if (ret_body_len == 1) {
2483 /// 0. return_type: Ref
2484 /// }
2485 /// if (ret_body_len > 1) {
2486 /// 1. return_type: Index // for each ret_body_len
2487 /// }
2488 /// 2. body: Index // for each body_len
2489 /// 3. src_locs: SrcLocs // if body_len != 0
2490 /// 4. proto_hash: std.zig.SrcHash // if body_len != 0; hash of function prototype
2491 pub const Func = struct {
2492 /// If this is 0 it means a void return type.
2493 /// If this is 1 it means return_type is a simple Ref
2494 ret_body_len: u32,
2495 /// Points to the block that contains the param instructions for this function.
2496 /// If this is a `declaration`, it refers to the declaration's value body.
2497 param_block: Index,
2498 body_len: u32,
2499
2500 pub const SrcLocs = struct {
2501 /// Line index in the source file relative to the parent decl.
2502 lbrace_line: u32,
2503 /// Line index in the source file relative to the parent decl.
2504 rbrace_line: u32,
2505 /// lbrace_column is least significant bits u16
2506 /// rbrace_column is most significant bits u16
2507 columns: u32,
2508 };
2509 };
2510
2511 /// Trailing:
2512 /// 0. lib_name: NullTerminatedString, // null terminated string index, if has_lib_name is set
2513 /// if (has_align_ref and !has_align_body) {
2514 /// 1. align: Ref,
2515 /// }
2516 /// if (has_align_body) {
2517 /// 2. align_body_len: u32
2518 /// 3. align_body: u32 // for each align_body_len
2519 /// }
2520 /// if (has_addrspace_ref and !has_addrspace_body) {
2521 /// 4. addrspace: Ref,
2522 /// }
2523 /// if (has_addrspace_body) {
2524 /// 5. addrspace_body_len: u32
2525 /// 6. addrspace_body: u32 // for each addrspace_body_len
2526 /// }
2527 /// if (has_section_ref and !has_section_body) {
2528 /// 7. section: Ref,
2529 /// }
2530 /// if (has_section_body) {
2531 /// 8. section_body_len: u32
2532 /// 9. section_body: u32 // for each section_body_len
2533 /// }
2534 /// if (has_cc_ref and !has_cc_body) {
2535 /// 10. cc: Ref,
2536 /// }
2537 /// if (has_cc_body) {
2538 /// 11. cc_body_len: u32
2539 /// 12. cc_body: u32 // for each cc_body_len
2540 /// }
2541 /// if (has_ret_ty_ref and !has_ret_ty_body) {
2542 /// 13. ret_ty: Ref,
2543 /// }
2544 /// if (has_ret_ty_body) {
2545 /// 14. ret_ty_body_len: u32
2546 /// 15. ret_ty_body: u32 // for each ret_ty_body_len
2547 /// }
2548 /// 16. noalias_bits: u32 // if has_any_noalias
2549 /// - each bit starting with LSB corresponds to parameter indexes
2550 /// 17. body: Index // for each body_len
2551 /// 18. src_locs: Func.SrcLocs // if body_len != 0
2552 /// 19. proto_hash: std.zig.SrcHash // if body_len != 0; hash of function prototype
2553 pub const FuncFancy = struct {
2554 /// Points to the block that contains the param instructions for this function.
2555 /// If this is a `declaration`, it refers to the declaration's value body.
2556 param_block: Index,
2557 body_len: u32,
2558 bits: Bits,
2559
2560 /// If both has_cc_ref and has_cc_body are false, it means auto calling convention.
2561 /// If both has_align_ref and has_align_body are false, it means default alignment.
2562 /// If both has_ret_ty_ref and has_ret_ty_body are false, it means void return type.
2563 /// If both has_section_ref and has_section_body are false, it means default section.
2564 /// If both has_addrspace_ref and has_addrspace_body are false, it means default addrspace.
2565 pub const Bits = packed struct {
2566 is_var_args: bool,
2567 is_inferred_error: bool,
2568 is_test: bool,
2569 is_extern: bool,
2570 is_noinline: bool,
2571 has_align_ref: bool,
2572 has_align_body: bool,
2573 has_addrspace_ref: bool,
2574 has_addrspace_body: bool,
2575 has_section_ref: bool,
2576 has_section_body: bool,
2577 has_cc_ref: bool,
2578 has_cc_body: bool,
2579 has_ret_ty_ref: bool,
2580 has_ret_ty_body: bool,
2581 has_lib_name: bool,
2582 has_any_noalias: bool,
2583 _: u15 = undefined,
2584 };
2585 };
2586
2587 /// Trailing:
2588 /// 0. lib_name: NullTerminatedString, // null terminated string index, if has_lib_name is set
2589 /// 1. align: Ref, // if has_align is set
2590 /// 2. init: Ref // if has_init is set
2591 /// The source node is obtained from the containing `block_inline`.
2592 pub const ExtendedVar = struct {
2593 var_type: Ref,
2594
2595 pub const Small = packed struct {
2596 has_lib_name: bool,
2597 has_align: bool,
2598 has_init: bool,
2599 is_extern: bool,
2600 is_const: bool,
2601 is_threadlocal: bool,
2602 _: u10 = undefined,
2603 };
2604 };
2605
2606 /// This data is stored inside extra, with trailing operands according to `operands_len`.
2607 /// Each operand is a `Ref`.
2608 pub const MultiOp = struct {
2609 operands_len: u32,
2610 };
2611
2612 /// Trailing: operand: Ref, // for each `operands_len` (stored in `small`).
2613 pub const NodeMultiOp = struct {
2614 src_node: i32,
2615 };
2616
2617 /// This data is stored inside extra, with trailing operands according to `body_len`.
2618 /// Each operand is an `Index`.
2619 pub const Block = struct {
2620 body_len: u32,
2621 };
2622
2623 /// Trailing:
2624 /// * inst: Index // for each `body_len`
2625 pub const BoolBr = struct {
2626 lhs: Ref,
2627 body_len: u32,
2628 };
2629
2630 /// Trailing:
2631 /// 0. doc_comment: u32 // if `has_doc_comment`; null-terminated string index
2632 /// 1. align_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `align`
2633 /// 2. linksection_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `linksection`
2634 /// 3. addrspace_body_len: u32 // if `has_align_linksection_addrspace`; 0 means no `addrspace`
2635 /// 4. value_body_inst: Zir.Inst.Index
2636 /// - for each `value_body_len`
2637 /// - body to be exited via `break_inline` to this `declaration` instruction
2638 /// 5. align_body_inst: Zir.Inst.Index
2639 /// - for each `align_body_len`
2640 /// - body to be exited via `break_inline` to this `declaration` instruction
2641 /// 6. linksection_body_inst: Zir.Inst.Index
2642 /// - for each `linksection_body_len`
2643 /// - body to be exited via `break_inline` to this `declaration` instruction
2644 /// 7. addrspace_body_inst: Zir.Inst.Index
2645 /// - for each `addrspace_body_len`
2646 /// - body to be exited via `break_inline` to this `declaration` instruction
2647 pub const Declaration = struct {
2648 // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`.
2649 src_hash_0: u32,
2650 src_hash_1: u32,
2651 src_hash_2: u32,
2652 src_hash_3: u32,
2653 /// The name of this `Decl`. Also indicates whether it is a test, comptime block, etc.
2654 name: Name,
2655 /// This Decl's line number relative to that of its parent.
2656 /// TODO: column must be encoded similarly to respect non-formatted code!
2657 line_offset: u32,
2658 flags: Flags,
2659
2660 pub const Flags = packed struct(u32) {
2661 value_body_len: u28,
2662 is_pub: bool,
2663 is_export: bool,
2664 has_doc_comment: bool,
2665 has_align_linksection_addrspace: bool,
2666 };
2667
2668 pub const Name = enum(u32) {
2669 @"comptime" = std.math.maxInt(u32),
2670 @"usingnamespace" = std.math.maxInt(u32) - 1,
2671 unnamed_test = std.math.maxInt(u32) - 2,
2672 /// In this case, `has_doc_comment` will be true, and the doc
2673 /// comment body is the identifier name.
2674 decltest = std.math.maxInt(u32) - 3,
2675 /// Other values are `NullTerminatedString` values, i.e. index into
2676 /// `string_bytes`. If the byte referenced is 0, the decl is a named
2677 /// test, and the actual name begins at the following byte.
2678 _,
2679
2680 pub fn isNamedTest(name: Name, zir: Zir) bool {
2681 return switch (name) {
2682 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => false,
2683 _ => zir.string_bytes[@intFromEnum(name)] == 0,
2684 };
2685 }
2686 pub fn toString(name: Name, zir: Zir) ?NullTerminatedString {
2687 switch (name) {
2688 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => return null,
2689 _ => {},
2690 }
2691 const idx: u32 = @intFromEnum(name);
2692 if (zir.string_bytes[idx] == 0) {
2693 // Named test
2694 return @enumFromInt(idx + 1);
2695 }
2696 return @enumFromInt(idx);
2697 }
2698 };
2699
2700 pub const Bodies = struct {
2701 value_body: []const Index,
2702 align_body: ?[]const Index,
2703 linksection_body: ?[]const Index,
2704 addrspace_body: ?[]const Index,
2705 };
2706
2707 pub fn getBodies(declaration: Declaration, extra_end: u32, zir: Zir) Bodies {
2708 var extra_index: u32 = extra_end;
2709 extra_index += @intFromBool(declaration.flags.has_doc_comment);
2710 const value_body_len = declaration.flags.value_body_len;
2711 const align_body_len, const linksection_body_len, const addrspace_body_len = lens: {
2712 if (!declaration.flags.has_align_linksection_addrspace) {
2713 break :lens .{ 0, 0, 0 };
2714 }
2715 const lens = zir.extra[extra_index..][0..3].*;
2716 extra_index += 3;
2717 break :lens lens;
2718 };
2719 return .{
2720 .value_body = b: {
2721 defer extra_index += value_body_len;
2722 break :b zir.bodySlice(extra_index, value_body_len);
2723 },
2724 .align_body = if (align_body_len == 0) null else b: {
2725 defer extra_index += align_body_len;
2726 break :b zir.bodySlice(extra_index, align_body_len);
2727 },
2728 .linksection_body = if (linksection_body_len == 0) null else b: {
2729 defer extra_index += linksection_body_len;
2730 break :b zir.bodySlice(extra_index, linksection_body_len);
2731 },
2732 .addrspace_body = if (addrspace_body_len == 0) null else b: {
2733 defer extra_index += addrspace_body_len;
2734 break :b zir.bodySlice(extra_index, addrspace_body_len);
2735 },
2736 };
2737 }
2738 };
2739
2740 /// Stored inside extra, with trailing arguments according to `args_len`.
2741 /// Implicit 0. arg_0_start: u32, // always same as `args_len`
2742 /// 1. arg_end: u32, // for each `args_len`
2743 /// arg_N_start is the same as arg_N-1_end
2744 pub const Call = struct {
2745 // Note: Flags *must* come first so that unusedResultExpr
2746 // can find it when it goes to modify them.
2747 flags: Flags,
2748 callee: Ref,
2749
2750 pub const Flags = packed struct {
2751 /// std.builtin.CallModifier in packed form
2752 pub const PackedModifier = u3;
2753 pub const PackedArgsLen = u27;
2754
2755 packed_modifier: PackedModifier,
2756 ensure_result_used: bool = false,
2757 pop_error_return_trace: bool,
2758 args_len: PackedArgsLen,
2759
2760 comptime {
2761 if (@sizeOf(Flags) != 4 or @bitSizeOf(Flags) != 32)
2762 @compileError("Layout of Call.Flags needs to be updated!");
2763 if (@bitSizeOf(std.builtin.CallModifier) != @bitSizeOf(PackedModifier))
2764 @compileError("Call.Flags.PackedModifier needs to be updated!");
2765 }
2766 };
2767 };
2768
2769 /// Stored inside extra, with trailing arguments according to `args_len`.
2770 /// Implicit 0. arg_0_start: u32, // always same as `args_len`
2771 /// 1. arg_end: u32, // for each `args_len`
2772 /// arg_N_start is the same as arg_N-1_end
2773 pub const FieldCall = struct {
2774 // Note: Flags *must* come first so that unusedResultExpr
2775 // can find it when it goes to modify them.
2776 flags: Call.Flags,
2777 obj_ptr: Ref,
2778 /// Offset into `string_bytes`.
2779 field_name_start: NullTerminatedString,
2780 };
2781
2782 pub const TypeOfPeer = struct {
2783 src_node: i32,
2784 body_len: u32,
2785 body_index: u32,
2786 };
2787
2788 pub const BuiltinCall = struct {
2789 // Note: Flags *must* come first so that unusedResultExpr
2790 // can find it when it goes to modify them.
2791 flags: Flags,
2792 modifier: Ref,
2793 callee: Ref,
2794 args: Ref,
2795
2796 pub const Flags = packed struct {
2797 is_nosuspend: bool,
2798 ensure_result_used: bool,
2799 _: u30 = undefined,
2800
2801 comptime {
2802 if (@sizeOf(Flags) != 4 or @bitSizeOf(Flags) != 32)
2803 @compileError("Layout of BuiltinCall.Flags needs to be updated!");
2804 }
2805 };
2806 };
2807
2808 /// This data is stored inside extra, with two sets of trailing `Ref`:
2809 /// * 0. the then body, according to `then_body_len`.
2810 /// * 1. the else body, according to `else_body_len`.
2811 pub const CondBr = struct {
2812 condition: Ref,
2813 then_body_len: u32,
2814 else_body_len: u32,
2815 };
2816
2817 /// This data is stored inside extra, trailed by:
2818 /// * 0. body: Index // for each `body_len`.
2819 pub const Try = struct {
2820 /// The error union to unwrap.
2821 operand: Ref,
2822 body_len: u32,
2823 };
2824
2825 /// Stored in extra. Depending on the flags in Data, there will be up to 5
2826 /// trailing Ref fields:
2827 /// 0. sentinel: Ref // if `has_sentinel` flag is set
2828 /// 1. align: Ref // if `has_align` flag is set
2829 /// 2. address_space: Ref // if `has_addrspace` flag is set
2830 /// 3. bit_start: Ref // if `has_bit_range` flag is set
2831 /// 4. host_size: Ref // if `has_bit_range` flag is set
2832 pub const PtrType = struct {
2833 elem_type: Ref,
2834 src_node: i32,
2835 };
2836
2837 pub const ArrayTypeSentinel = struct {
2838 len: Ref,
2839 sentinel: Ref,
2840 elem_type: Ref,
2841 };
2842
2843 pub const SliceStart = struct {
2844 lhs: Ref,
2845 start: Ref,
2846 };
2847
2848 pub const SliceEnd = struct {
2849 lhs: Ref,
2850 start: Ref,
2851 end: Ref,
2852 };
2853
2854 pub const SliceSentinel = struct {
2855 lhs: Ref,
2856 start: Ref,
2857 end: Ref,
2858 sentinel: Ref,
2859 };
2860
2861 pub const SliceLength = struct {
2862 lhs: Ref,
2863 start: Ref,
2864 len: Ref,
2865 sentinel: Ref,
2866 start_src_node_offset: i32,
2867 };
2868
2869 /// The meaning of these operands depends on the corresponding `Tag`.
2870 pub const Bin = struct {
2871 lhs: Ref,
2872 rhs: Ref,
2873 };
2874
2875 pub const BinNode = struct {
2876 node: i32,
2877 lhs: Ref,
2878 rhs: Ref,
2879 };
2880
2881 pub const UnNode = struct {
2882 node: i32,
2883 operand: Ref,
2884 };
2885
2886 pub const ElemPtrImm = struct {
2887 ptr: Ref,
2888 index: u32,
2889 };
2890
2891 pub const SwitchBlockErrUnion = struct {
2892 operand: Ref,
2893 bits: Bits,
2894 main_src_node_offset: i32,
2895
2896 pub const Bits = packed struct(u32) {
2897 /// If true, one or more prongs have multiple items.
2898 has_multi_cases: bool,
2899 /// If true, there is an else prong. This is mutually exclusive with `has_under`.
2900 has_else: bool,
2901 any_uses_err_capture: bool,
2902 payload_is_ref: bool,
2903 scalar_cases_len: ScalarCasesLen,
2904
2905 pub const ScalarCasesLen = u28;
2906 };
2907
2908 pub const MultiProng = struct {
2909 items: []const Ref,
2910 body: []const Index,
2911 };
2912 };
2913
2914 /// 0. multi_cases_len: u32 // If has_multi_cases is set.
2915 /// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture.
2916 /// 2. else_body { // If has_else or has_under is set.
2917 /// info: ProngInfo,
2918 /// body member Index for every info.body_len
2919 /// }
2920 /// 3. scalar_cases: { // for every scalar_cases_len
2921 /// item: Ref,
2922 /// info: ProngInfo,
2923 /// body member Index for every info.body_len
2924 /// }
2925 /// 4. multi_cases: { // for every multi_cases_len
2926 /// items_len: u32,
2927 /// ranges_len: u32,
2928 /// info: ProngInfo,
2929 /// item: Ref // for every items_len
2930 /// ranges: { // for every ranges_len
2931 /// item_first: Ref,
2932 /// item_last: Ref,
2933 /// }
2934 /// body member Index for every info.body_len
2935 /// }
2936 ///
2937 /// When analyzing a case body, the switch instruction itself refers to the
2938 /// captured payload. Whether this is captured by reference or by value
2939 /// depends on whether the `byref` bit is set for the corresponding body.
2940 pub const SwitchBlock = struct {
2941 /// The operand passed to the `switch` expression. If this is a
2942 /// `switch_block`, this is the operand value; if `switch_block_ref` it
2943 /// is a pointer to the operand. `switch_block_ref` is always used if
2944 /// any prong has a byref capture.
2945 operand: Ref,
2946 bits: Bits,
2947
2948 /// These are stored in trailing data in `extra` for each prong.
2949 pub const ProngInfo = packed struct(u32) {
2950 body_len: u28,
2951 capture: Capture,
2952 is_inline: bool,
2953 has_tag_capture: bool,
2954
2955 pub const Capture = enum(u2) {
2956 none,
2957 by_val,
2958 by_ref,
2959 };
2960 };
2961
2962 pub const Bits = packed struct(u32) {
2963 /// If true, one or more prongs have multiple items.
2964 has_multi_cases: bool,
2965 /// If true, there is an else prong. This is mutually exclusive with `has_under`.
2966 has_else: bool,
2967 /// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
2968 has_under: bool,
2969 /// If true, at least one prong has an inline tag capture.
2970 any_has_tag_capture: bool,
2971 scalar_cases_len: ScalarCasesLen,
2972
2973 pub const ScalarCasesLen = u28;
2974
2975 pub fn specialProng(bits: Bits) SpecialProng {
2976 const has_else: u2 = @intFromBool(bits.has_else);
2977 const has_under: u2 = @intFromBool(bits.has_under);
2978 return switch ((has_else << 1) | has_under) {
2979 0b00 => .none,
2980 0b01 => .under,
2981 0b10 => .@"else",
2982 0b11 => unreachable,
2983 };
2984 }
2985 };
2986
2987 pub const MultiProng = struct {
2988 items: []const Ref,
2989 body: []const Index,
2990 };
2991 };
2992
2993 pub const ArrayInitRefTy = struct {
2994 ptr_ty: Ref,
2995 elem_count: u32,
2996 };
2997
2998 pub const Field = struct {
2999 lhs: Ref,
3000 /// Offset into `string_bytes`.
3001 field_name_start: NullTerminatedString,
3002 };
3003
3004 pub const FieldNamed = struct {
3005 lhs: Ref,
3006 field_name: Ref,
3007 };
3008
3009 pub const As = struct {
3010 dest_type: Ref,
3011 operand: Ref,
3012 };
3013
3014 /// Trailing:
3015 /// 0. fields_len: u32, // if has_fields_len
3016 /// 1. decls_len: u32, // if has_decls_len
3017 /// 2. backing_int_body_len: u32, // if has_backing_int
3018 /// 3. backing_int_ref: Ref, // if has_backing_int and backing_int_body_len is 0
3019 /// 4. backing_int_body_inst: Inst, // if has_backing_int and backing_int_body_len is > 0
3020 /// 5. decl: Index, // for every decls_len; points to a `declaration` instruction
3021 /// 6. flags: u32 // for every 8 fields
3022 /// - sets of 4 bits:
3023 /// 0b000X: whether corresponding field has an align expression
3024 /// 0b00X0: whether corresponding field has a default expression
3025 /// 0b0X00: whether corresponding field is comptime
3026 /// 0bX000: whether corresponding field has a type expression
3027 /// 7. fields: { // for every fields_len
3028 /// field_name: u32, // if !is_tuple
3029 /// doc_comment: NullTerminatedString, // .empty if no doc comment
3030 /// field_type: Ref, // if corresponding bit is not set. none means anytype.
3031 /// field_type_body_len: u32, // if corresponding bit is set
3032 /// align_body_len: u32, // if corresponding bit is set
3033 /// init_body_len: u32, // if corresponding bit is set
3034 /// }
3035 /// 8. bodies: { // for every fields_len
3036 /// field_type_body_inst: Inst, // for each field_type_body_len
3037 /// align_body_inst: Inst, // for each align_body_len
3038 /// init_body_inst: Inst, // for each init_body_len
3039 /// }
3040 pub const StructDecl = struct {
3041 // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`.
3042 // This hash contains the source of all fields, and any specified attributes (`extern`, backing type, etc).
3043 fields_hash_0: u32,
3044 fields_hash_1: u32,
3045 fields_hash_2: u32,
3046 fields_hash_3: u32,
3047 src_node: i32,
3048
3049 pub fn src(self: StructDecl) LazySrcLoc {
3050 return LazySrcLoc.nodeOffset(self.src_node);
3051 }
3052
3053 pub const Small = packed struct {
3054 has_fields_len: bool,
3055 has_decls_len: bool,
3056 has_backing_int: bool,
3057 known_non_opv: bool,
3058 known_comptime_only: bool,
3059 is_tuple: bool,
3060 name_strategy: NameStrategy,
3061 layout: std.builtin.Type.ContainerLayout,
3062 any_default_inits: bool,
3063 any_comptime_fields: bool,
3064 any_aligned_fields: bool,
3065 _: u3 = undefined,
3066 };
3067 };
3068
3069 pub const NameStrategy = enum(u2) {
3070 /// Use the same name as the parent declaration name.
3071 /// e.g. `const Foo = struct {...};`.
3072 parent,
3073 /// Use the name of the currently executing comptime function call,
3074 /// with the current parameters. e.g. `ArrayList(i32)`.
3075 func,
3076 /// Create an anonymous name for this declaration.
3077 /// Like this: "ParentDeclName_struct_69"
3078 anon,
3079 /// Use the name specified in the next `dbg_var_{val,ptr}` instruction.
3080 dbg_var,
3081 };
3082
3083 pub const FullPtrCastFlags = packed struct(u5) {
3084 ptr_cast: bool = false,
3085 align_cast: bool = false,
3086 addrspace_cast: bool = false,
3087 const_cast: bool = false,
3088 volatile_cast: bool = false,
3089
3090 pub inline fn needResultTypeBuiltinName(flags: FullPtrCastFlags) []const u8 {
3091 if (flags.ptr_cast) return "@ptrCast";
3092 if (flags.align_cast) return "@alignCast";
3093 if (flags.addrspace_cast) return "@addrSpaceCast";
3094 unreachable;
3095 }
3096 };
3097
3098 /// Trailing:
3099 /// 0. tag_type: Ref, // if has_tag_type
3100 /// 1. body_len: u32, // if has_body_len
3101 /// 2. fields_len: u32, // if has_fields_len
3102 /// 3. decls_len: u32, // if has_decls_len
3103 /// 4. decl: Index, // for every decls_len; points to a `declaration` instruction
3104 /// 5. inst: Index // for every body_len
3105 /// 6. has_bits: u32 // for every 32 fields
3106 /// - the bit is whether corresponding field has an value expression
3107 /// 7. fields: { // for every fields_len
3108 /// field_name: u32,
3109 /// doc_comment: u32, // .empty if no doc_comment
3110 /// value: Ref, // if corresponding bit is set
3111 /// }
3112 pub const EnumDecl = struct {
3113 // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`.
3114 // This hash contains the source of all fields, and the backing type if specified.
3115 fields_hash_0: u32,
3116 fields_hash_1: u32,
3117 fields_hash_2: u32,
3118 fields_hash_3: u32,
3119 src_node: i32,
3120
3121 pub fn src(self: EnumDecl) LazySrcLoc {
3122 return LazySrcLoc.nodeOffset(self.src_node);
3123 }
3124
3125 pub const Small = packed struct {
3126 has_tag_type: bool,
3127 has_body_len: bool,
3128 has_fields_len: bool,
3129 has_decls_len: bool,
3130 name_strategy: NameStrategy,
3131 nonexhaustive: bool,
3132 _: u9 = undefined,
3133 };
3134 };
3135
3136 /// Trailing:
3137 /// 0. tag_type: Ref, // if has_tag_type
3138 /// 1. body_len: u32, // if has_body_len
3139 /// 2. fields_len: u32, // if has_fields_len
3140 /// 3. decls_len: u32, // if has_decls_len
3141 /// 4. decl: Index, // for every decls_len; points to a `declaration` instruction
3142 /// 5. inst: Index // for every body_len
3143 /// 6. has_bits: u32 // for every 8 fields
3144 /// - sets of 4 bits:
3145 /// 0b000X: whether corresponding field has a type expression
3146 /// 0b00X0: whether corresponding field has a align expression
3147 /// 0b0X00: whether corresponding field has a tag value expression
3148 /// 0bX000: unused
3149 /// 7. fields: { // for every fields_len
3150 /// field_name: NullTerminatedString, // null terminated string index
3151 /// doc_comment: NullTerminatedString, // .empty if no doc comment
3152 /// field_type: Ref, // if corresponding bit is set
3153 /// - if none, means `anytype`.
3154 /// align: Ref, // if corresponding bit is set
3155 /// tag_value: Ref, // if corresponding bit is set
3156 /// }
3157 pub const UnionDecl = struct {
3158 // These fields should be concatenated and reinterpreted as a `std.zig.SrcHash`.
3159 // This hash contains the source of all fields, and any specified attributes (`extern` etc).
3160 fields_hash_0: u32,
3161 fields_hash_1: u32,
3162 fields_hash_2: u32,
3163 fields_hash_3: u32,
3164 src_node: i32,
3165
3166 pub fn src(self: UnionDecl) LazySrcLoc {
3167 return LazySrcLoc.nodeOffset(self.src_node);
3168 }
3169
3170 pub const Small = packed struct {
3171 has_tag_type: bool,
3172 has_body_len: bool,
3173 has_fields_len: bool,
3174 has_decls_len: bool,
3175 name_strategy: NameStrategy,
3176 layout: std.builtin.Type.ContainerLayout,
3177 /// has_tag_type | auto_enum_tag | result
3178 /// -------------------------------------
3179 /// false | false | union { }
3180 /// false | true | union(enum) { }
3181 /// true | true | union(enum(T)) { }
3182 /// true | false | union(T) { }
3183 auto_enum_tag: bool,
3184 any_aligned_fields: bool,
3185 _: u6 = undefined,
3186 };
3187 };
3188
3189 /// Trailing:
3190 /// 0. decls_len: u32, // if has_decls_len
3191 /// 1. decl: Index, // for every decls_len; points to a `declaration` instruction
3192 pub const OpaqueDecl = struct {
3193 src_node: i32,
3194
3195 pub fn src(self: OpaqueDecl) LazySrcLoc {
3196 return LazySrcLoc.nodeOffset(self.src_node);
3197 }
3198
3199 pub const Small = packed struct {
3200 has_decls_len: bool,
3201 name_strategy: NameStrategy,
3202 _: u13 = undefined,
3203 };
3204 };
3205
3206 /// Trailing:
3207 /// { // for every fields_len
3208 /// field_name: NullTerminatedString // null terminated string index
3209 /// doc_comment: NullTerminatedString // null terminated string index
3210 /// }
3211 pub const ErrorSetDecl = struct {
3212 fields_len: u32,
3213 };
3214
3215 /// A f128 value, broken up into 4 u32 parts.
3216 pub const Float128 = struct {
3217 piece0: u32,
3218 piece1: u32,
3219 piece2: u32,
3220 piece3: u32,
3221
3222 pub fn get(self: Float128) f128 {
3223 const int_bits = @as(u128, self.piece0) |
3224 (@as(u128, self.piece1) << 32) |
3225 (@as(u128, self.piece2) << 64) |
3226 (@as(u128, self.piece3) << 96);
3227 return @as(f128, @bitCast(int_bits));
3228 }
3229 };
3230
3231 /// Trailing is an item per field.
3232 pub const StructInit = struct {
3233 fields_len: u32,
3234
3235 pub const Item = struct {
3236 /// The `struct_init_field_type` ZIR instruction for this field init.
3237 field_type: Index,
3238 /// The field init expression to be used as the field value. This value will be coerced
3239 /// to the field type if not already.
3240 init: Ref,
3241 };
3242 };
3243
3244 /// Trailing is an Item per field.
3245 /// TODO make this instead array of inits followed by array of names because
3246 /// it will be simpler Sema code and better for CPU cache.
3247 pub const StructInitAnon = struct {
3248 fields_len: u32,
3249
3250 pub const Item = struct {
3251 /// Null-terminated string table index.
3252 field_name: NullTerminatedString,
3253 /// The field init expression to be used as the field value.
3254 init: Ref,
3255 };
3256 };
3257
3258 pub const FieldType = struct {
3259 container_type: Ref,
3260 /// Offset into `string_bytes`, null terminated.
3261 name_start: NullTerminatedString,
3262 };
3263
3264 pub const FieldTypeRef = struct {
3265 container_type: Ref,
3266 field_name: Ref,
3267 };
3268
3269 pub const Cmpxchg = struct {
3270 node: i32,
3271 ptr: Ref,
3272 expected_value: Ref,
3273 new_value: Ref,
3274 success_order: Ref,
3275 failure_order: Ref,
3276 };
3277
3278 pub const AtomicRmw = struct {
3279 ptr: Ref,
3280 operation: Ref,
3281 operand: Ref,
3282 ordering: Ref,
3283 };
3284
3285 pub const UnionInit = struct {
3286 union_type: Ref,
3287 field_name: Ref,
3288 init: Ref,
3289 };
3290
3291 pub const AtomicStore = struct {
3292 ptr: Ref,
3293 operand: Ref,
3294 ordering: Ref,
3295 };
3296
3297 pub const AtomicLoad = struct {
3298 elem_type: Ref,
3299 ptr: Ref,
3300 ordering: Ref,
3301 };
3302
3303 pub const MulAdd = struct {
3304 mulend1: Ref,
3305 mulend2: Ref,
3306 addend: Ref,
3307 };
3308
3309 pub const FieldParentPtr = struct {
3310 parent_type: Ref,
3311 field_name: Ref,
3312 field_ptr: Ref,
3313 };
3314
3315 pub const Shuffle = struct {
3316 elem_type: Ref,
3317 a: Ref,
3318 b: Ref,
3319 mask: Ref,
3320 };
3321
3322 pub const Select = struct {
3323 node: i32,
3324 elem_type: Ref,
3325 pred: Ref,
3326 a: Ref,
3327 b: Ref,
3328 };
3329
3330 pub const AsyncCall = struct {
3331 node: i32,
3332 frame_buffer: Ref,
3333 result_ptr: Ref,
3334 fn_ptr: Ref,
3335 args: Ref,
3336 };
3337
3338 /// Trailing: inst: Index // for every body_len
3339 pub const Param = struct {
3340 /// Null-terminated string index.
3341 name: NullTerminatedString,
3342 /// Null-terminated string index.
3343 doc_comment: NullTerminatedString,
3344 /// The body contains the type of the parameter.
3345 body_len: u32,
3346 };
3347
3348 /// Trailing:
3349 /// 0. type_inst: Ref, // if small 0b000X is set
3350 /// 1. align_inst: Ref, // if small 0b00X0 is set
3351 pub const AllocExtended = struct {
3352 src_node: i32,
3353
3354 pub const Small = packed struct {
3355 has_type: bool,
3356 has_align: bool,
3357 is_const: bool,
3358 is_comptime: bool,
3359 _: u12 = undefined,
3360 };
3361 };
3362
3363 pub const Export = struct {
3364 /// If present, this is referring to a Decl via field access, e.g. `a.b`.
3365 /// If omitted, this is referring to a Decl via identifier, e.g. `a`.
3366 namespace: Ref,
3367 /// Null-terminated string index.
3368 decl_name: NullTerminatedString,
3369 options: Ref,
3370 };
3371
3372 pub const ExportValue = struct {
3373 /// The comptime value to export.
3374 operand: Ref,
3375 options: Ref,
3376 };
3377
3378 /// Trailing: `CompileErrors.Item` for each `items_len`.
3379 pub const CompileErrors = struct {
3380 items_len: u32,
3381
3382 /// Trailing: `note_payload_index: u32` for each `notes_len`.
3383 /// It's a payload index of another `Item`.
3384 pub const Item = struct {
3385 /// null terminated string index
3386 msg: NullTerminatedString,
3387 node: Ast.Node.Index,
3388 /// If node is 0 then this will be populated.
3389 token: Ast.TokenIndex,
3390 /// Can be used in combination with `token`.
3391 byte_offset: u32,
3392 /// 0 or a payload index of a `Block`, each is a payload
3393 /// index of another `Item`.
3394 notes: u32,
3395
3396 pub fn notesLen(item: Item, zir: Zir) u32 {
3397 if (item.notes == 0) return 0;
3398 const block = zir.extraData(Block, item.notes);
3399 return block.data.body_len;
3400 }
3401 };
3402 };
3403
3404 /// Trailing: for each `imports_len` there is an Item
3405 pub const Imports = struct {
3406 imports_len: u32,
3407
3408 pub const Item = struct {
3409 /// null terminated string index
3410 name: NullTerminatedString,
3411 /// points to the import name
3412 token: Ast.TokenIndex,
3413 };
3414 };
3415
3416 pub const LineColumn = struct {
3417 line: u32,
3418 column: u32,
3419 };
3420
3421 pub const ArrayInit = struct {
3422 ty: Ref,
3423 init_count: u32,
3424 };
3425
3426 pub const Src = struct {
3427 node: i32,
3428 line: u32,
3429 column: u32,
3430 };
3431
3432 pub const DeferErrCode = struct {
3433 remapped_err_code: Index,
3434 index: u32,
3435 len: u32,
3436 };
3437
3438 pub const ValidateDestructure = struct {
3439 /// The value being destructured.
3440 operand: Ref,
3441 /// The `destructure_assign` node.
3442 destructure_node: i32,
3443 /// The expected field count.
3444 expect_len: u32,
3445 };
3446
3447 pub const ArrayMul = struct {
3448 /// The result type of the array multiplication operation, or `.none` if none was available.
3449 res_ty: Ref,
3450 /// The LHS of the array multiplication.
3451 lhs: Ref,
3452 /// The RHS of the array multiplication.
3453 rhs: Ref,
3454 };
3455
3456 pub const RestoreErrRetIndex = struct {
3457 src_node: i32,
3458 /// If `.none`, restore the trace to its state upon function entry.
3459 block: Ref,
3460 /// If `.none`, restore unconditionally.
3461 operand: Ref,
3462
3463 pub fn src(self: RestoreErrRetIndex) LazySrcLoc {
3464 return LazySrcLoc.nodeOffset(self.src_node);
3465 }
3466 };
3467};
3468
3469pub const SpecialProng = enum { none, @"else", under };
3470
3471pub const DeclIterator = struct {
3472 extra_index: u32,
3473 decls_remaining: u32,
3474 zir: Zir,
3475
3476 pub fn next(it: *DeclIterator) ?Inst.Index {
3477 if (it.decls_remaining == 0) return null;
3478 const decl_inst: Zir.Inst.Index = @enumFromInt(it.zir.extra[it.extra_index]);
3479 it.extra_index += 1;
3480 it.decls_remaining -= 1;
3481 assert(it.zir.instructions.items(.tag)[@intFromEnum(decl_inst)] == .declaration);
3482 return decl_inst;
3483 }
3484};
3485
3486pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
3487 const tags = zir.instructions.items(.tag);
3488 const datas = zir.instructions.items(.data);
3489 switch (tags[@intFromEnum(decl_inst)]) {
3490 // Functions are allowed and yield no iterations.
3491 // There is one case matching this in the extended instruction set below.
3492 .func, .func_inferred, .func_fancy => return .{
3493 .extra_index = undefined,
3494 .decls_remaining = 0,
3495 .zir = zir,
3496 },
3497
3498 .extended => {
3499 const extended = datas[@intFromEnum(decl_inst)].extended;
3500 switch (extended.opcode) {
3501 .struct_decl => {
3502 const small: Inst.StructDecl.Small = @bitCast(extended.small);
3503 var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.StructDecl).Struct.fields.len);
3504 extra_index += @intFromBool(small.has_fields_len);
3505 const decls_len = if (small.has_decls_len) decls_len: {
3506 const decls_len = zir.extra[extra_index];
3507 extra_index += 1;
3508 break :decls_len decls_len;
3509 } else 0;
3510
3511 if (small.has_backing_int) {
3512 const backing_int_body_len = zir.extra[extra_index];
3513 extra_index += 1; // backing_int_body_len
3514 if (backing_int_body_len == 0) {
3515 extra_index += 1; // backing_int_ref
3516 } else {
3517 extra_index += backing_int_body_len; // backing_int_body_inst
3518 }
3519 }
3520
3521 return .{
3522 .extra_index = extra_index,
3523 .decls_remaining = decls_len,
3524 .zir = zir,
3525 };
3526 },
3527 .enum_decl => {
3528 const small: Inst.EnumDecl.Small = @bitCast(extended.small);
3529 var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.EnumDecl).Struct.fields.len);
3530 extra_index += @intFromBool(small.has_tag_type);
3531 extra_index += @intFromBool(small.has_body_len);
3532 extra_index += @intFromBool(small.has_fields_len);
3533 const decls_len = if (small.has_decls_len) decls_len: {
3534 const decls_len = zir.extra[extra_index];
3535 extra_index += 1;
3536 break :decls_len decls_len;
3537 } else 0;
3538
3539 return .{
3540 .extra_index = extra_index,
3541 .decls_remaining = decls_len,
3542 .zir = zir,
3543 };
3544 },
3545 .union_decl => {
3546 const small: Inst.UnionDecl.Small = @bitCast(extended.small);
3547 var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.UnionDecl).Struct.fields.len);
3548 extra_index += @intFromBool(small.has_tag_type);
3549 extra_index += @intFromBool(small.has_body_len);
3550 extra_index += @intFromBool(small.has_fields_len);
3551 const decls_len = if (small.has_decls_len) decls_len: {
3552 const decls_len = zir.extra[extra_index];
3553 extra_index += 1;
3554 break :decls_len decls_len;
3555 } else 0;
3556
3557 return .{
3558 .extra_index = extra_index,
3559 .decls_remaining = decls_len,
3560 .zir = zir,
3561 };
3562 },
3563 .opaque_decl => {
3564 const small: Inst.OpaqueDecl.Small = @bitCast(extended.small);
3565 var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.OpaqueDecl).Struct.fields.len);
3566 const decls_len = if (small.has_decls_len) decls_len: {
3567 const decls_len = zir.extra[extra_index];
3568 extra_index += 1;
3569 break :decls_len decls_len;
3570 } else 0;
3571
3572 return .{
3573 .extra_index = extra_index,
3574 .decls_remaining = decls_len,
3575 .zir = zir,
3576 };
3577 },
3578 else => unreachable,
3579 }
3580 },
3581 else => unreachable,
3582 }
3583}
3584
3585/// The iterator would have to allocate memory anyway to iterate. So here we populate
3586/// an ArrayList as the result.
3587pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_inst: Zir.Inst.Index) !void {
3588 list.clearRetainingCapacity();
3589 const declaration, const extra_end = zir.getDeclaration(decl_inst);
3590 const bodies = declaration.getBodies(extra_end, zir);
3591
3592 try zir.findDeclsBody(list, bodies.value_body);
3593 if (bodies.align_body) |b| try zir.findDeclsBody(list, b);
3594 if (bodies.linksection_body) |b| try zir.findDeclsBody(list, b);
3595 if (bodies.addrspace_body) |b| try zir.findDeclsBody(list, b);
3596}
3597
3598fn findDeclsInner(
3599 zir: Zir,
3600 list: *std.ArrayList(Inst.Index),
3601 inst: Inst.Index,
3602) Allocator.Error!void {
3603 const tags = zir.instructions.items(.tag);
3604 const datas = zir.instructions.items(.data);
3605
3606 switch (tags[@intFromEnum(inst)]) {
3607 // Functions instructions are interesting and have a body.
3608 .func,
3609 .func_inferred,
3610 => {
3611 try list.append(inst);
3612
3613 const inst_data = datas[@intFromEnum(inst)].pl_node;
3614 const extra = zir.extraData(Inst.Func, inst_data.payload_index);
3615 var extra_index: usize = extra.end;
3616 switch (extra.data.ret_body_len) {
3617 0 => {},
3618 1 => extra_index += 1,
3619 else => {
3620 const body = zir.bodySlice(extra_index, extra.data.ret_body_len);
3621 extra_index += body.len;
3622 try zir.findDeclsBody(list, body);
3623 },
3624 }
3625 const body = zir.bodySlice(extra_index, extra.data.body_len);
3626 return zir.findDeclsBody(list, body);
3627 },
3628 .func_fancy => {
3629 try list.append(inst);
3630
3631 const inst_data = datas[@intFromEnum(inst)].pl_node;
3632 const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
3633 var extra_index: usize = extra.end;
3634 extra_index += @intFromBool(extra.data.bits.has_lib_name);
3635
3636 if (extra.data.bits.has_align_body) {
3637 const body_len = zir.extra[extra_index];
3638 extra_index += 1;
3639 const body = zir.bodySlice(extra_index, body_len);
3640 try zir.findDeclsBody(list, body);
3641 extra_index += body.len;
3642 } else if (extra.data.bits.has_align_ref) {
3643 extra_index += 1;
3644 }
3645
3646 if (extra.data.bits.has_addrspace_body) {
3647 const body_len = zir.extra[extra_index];
3648 extra_index += 1;
3649 const body = zir.bodySlice(extra_index, body_len);
3650 try zir.findDeclsBody(list, body);
3651 extra_index += body.len;
3652 } else if (extra.data.bits.has_addrspace_ref) {
3653 extra_index += 1;
3654 }
3655
3656 if (extra.data.bits.has_section_body) {
3657 const body_len = zir.extra[extra_index];
3658 extra_index += 1;
3659 const body = zir.bodySlice(extra_index, body_len);
3660 try zir.findDeclsBody(list, body);
3661 extra_index += body.len;
3662 } else if (extra.data.bits.has_section_ref) {
3663 extra_index += 1;
3664 }
3665
3666 if (extra.data.bits.has_cc_body) {
3667 const body_len = zir.extra[extra_index];
3668 extra_index += 1;
3669 const body = zir.bodySlice(extra_index, body_len);
3670 try zir.findDeclsBody(list, body);
3671 extra_index += body.len;
3672 } else if (extra.data.bits.has_cc_ref) {
3673 extra_index += 1;
3674 }
3675
3676 if (extra.data.bits.has_ret_ty_body) {
3677 const body_len = zir.extra[extra_index];
3678 extra_index += 1;
3679 const body = zir.bodySlice(extra_index, body_len);
3680 try zir.findDeclsBody(list, body);
3681 extra_index += body.len;
3682 } else if (extra.data.bits.has_ret_ty_ref) {
3683 extra_index += 1;
3684 }
3685
3686 extra_index += @intFromBool(extra.data.bits.has_any_noalias);
3687
3688 const body = zir.bodySlice(extra_index, extra.data.body_len);
3689 return zir.findDeclsBody(list, body);
3690 },
3691 .extended => {
3692 const extended = datas[@intFromEnum(inst)].extended;
3693 switch (extended.opcode) {
3694
3695 // Decl instructions are interesting but have no body.
3696 // TODO yes they do have a body actually. recurse over them just like block instructions.
3697 .struct_decl,
3698 .union_decl,
3699 .enum_decl,
3700 .opaque_decl,
3701 => return list.append(inst),
3702
3703 else => return,
3704 }
3705 },
3706
3707 // Block instructions, recurse over the bodies.
3708
3709 .block, .block_comptime, .block_inline => {
3710 const inst_data = datas[@intFromEnum(inst)].pl_node;
3711 const extra = zir.extraData(Inst.Block, inst_data.payload_index);
3712 const body = zir.bodySlice(extra.end, extra.data.body_len);
3713 return zir.findDeclsBody(list, body);
3714 },
3715 .condbr, .condbr_inline => {
3716 const inst_data = datas[@intFromEnum(inst)].pl_node;
3717 const extra = zir.extraData(Inst.CondBr, inst_data.payload_index);
3718 const then_body = zir.bodySlice(extra.end, extra.data.then_body_len);
3719 const else_body = zir.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
3720 try zir.findDeclsBody(list, then_body);
3721 try zir.findDeclsBody(list, else_body);
3722 },
3723 .@"try", .try_ptr => {
3724 const inst_data = datas[@intFromEnum(inst)].pl_node;
3725 const extra = zir.extraData(Inst.Try, inst_data.payload_index);
3726 const body = zir.bodySlice(extra.end, extra.data.body_len);
3727 try zir.findDeclsBody(list, body);
3728 },
3729 .switch_block => return findDeclsSwitch(zir, list, inst),
3730
3731 .suspend_block => @panic("TODO iterate suspend block"),
3732
3733 else => return, // Regular instruction, not interesting.
3734 }
3735}
3736
3737fn findDeclsSwitch(
3738 zir: Zir,
3739 list: *std.ArrayList(Inst.Index),
3740 inst: Inst.Index,
3741) Allocator.Error!void {
3742 const inst_data = zir.instructions.items(.data)[@intFromEnum(inst)].pl_node;
3743 const extra = zir.extraData(Inst.SwitchBlock, inst_data.payload_index);
3744
3745 var extra_index: usize = extra.end;
3746
3747 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
3748 const multi_cases_len = zir.extra[extra_index];
3749 extra_index += 1;
3750 break :blk multi_cases_len;
3751 } else 0;
3752
3753 const special_prong = extra.data.bits.specialProng();
3754 if (special_prong != .none) {
3755 const body_len: u31 = @truncate(zir.extra[extra_index]);
3756 extra_index += 1;
3757 const body = zir.bodySlice(extra_index, body_len);
3758 extra_index += body.len;
3759
3760 try zir.findDeclsBody(list, body);
3761 }
3762
3763 {
3764 const scalar_cases_len = extra.data.bits.scalar_cases_len;
3765 for (0..scalar_cases_len) |_| {
3766 extra_index += 1;
3767 const body_len: u31 = @truncate(zir.extra[extra_index]);
3768 extra_index += 1;
3769 const body = zir.bodySlice(extra_index, body_len);
3770 extra_index += body_len;
3771
3772 try zir.findDeclsBody(list, body);
3773 }
3774 }
3775 {
3776 for (0..multi_cases_len) |_| {
3777 const items_len = zir.extra[extra_index];
3778 extra_index += 1;
3779 const ranges_len = zir.extra[extra_index];
3780 extra_index += 1;
3781 const body_len: u31 = @truncate(zir.extra[extra_index]);
3782 extra_index += 1;
3783 const items = zir.refSlice(extra_index, items_len);
3784 extra_index += items_len;
3785 _ = items;
3786
3787 var range_i: usize = 0;
3788 while (range_i < ranges_len) : (range_i += 1) {
3789 extra_index += 1;
3790 extra_index += 1;
3791 }
3792
3793 const body = zir.bodySlice(extra_index, body_len);
3794 extra_index += body_len;
3795
3796 try zir.findDeclsBody(list, body);
3797 }
3798 }
3799}
3800
3801fn findDeclsBody(
3802 zir: Zir,
3803 list: *std.ArrayList(Inst.Index),
3804 body: []const Inst.Index,
3805) Allocator.Error!void {
3806 for (body) |member| {
3807 try zir.findDeclsInner(list, member);
3808 }
3809}
3810
3811pub const FnInfo = struct {
3812 param_body: []const Inst.Index,
3813 param_body_inst: Inst.Index,
3814 ret_ty_body: []const Inst.Index,
3815 body: []const Inst.Index,
3816 ret_ty_ref: Zir.Inst.Ref,
3817 total_params_len: u32,
3818};
3819
3820pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const Zir.Inst.Index {
3821 const tags = zir.instructions.items(.tag);
3822 const datas = zir.instructions.items(.data);
3823 const inst_data = datas[@intFromEnum(fn_inst)].pl_node;
3824
3825 const param_block_index = switch (tags[@intFromEnum(fn_inst)]) {
3826 .func, .func_inferred => blk: {
3827 const extra = zir.extraData(Inst.Func, inst_data.payload_index);
3828 break :blk extra.data.param_block;
3829 },
3830 .func_fancy => blk: {
3831 const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
3832 break :blk extra.data.param_block;
3833 },
3834 else => unreachable,
3835 };
3836
3837 switch (tags[@intFromEnum(param_block_index)]) {
3838 .block, .block_comptime, .block_inline => {
3839 const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(param_block_index)].pl_node.payload_index);
3840 return zir.bodySlice(param_block.end, param_block.data.body_len);
3841 },
3842 .declaration => {
3843 const decl, const extra_end = zir.getDeclaration(param_block_index);
3844 return decl.getBodies(extra_end, zir).value_body;
3845 },
3846 else => unreachable,
3847 }
3848}
3849
3850pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
3851 const tags = zir.instructions.items(.tag);
3852 const datas = zir.instructions.items(.data);
3853 const info: struct {
3854 param_block: Inst.Index,
3855 body: []const Inst.Index,
3856 ret_ty_ref: Inst.Ref,
3857 ret_ty_body: []const Inst.Index,
3858 } = switch (tags[@intFromEnum(fn_inst)]) {
3859 .func, .func_inferred => blk: {
3860 const inst_data = datas[@intFromEnum(fn_inst)].pl_node;
3861 const extra = zir.extraData(Inst.Func, inst_data.payload_index);
3862
3863 var extra_index: usize = extra.end;
3864 var ret_ty_ref: Inst.Ref = .none;
3865 var ret_ty_body: []const Inst.Index = &.{};
3866
3867 switch (extra.data.ret_body_len) {
3868 0 => {
3869 ret_ty_ref = .void_type;
3870 },
3871 1 => {
3872 ret_ty_ref = @enumFromInt(zir.extra[extra_index]);
3873 extra_index += 1;
3874 },
3875 else => {
3876 ret_ty_body = zir.bodySlice(extra_index, extra.data.ret_body_len);
3877 extra_index += ret_ty_body.len;
3878 },
3879 }
3880
3881 const body = zir.bodySlice(extra_index, extra.data.body_len);
3882 extra_index += body.len;
3883
3884 break :blk .{
3885 .param_block = extra.data.param_block,
3886 .ret_ty_ref = ret_ty_ref,
3887 .ret_ty_body = ret_ty_body,
3888 .body = body,
3889 };
3890 },
3891 .func_fancy => blk: {
3892 const inst_data = datas[@intFromEnum(fn_inst)].pl_node;
3893 const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
3894
3895 var extra_index: usize = extra.end;
3896 var ret_ty_ref: Inst.Ref = .void_type;
3897 var ret_ty_body: []const Inst.Index = &.{};
3898
3899 extra_index += @intFromBool(extra.data.bits.has_lib_name);
3900 if (extra.data.bits.has_align_body) {
3901 extra_index += zir.extra[extra_index] + 1;
3902 } else if (extra.data.bits.has_align_ref) {
3903 extra_index += 1;
3904 }
3905 if (extra.data.bits.has_addrspace_body) {
3906 extra_index += zir.extra[extra_index] + 1;
3907 } else if (extra.data.bits.has_addrspace_ref) {
3908 extra_index += 1;
3909 }
3910 if (extra.data.bits.has_section_body) {
3911 extra_index += zir.extra[extra_index] + 1;
3912 } else if (extra.data.bits.has_section_ref) {
3913 extra_index += 1;
3914 }
3915 if (extra.data.bits.has_cc_body) {
3916 extra_index += zir.extra[extra_index] + 1;
3917 } else if (extra.data.bits.has_cc_ref) {
3918 extra_index += 1;
3919 }
3920 if (extra.data.bits.has_ret_ty_body) {
3921 const body_len = zir.extra[extra_index];
3922 extra_index += 1;
3923 ret_ty_body = zir.bodySlice(extra_index, body_len);
3924 extra_index += ret_ty_body.len;
3925 } else if (extra.data.bits.has_ret_ty_ref) {
3926 ret_ty_ref = @enumFromInt(zir.extra[extra_index]);
3927 extra_index += 1;
3928 }
3929
3930 extra_index += @intFromBool(extra.data.bits.has_any_noalias);
3931
3932 const body = zir.bodySlice(extra_index, extra.data.body_len);
3933 extra_index += body.len;
3934 break :blk .{
3935 .param_block = extra.data.param_block,
3936 .ret_ty_ref = ret_ty_ref,
3937 .ret_ty_body = ret_ty_body,
3938 .body = body,
3939 };
3940 },
3941 else => unreachable,
3942 };
3943 const param_body = switch (tags[@intFromEnum(info.param_block)]) {
3944 .block, .block_comptime, .block_inline => param_body: {
3945 const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(info.param_block)].pl_node.payload_index);
3946 break :param_body zir.bodySlice(param_block.end, param_block.data.body_len);
3947 },
3948 .declaration => param_body: {
3949 const decl, const extra_end = zir.getDeclaration(info.param_block);
3950 break :param_body decl.getBodies(extra_end, zir).value_body;
3951 },
3952 else => unreachable,
3953 };
3954 var total_params_len: u32 = 0;
3955 for (param_body) |inst| {
3956 switch (tags[@intFromEnum(inst)]) {
3957 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
3958 total_params_len += 1;
3959 },
3960 else => continue,
3961 }
3962 }
3963 return .{
3964 .param_body = param_body,
3965 .param_body_inst = info.param_block,
3966 .ret_ty_body = info.ret_ty_body,
3967 .ret_ty_ref = info.ret_ty_ref,
3968 .body = info.body,
3969 .total_params_len = total_params_len,
3970 };
3971}
3972
3973pub fn getDeclaration(zir: Zir, inst: Zir.Inst.Index) struct { Inst.Declaration, u32 } {
3974 assert(zir.instructions.items(.tag)[@intFromEnum(inst)] == .declaration);
3975 const pl_node = zir.instructions.items(.data)[@intFromEnum(inst)].pl_node;
3976 const extra = zir.extraData(Inst.Declaration, pl_node.payload_index);
3977 return .{
3978 extra.data,
3979 @intCast(extra.end),
3980 };
3981}
3982
3983pub fn getAssociatedSrcHash(zir: Zir, inst: Zir.Inst.Index) ?std.zig.SrcHash {
3984 const tag = zir.instructions.items(.tag);
3985 const data = zir.instructions.items(.data);
3986 switch (tag[@intFromEnum(inst)]) {
3987 .declaration => {
3988 const pl_node = data[@intFromEnum(inst)].pl_node;
3989 const extra = zir.extraData(Inst.Declaration, pl_node.payload_index);
3990 return @bitCast([4]u32{
3991 extra.data.src_hash_0,
3992 extra.data.src_hash_1,
3993 extra.data.src_hash_2,
3994 extra.data.src_hash_3,
3995 });
3996 },
3997 .func, .func_inferred => {
3998 const pl_node = data[@intFromEnum(inst)].pl_node;
3999 const extra = zir.extraData(Inst.Func, pl_node.payload_index);
4000 if (extra.data.body_len == 0) {
4001 // Function type or extern fn - no associated hash
4002 return null;
4003 }
4004 const extra_index = extra.end +
4005 1 +
4006 extra.data.body_len +
4007 @typeInfo(Inst.Func.SrcLocs).Struct.fields.len;
4008 return @bitCast([4]u32{
4009 zir.extra[extra_index + 0],
4010 zir.extra[extra_index + 1],
4011 zir.extra[extra_index + 2],
4012 zir.extra[extra_index + 3],
4013 });
4014 },
4015 .func_fancy => {
4016 const pl_node = data[@intFromEnum(inst)].pl_node;
4017 const extra = zir.extraData(Inst.FuncFancy, pl_node.payload_index);
4018 if (extra.data.body_len == 0) {
4019 // Function type or extern fn - no associated hash
4020 return null;
4021 }
4022 const bits = extra.data.bits;
4023 var extra_index = extra.end;
4024 extra_index += @intFromBool(bits.has_lib_name);
4025 if (bits.has_align_body) {
4026 const body_len = zir.extra[extra_index];
4027 extra_index += 1 + body_len;
4028 } else extra_index += @intFromBool(bits.has_align_ref);
4029 if (bits.has_addrspace_body) {
4030 const body_len = zir.extra[extra_index];
4031 extra_index += 1 + body_len;
4032 } else extra_index += @intFromBool(bits.has_addrspace_ref);
4033 if (bits.has_section_body) {
4034 const body_len = zir.extra[extra_index];
4035 extra_index += 1 + body_len;
4036 } else extra_index += @intFromBool(bits.has_section_ref);
4037 if (bits.has_cc_body) {
4038 const body_len = zir.extra[extra_index];
4039 extra_index += 1 + body_len;
4040 } else extra_index += @intFromBool(bits.has_cc_ref);
4041 if (bits.has_ret_ty_body) {
4042 const body_len = zir.extra[extra_index];
4043 extra_index += 1 + body_len;
4044 } else extra_index += @intFromBool(bits.has_ret_ty_ref);
4045 extra_index += @intFromBool(bits.has_any_noalias);
4046 extra_index += extra.data.body_len;
4047 extra_index += @typeInfo(Zir.Inst.Func.SrcLocs).Struct.fields.len;
4048 return @bitCast([4]u32{
4049 zir.extra[extra_index + 0],
4050 zir.extra[extra_index + 1],
4051 zir.extra[extra_index + 2],
4052 zir.extra[extra_index + 3],
4053 });
4054 },
4055 .extended => {},
4056 else => return null,
4057 }
4058 const extended = data[@intFromEnum(inst)].extended;
4059 switch (extended.opcode) {
4060 .struct_decl => {
4061 const extra = zir.extraData(Inst.StructDecl, extended.operand).data;
4062 return @bitCast([4]u32{
4063 extra.fields_hash_0,
4064 extra.fields_hash_1,
4065 extra.fields_hash_2,
4066 extra.fields_hash_3,
4067 });
4068 },
4069 .union_decl => {
4070 const extra = zir.extraData(Inst.UnionDecl, extended.operand).data;
4071 return @bitCast([4]u32{
4072 extra.fields_hash_0,
4073 extra.fields_hash_1,
4074 extra.fields_hash_2,
4075 extra.fields_hash_3,
4076 });
4077 },
4078 .enum_decl => {
4079 const extra = zir.extraData(Inst.EnumDecl, extended.operand).data;
4080 return @bitCast([4]u32{
4081 extra.fields_hash_0,
4082 extra.fields_hash_1,
4083 extra.fields_hash_2,
4084 extra.fields_hash_3,
4085 });
4086 },
4087 else => return null,
4088 }
4089}
src/arch/wasm/CodeGen.zig+2-3
...@@ -16,7 +16,7 @@ const Decl = Module.Decl;...@@ -16,7 +16,7 @@ const Decl = Module.Decl;
16const Type = @import("../../type.zig").Type;16const Type = @import("../../type.zig").Type;
17const Value = @import("../../Value.zig");17const Value = @import("../../Value.zig");
18const Compilation = @import("../../Compilation.zig");18const Compilation = @import("../../Compilation.zig");
19const LazySrcLoc = Module.LazySrcLoc;19const LazySrcLoc = std.zig.LazySrcLoc;
20const link = @import("../../link.zig");20const link = @import("../../link.zig");
21const TypedValue = @import("../../TypedValue.zig");21const TypedValue = @import("../../TypedValue.zig");
22const Air = @import("../../Air.zig");22const Air = @import("../../Air.zig");
...@@ -767,8 +767,7 @@ pub fn deinit(func: *CodeGen) void {...@@ -767,8 +767,7 @@ pub fn deinit(func: *CodeGen) void {
767/// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig767/// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig
768fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError {768fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError {
769 const mod = func.bin_file.base.comp.module.?;769 const mod = func.bin_file.base.comp.module.?;
770 const src = LazySrcLoc.nodeOffset(0);770 const src_loc = func.decl.srcLoc(mod);
771 const src_loc = src.toSrcLoc(func.decl, mod);
772 func.err_msg = try Module.ErrorMsg.create(func.gpa, src_loc, fmt, args);771 func.err_msg = try Module.ErrorMsg.create(func.gpa, src_loc, fmt, args);
773 return error.CodegenFail;772 return error.CodegenFail;
774}773}
src/codegen.zig+1-1
...@@ -21,7 +21,7 @@ const Target = std.Target;...@@ -21,7 +21,7 @@ const Target = std.Target;
21const Type = @import("type.zig").Type;21const Type = @import("type.zig").Type;
22const TypedValue = @import("TypedValue.zig");22const TypedValue = @import("TypedValue.zig");
23const Value = @import("Value.zig");23const Value = @import("Value.zig");
24const Zir = @import("Zir.zig");24const Zir = std.zig.Zir;
25const Alignment = InternPool.Alignment;25const Alignment = InternPool.Alignment;
2626
27pub const Result = union(enum) {27pub const Result = union(enum) {
src/codegen/c.zig+2-3
...@@ -13,7 +13,7 @@ const TypedValue = @import("../TypedValue.zig");...@@ -13,7 +13,7 @@ const TypedValue = @import("../TypedValue.zig");
13const C = link.File.C;13const C = link.File.C;
14const Decl = Module.Decl;14const Decl = Module.Decl;
15const trace = @import("../tracy.zig").trace;15const trace = @import("../tracy.zig").trace;
16const LazySrcLoc = Module.LazySrcLoc;16const LazySrcLoc = std.zig.LazySrcLoc;
17const Air = @import("../Air.zig");17const Air = @import("../Air.zig");
18const Liveness = @import("../Liveness.zig");18const Liveness = @import("../Liveness.zig");
19const InternPool = @import("../InternPool.zig");19const InternPool = @import("../InternPool.zig");
...@@ -570,8 +570,7 @@ pub const DeclGen = struct {...@@ -570,8 +570,7 @@ pub const DeclGen = struct {
570 const mod = dg.module;570 const mod = dg.module;
571 const decl_index = dg.pass.decl;571 const decl_index = dg.pass.decl;
572 const decl = mod.declPtr(decl_index);572 const decl = mod.declPtr(decl_index);
573 const src = LazySrcLoc.nodeOffset(0);573 const src_loc = decl.srcLoc(mod);
574 const src_loc = src.toSrcLoc(decl, mod);
575 dg.error_msg = try Module.ErrorMsg.create(dg.gpa, src_loc, format, args);574 dg.error_msg = try Module.ErrorMsg.create(dg.gpa, src_loc, format, args);
576 return error.AnalysisFail;575 return error.AnalysisFail;
577 }576 }
src/codegen/llvm.zig+2-2
...@@ -23,7 +23,7 @@ const Air = @import("../Air.zig");...@@ -23,7 +23,7 @@ const Air = @import("../Air.zig");
23const Liveness = @import("../Liveness.zig");23const Liveness = @import("../Liveness.zig");
24const Value = @import("../Value.zig");24const Value = @import("../Value.zig");
25const Type = @import("../type.zig").Type;25const Type = @import("../type.zig").Type;
26const LazySrcLoc = Module.LazySrcLoc;26const LazySrcLoc = std.zig.LazySrcLoc;
27const x86_64_abi = @import("../arch/x86_64/abi.zig");27const x86_64_abi = @import("../arch/x86_64/abi.zig");
28const wasm_c_abi = @import("../arch/wasm/abi.zig");28const wasm_c_abi = @import("../arch/wasm/abi.zig");
29const aarch64_c_abi = @import("../arch/aarch64/abi.zig");29const aarch64_c_abi = @import("../arch/aarch64/abi.zig");
...@@ -4686,7 +4686,7 @@ pub const DeclGen = struct {...@@ -4686,7 +4686,7 @@ pub const DeclGen = struct {
4686 const o = dg.object;4686 const o = dg.object;
4687 const gpa = o.gpa;4687 const gpa = o.gpa;
4688 const mod = o.module;4688 const mod = o.module;
4689 const src_loc = LazySrcLoc.nodeOffset(0).toSrcLoc(dg.decl, mod);4689 const src_loc = dg.decl.srcLoc(mod);
4690 dg.err_msg = try Module.ErrorMsg.create(gpa, src_loc, "TODO (LLVM): " ++ format, args);4690 dg.err_msg = try Module.ErrorMsg.create(gpa, src_loc, "TODO (LLVM): " ++ format, args);
4691 return error.CodegenFail;4691 return error.CodegenFail;
4692 }4692 }
src/codegen/spirv.zig+3-6
...@@ -8,9 +8,8 @@ const Module = @import("../Module.zig");...@@ -8,9 +8,8 @@ const Module = @import("../Module.zig");
8const Decl = Module.Decl;8const Decl = Module.Decl;
9const Type = @import("../type.zig").Type;9const Type = @import("../type.zig").Type;
10const Value = @import("../Value.zig");10const Value = @import("../Value.zig");
11const LazySrcLoc = Module.LazySrcLoc;11const LazySrcLoc = std.zig.LazySrcLoc;
12const Air = @import("../Air.zig");12const Air = @import("../Air.zig");
13const Zir = @import("../Zir.zig");
14const Liveness = @import("../Liveness.zig");13const Liveness = @import("../Liveness.zig");
15const InternPool = @import("../InternPool.zig");14const InternPool = @import("../InternPool.zig");
1615
...@@ -413,8 +412,7 @@ const DeclGen = struct {...@@ -413,8 +412,7 @@ const DeclGen = struct {
413 pub fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error {412 pub fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error {
414 @setCold(true);413 @setCold(true);
415 const mod = self.module;414 const mod = self.module;
416 const src = LazySrcLoc.nodeOffset(0);415 const src_loc = self.module.declPtr(self.decl_index).srcLoc(mod);
417 const src_loc = src.toSrcLoc(self.module.declPtr(self.decl_index), mod);
418 assert(self.error_msg == null);416 assert(self.error_msg == null);
419 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args);417 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args);
420 return error.CodegenFail;418 return error.CodegenFail;
...@@ -5270,8 +5268,7 @@ const DeclGen = struct {...@@ -5270,8 +5268,7 @@ const DeclGen = struct {
5270 // TODO: Translate proper error locations.5268 // TODO: Translate proper error locations.
5271 assert(as.errors.items.len != 0);5269 assert(as.errors.items.len != 0);
5272 assert(self.error_msg == null);5270 assert(self.error_msg == null);
5273 const loc = LazySrcLoc.nodeOffset(0);5271 const src_loc = self.module.declPtr(self.decl_index).srcLoc(mod);
5274 const src_loc = loc.toSrcLoc(self.module.declPtr(self.decl_index), mod);
5275 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{});5272 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{});
5276 const notes = try self.module.gpa.alloc(Module.ErrorMsg, as.errors.items.len);5273 const notes = try self.module.gpa.alloc(Module.ErrorMsg, as.errors.items.len);
52775274
src/crash_report.zig+1-1
...@@ -8,7 +8,7 @@ const native_os = builtin.os.tag;...@@ -8,7 +8,7 @@ const native_os = builtin.os.tag;
88
9const Module = @import("Module.zig");9const Module = @import("Module.zig");
10const Sema = @import("Sema.zig");10const Sema = @import("Sema.zig");
11const Zir = @import("Zir.zig");11const Zir = std.zig.Zir;
12const Decl = Module.Decl;12const Decl = Module.Decl;
1313
14pub const is_enabled = builtin.mode == .Debug;14pub const is_enabled = builtin.mode == .Debug;
src/introspect.zig+1
...@@ -153,6 +153,7 @@ pub const EnvVar = enum {...@@ -153,6 +153,7 @@ pub const EnvVar = enum {
153 ZIG_VERBOSE_LINK,153 ZIG_VERBOSE_LINK,
154 ZIG_VERBOSE_CC,154 ZIG_VERBOSE_CC,
155 ZIG_BTRFS_WORKAROUND,155 ZIG_BTRFS_WORKAROUND,
156 ZIG_DEBUG_CMD,
156 CC,157 CC,
157 NO_COLOR,158 NO_COLOR,
158 XDG_CACHE_HOME,159 XDG_CACHE_HOME,
src/main.zig+154-490
...@@ -8,6 +8,7 @@ const process = std.process;...@@ -8,6 +8,7 @@ const process = std.process;
8const Allocator = mem.Allocator;8const Allocator = mem.Allocator;
9const ArrayList = std.ArrayList;9const ArrayList = std.ArrayList;
10const Ast = std.zig.Ast;10const Ast = std.zig.Ast;
11const Color = std.zig.Color;
11const warn = std.log.warn;12const warn = std.log.warn;
12const ThreadPool = std.Thread.Pool;13const ThreadPool = std.Thread.Pool;
13const cleanExit = std.process.cleanExit;14const cleanExit = std.process.cleanExit;
...@@ -25,7 +26,7 @@ const Cache = std.Build.Cache;...@@ -25,7 +26,7 @@ const Cache = std.Build.Cache;
25const target_util = @import("target.zig");26const target_util = @import("target.zig");
26const crash_report = @import("crash_report.zig");27const crash_report = @import("crash_report.zig");
27const Module = @import("Module.zig");28const Module = @import("Module.zig");
28const AstGen = @import("AstGen.zig");29const AstGen = std.zig.AstGen;
29const mingw = @import("mingw.zig");30const mingw = @import("mingw.zig");
30const Server = std.zig.Server;31const Server = std.zig.Server;
3132
...@@ -66,18 +67,8 @@ pub fn fatal(comptime format: []const u8, args: anytype) noreturn {...@@ -66,18 +67,8 @@ pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
66 process.exit(1);67 process.exit(1);
67}68}
6869
69/// There are many assumptions in the entire codebase that Zig source files can
70/// be byte-indexed with a u32 integer.
71const max_src_size = std.math.maxInt(u32);
72
73const debug_extensions_enabled = builtin.mode == .Debug;70const debug_extensions_enabled = builtin.mode == .Debug;
7471
75const Color = enum {
76 auto,
77 off,
78 on,
79};
80
81const normal_usage =72const normal_usage =
82 \\Usage: zig [command] [options]73 \\Usage: zig [command] [options]
83 \\74 \\
...@@ -212,14 +203,6 @@ pub fn main() anyerror!void {...@@ -212,14 +203,6 @@ pub fn main() anyerror!void {
212 }203 }
213 }204 }
214205
215 if (build_options.only_reduce) {
216 if (mem.eql(u8, args[1], "reduce")) {
217 return @import("reduce.zig").main(gpa, arena, args);
218 } else {
219 @panic("only reduce is supported in a -Donly-reduce build");
220 }
221 }
222
223 return mainArgs(gpa, arena, args);206 return mainArgs(gpa, arena, args);
224}207}
225208
...@@ -311,7 +294,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -311,7 +294,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
311 } else if (mem.eql(u8, cmd, "rc")) {294 } else if (mem.eql(u8, cmd, "rc")) {
312 return cmdRc(gpa, arena, args[1..]);295 return cmdRc(gpa, arena, args[1..]);
313 } else if (mem.eql(u8, cmd, "fmt")) {296 } else if (mem.eql(u8, cmd, "fmt")) {
314 return cmdFmt(gpa, arena, cmd_args);297 return jitCmd(gpa, arena, cmd_args, "fmt", "fmt.zig");
315 } else if (mem.eql(u8, cmd, "objcopy")) {298 } else if (mem.eql(u8, cmd, "objcopy")) {
316 return @import("objcopy.zig").cmdObjCopy(gpa, arena, cmd_args);299 return @import("objcopy.zig").cmdObjCopy(gpa, arena, cmd_args);
317 } else if (mem.eql(u8, cmd, "fetch")) {300 } else if (mem.eql(u8, cmd, "fetch")) {
...@@ -334,7 +317,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -334,7 +317,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
334 verifyLibcxxCorrectlyLinked();317 verifyLibcxxCorrectlyLinked();
335 return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());318 return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());
336 } else if (mem.eql(u8, cmd, "reduce")) {319 } else if (mem.eql(u8, cmd, "reduce")) {
337 return @import("reduce.zig").main(gpa, arena, args);320 return jitCmd(gpa, arena, cmd_args, "reduce", "reduce.zig");
338 } else if (mem.eql(u8, cmd, "zen")) {321 } else if (mem.eql(u8, cmd, "zen")) {
339 return io.getStdOut().writeAll(info_zen);322 return io.getStdOut().writeAll(info_zen);
340 } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {323 } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {
...@@ -2756,6 +2739,7 @@ fn buildOutputType(...@@ -2756,6 +2739,7 @@ fn buildOutputType(
2756 .paths = .{2739 .paths = .{
2757 .root = .{2740 .root = .{
2758 .root_dir = zig_lib_directory,2741 .root_dir = zig_lib_directory,
2742 .sub_path = "compiler",
2759 },2743 },
2760 .root_src_path = "test_runner.zig",2744 .root_src_path = "test_runner.zig",
2761 },2745 },
...@@ -4501,7 +4485,7 @@ fn updateModule(comp: *Compilation, color: Color) !void {...@@ -4501,7 +4485,7 @@ fn updateModule(comp: *Compilation, color: Color) !void {
4501 defer errors.deinit(comp.gpa);4485 defer errors.deinit(comp.gpa);
45024486
4503 if (errors.errorMessageCount() > 0) {4487 if (errors.errorMessageCount() > 0) {
4504 errors.renderToStdErr(renderOptions(color));4488 errors.renderToStdErr(color.renderOptions());
4505 return error.SemanticAnalyzeFail;4489 return error.SemanticAnalyzeFail;
4506 }4490 }
4507}4491}
...@@ -4601,7 +4585,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati...@@ -4601,7 +4585,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
4601 p.errors = errors;4585 p.errors = errors;
4602 return;4586 return;
4603 } else {4587 } else {
4604 errors.renderToStdErr(renderOptions(color));4588 errors.renderToStdErr(color.renderOptions());
4605 process.exit(1);4589 process.exit(1);
4606 }4590 }
4607 },4591 },
...@@ -5402,7 +5386,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5402,7 +5386,9 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5402 },5386 },
5403 .root_src_path = fs.path.basename(runner),5387 .root_src_path = fs.path.basename(runner),
5404 } else .{5388 } else .{
5405 .root = .{ .root_dir = zig_lib_directory },5389 .root = .{
5390 .root_dir = zig_lib_directory,
5391 },
5406 .root_src_path = "build_runner.zig",5392 .root_src_path = "build_runner.zig",
5407 };5393 };
54085394
...@@ -5528,7 +5514,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5528,7 +5514,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
55285514
5529 if (fetch.error_bundle.root_list.items.len > 0) {5515 if (fetch.error_bundle.root_list.items.len > 0) {
5530 var errors = try fetch.error_bundle.toOwnedBundle("");5516 var errors = try fetch.error_bundle.toOwnedBundle("");
5531 errors.renderToStdErr(renderOptions(color));5517 errors.renderToStdErr(color.renderOptions());
5532 process.exit(1);5518 process.exit(1);
5533 }5519 }
55345520
...@@ -5719,470 +5705,165 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -5719,470 +5705,165 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5719 }5705 }
5720}5706}
57215707
5722fn readSourceFileToEndAlloc(5708fn jitCmd(
5723 allocator: Allocator,
5724 input: *const fs.File,
5725 size_hint: ?usize,
5726) ![:0]u8 {
5727 const source_code = input.readToEndAllocOptions(
5728 allocator,
5729 max_src_size,
5730 size_hint,
5731 @alignOf(u16),
5732 0,
5733 ) catch |err| switch (err) {
5734 error.ConnectionResetByPeer => unreachable,
5735 error.ConnectionTimedOut => unreachable,
5736 error.NotOpenForReading => unreachable,
5737 else => |e| return e,
5738 };
5739 errdefer allocator.free(source_code);
5740
5741 // Detect unsupported file types with their Byte Order Mark
5742 const unsupported_boms = [_][]const u8{
5743 "\xff\xfe\x00\x00", // UTF-32 little endian
5744 "\xfe\xff\x00\x00", // UTF-32 big endian
5745 "\xfe\xff", // UTF-16 big endian
5746 };
5747 for (unsupported_boms) |bom| {
5748 if (mem.startsWith(u8, source_code, bom)) {
5749 return error.UnsupportedEncoding;
5750 }
5751 }
5752
5753 // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8
5754 if (mem.startsWith(u8, source_code, "\xff\xfe")) {
5755 const source_code_utf16_le = mem.bytesAsSlice(u16, source_code);
5756 const source_code_utf8 = std.unicode.utf16LeToUtf8AllocZ(allocator, source_code_utf16_le) catch |err| switch (err) {
5757 error.DanglingSurrogateHalf => error.UnsupportedEncoding,
5758 error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding,
5759 error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding,
5760 else => |e| return e,
5761 };
5762
5763 allocator.free(source_code);
5764 return source_code_utf8;
5765 }
5766
5767 return source_code;
5768}
5769
5770const usage_fmt =
5771 \\Usage: zig fmt [file]...
5772 \\
5773 \\ Formats the input files and modifies them in-place.
5774 \\ Arguments can be files or directories, which are searched
5775 \\ recursively.
5776 \\
5777 \\Options:
5778 \\ -h, --help Print this help and exit
5779 \\ --color [auto|off|on] Enable or disable colored error messages
5780 \\ --stdin Format code from stdin; output to stdout
5781 \\ --check List non-conforming files and exit with an error
5782 \\ if the list is non-empty
5783 \\ --ast-check Run zig ast-check on every file
5784 \\ --exclude [file] Exclude file or directory from formatting
5785 \\
5786 \\
5787;
5788
5789const Fmt = struct {
5790 seen: SeenMap,
5791 any_error: bool,
5792 check_ast: bool,
5793 color: Color,
5794 gpa: Allocator,5709 gpa: Allocator,
5795 arena: Allocator,5710 arena: Allocator,
5796 out_buffer: std.ArrayList(u8),5711 args: []const []const u8,
57975712 cmd_name: []const u8,
5798 const SeenMap = std.AutoHashMap(fs.File.INode, void);5713 root_src_path: []const u8,
5799};5714) !void {
58005715 const color: Color = .auto;
5801fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5802 var color: Color = .auto;
5803 var stdin_flag: bool = false;
5804 var check_flag: bool = false;
5805 var check_ast_flag: bool = false;
5806 var input_files = ArrayList([]const u8).init(gpa);
5807 defer input_files.deinit();
5808 var excluded_files = ArrayList([]const u8).init(gpa);
5809 defer excluded_files.deinit();
5810
5811 {
5812 var i: usize = 0;
5813 while (i < args.len) : (i += 1) {
5814 const arg = args[i];
5815 if (mem.startsWith(u8, arg, "-")) {
5816 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
5817 const stdout = io.getStdOut().writer();
5818 try stdout.writeAll(usage_fmt);
5819 return cleanExit();
5820 } else if (mem.eql(u8, arg, "--color")) {
5821 if (i + 1 >= args.len) {
5822 fatal("expected [auto|on|off] after --color", .{});
5823 }
5824 i += 1;
5825 const next_arg = args[i];
5826 color = std.meta.stringToEnum(Color, next_arg) orelse {
5827 fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
5828 };
5829 } else if (mem.eql(u8, arg, "--stdin")) {
5830 stdin_flag = true;
5831 } else if (mem.eql(u8, arg, "--check")) {
5832 check_flag = true;
5833 } else if (mem.eql(u8, arg, "--ast-check")) {
5834 check_ast_flag = true;
5835 } else if (mem.eql(u8, arg, "--exclude")) {
5836 if (i + 1 >= args.len) {
5837 fatal("expected parameter after --exclude", .{});
5838 }
5839 i += 1;
5840 const next_arg = args[i];
5841 try excluded_files.append(next_arg);
5842 } else {
5843 fatal("unrecognized parameter: '{s}'", .{arg});
5844 }
5845 } else {
5846 try input_files.append(arg);
5847 }
5848 }
5849 }
5850
5851 if (stdin_flag) {
5852 if (input_files.items.len != 0) {
5853 fatal("cannot use --stdin with positional arguments", .{});
5854 }
5855
5856 const stdin = io.getStdIn();
5857 const source_code = readSourceFileToEndAlloc(gpa, &stdin, null) catch |err| {
5858 fatal("unable to read stdin: {}", .{err});
5859 };
5860 defer gpa.free(source_code);
5861
5862 var tree = Ast.parse(gpa, source_code, .zig) catch |err| {
5863 fatal("error parsing stdin: {}", .{err});
5864 };
5865 defer tree.deinit(gpa);
5866
5867 if (check_ast_flag) {
5868 var file: Module.File = .{
5869 .status = .never_loaded,
5870 .source_loaded = true,
5871 .zir_loaded = false,
5872 .sub_file_path = "<stdin>",
5873 .source = source_code,
5874 .stat = undefined,
5875 .tree = tree,
5876 .tree_loaded = true,
5877 .zir = undefined,
5878 .mod = undefined,
5879 .root_decl = .none,
5880 };
5881
5882 file.mod = try Package.Module.createLimited(arena, .{
5883 .root = Package.Path.cwd(),
5884 .root_src_path = file.sub_file_path,
5885 .fully_qualified_name = "root",
5886 });
5887
5888 file.zir = try AstGen.generate(gpa, file.tree);
5889 file.zir_loaded = true;
5890 defer file.zir.deinit(gpa);
5891
5892 if (file.zir.hasCompileErrors()) {
5893 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
5894 try wip_errors.init(gpa);
5895 defer wip_errors.deinit();
5896 try Compilation.addZirErrorMessages(&wip_errors, &file);
5897 var error_bundle = try wip_errors.toOwnedBundle("");
5898 defer error_bundle.deinit(gpa);
5899 error_bundle.renderToStdErr(renderOptions(color));
5900 process.exit(2);
5901 }
5902 } else if (tree.errors.len != 0) {
5903 try printAstErrorsToStderr(gpa, tree, "<stdin>", color);
5904 process.exit(2);
5905 }
5906 const formatted = try tree.render(gpa);
5907 defer gpa.free(formatted);
5908
5909 if (check_flag) {
5910 const code: u8 = @intFromBool(mem.eql(u8, formatted, source_code));
5911 process.exit(code);
5912 }
5913
5914 return io.getStdOut().writeAll(formatted);
5915 }
59165716
5917 if (input_files.items.len == 0) {5717 const target_query: std.Target.Query = .{};
5918 fatal("expected at least one source file argument", .{});5718 const resolved_target: Package.Module.ResolvedTarget = .{
5919 }5719 .result = resolveTargetQueryOrFatal(target_query),
5720 .is_native_os = true,
5721 .is_native_abi = true,
5722 };
59205723
5921 var fmt = Fmt{5724 const exe_basename = try std.zig.binNameAlloc(arena, .{
5922 .gpa = gpa,5725 .root_name = cmd_name,
5923 .arena = arena,5726 .target = resolved_target.result,
5924 .seen = Fmt.SeenMap.init(gpa),5727 .output_mode = .Exe,
5925 .any_error = false,5728 });
5926 .check_ast = check_ast_flag,5729 const emit_bin: Compilation.EmitLoc = .{
5927 .color = color,5730 .directory = null, // Use the global zig-cache.
5928 .out_buffer = std.ArrayList(u8).init(gpa),5731 .basename = exe_basename,
5929 };5732 };
5930 defer fmt.seen.deinit();
5931 defer fmt.out_buffer.deinit();
59325733
5933 // Mark any excluded files/directories as already seen,5734 const self_exe_path = introspect.findZigExePath(arena) catch |err| {
5934 // so that they are skipped later during actual processing5735 fatal("unable to find self exe path: {s}", .{@errorName(err)});
5935 for (excluded_files.items) |file_path| {5736 };
5936 const stat = fs.cwd().statFile(file_path) catch |err| switch (err) {
5937 error.FileNotFound => continue,
5938 // On Windows, statFile does not work for directories
5939 error.IsDir => dir: {
5940 var dir = try fs.cwd().openDir(file_path, .{});
5941 defer dir.close();
5942 break :dir try dir.stat();
5943 },
5944 else => |e| return e,
5945 };
5946 try fmt.seen.put(stat.inode, {});
5947 }
59485737
5949 for (input_files.items) |file_path| {5738 const optimize_mode: std.builtin.OptimizeMode = if (EnvVar.ZIG_DEBUG_CMD.isSet())
5950 try fmtPath(&fmt, file_path, check_flag, fs.cwd(), file_path);5739 .Debug
5951 }5740 else
5952 if (fmt.any_error) {5741 .ReleaseFast;
5953 process.exit(1);5742 const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
5954 }5743 const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
5955}
59565744
5957const FmtError = error{5745 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
5958 SystemResources,5746 .path = lib_dir,
5959 OperationAborted,5747 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
5960 IoPending,5748 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });
5961 BrokenPipe,
5962 Unexpected,
5963 WouldBlock,
5964 FileClosed,
5965 DestinationAddressRequired,
5966 DiskQuota,
5967 FileTooBig,
5968 InputOutput,
5969 NoSpaceLeft,
5970 AccessDenied,
5971 OutOfMemory,
5972 RenameAcrossMountPoints,
5973 ReadOnlyFileSystem,
5974 LinkQuotaExceeded,
5975 FileBusy,
5976 EndOfStream,
5977 Unseekable,
5978 NotOpenForWriting,
5979 UnsupportedEncoding,
5980 ConnectionResetByPeer,
5981 SocketNotConnected,
5982 LockViolation,
5983 NetNameDeleted,
5984 InvalidArgument,
5985} || fs.File.OpenError;
5986
5987fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
5988 fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
5989 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
5990 else => {
5991 warn("unable to format '{s}': {s}", .{ file_path, @errorName(err) });
5992 fmt.any_error = true;
5993 return;
5994 },5749 },
5750 } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
5751 fatal("unable to find zig installation directory '{s}': {s}", .{ self_exe_path, @errorName(err) });
5995 };5752 };
5996}5753 defer zig_lib_directory.handle.close();
5997
5998fn fmtPathDir(
5999 fmt: *Fmt,
6000 file_path: []const u8,
6001 check_mode: bool,
6002 parent_dir: fs.Dir,
6003 parent_sub_path: []const u8,
6004) FmtError!void {
6005 var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
6006 defer dir.close();
6007
6008 const stat = try dir.stat();
6009 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
6010
6011 var dir_it = dir.iterate();
6012 while (try dir_it.next()) |entry| {
6013 const is_dir = entry.kind == .directory;
6014
6015 if (is_dir and (mem.eql(u8, entry.name, "zig-cache") or mem.eql(u8, entry.name, "zig-out"))) continue;
6016
6017 if (is_dir or entry.kind == .file and (mem.endsWith(u8, entry.name, ".zig") or mem.endsWith(u8, entry.name, ".zon"))) {
6018 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
6019 defer fmt.gpa.free(full_path);
6020
6021 if (is_dir) {
6022 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
6023 } else {
6024 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
6025 warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) });
6026 fmt.any_error = true;
6027 return;
6028 };
6029 }
6030 }
6031 }
6032}
6033
6034fn fmtPathFile(
6035 fmt: *Fmt,
6036 file_path: []const u8,
6037 check_mode: bool,
6038 dir: fs.Dir,
6039 sub_path: []const u8,
6040) FmtError!void {
6041 const source_file = try dir.openFile(sub_path, .{});
6042 var file_closed = false;
6043 errdefer if (!file_closed) source_file.close();
6044
6045 const stat = try source_file.stat();
6046
6047 if (stat.kind == .directory)
6048 return error.IsDir;
6049
6050 const gpa = fmt.gpa;
6051 const source_code = try readSourceFileToEndAlloc(
6052 gpa,
6053 &source_file,
6054 std.math.cast(usize, stat.size) orelse return error.FileTooBig,
6055 );
6056 defer gpa.free(source_code);
6057
6058 source_file.close();
6059 file_closed = true;
60605754
6061 // Add to set after no longer possible to get error.IsDir.5755 var global_cache_directory: Compilation.Directory = l: {
6062 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;5756 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
5757 break :l .{
5758 .handle = try fs.cwd().makeOpenPath(p, .{}),
5759 .path = p,
5760 };
5761 };
5762 defer global_cache_directory.handle.close();
60635763
6064 var tree = try Ast.parse(gpa, source_code, .zig);5764 var thread_pool: ThreadPool = undefined;
6065 defer tree.deinit(gpa);5765 try thread_pool.init(.{ .allocator = gpa });
5766 defer thread_pool.deinit();
60665767
6067 if (tree.errors.len != 0) {5768 var child_argv: std.ArrayListUnmanaged([]const u8) = .{};
6068 try printAstErrorsToStderr(gpa, tree, file_path, fmt.color);5769 try child_argv.ensureUnusedCapacity(arena, args.len + 1);
6069 fmt.any_error = true;
6070 return;
6071 }
60725770
6073 if (fmt.check_ast) {5771 // We want to release all the locks before executing the child process, so we make a nice
6074 var file: Module.File = .{5772 // big block here to ensure the cleanup gets run when we extract out our argv.
6075 .status = .never_loaded,5773 {
6076 .source_loaded = true,5774 const main_mod_paths: Package.Module.CreateOptions.Paths = .{
6077 .zir_loaded = false,5775 .root = .{
6078 .sub_file_path = file_path,5776 .root_dir = zig_lib_directory,
6079 .source = source_code,5777 .sub_path = "compiler",
6080 .stat = .{
6081 .size = stat.size,
6082 .inode = stat.inode,
6083 .mtime = stat.mtime,
6084 },5778 },
6085 .tree = tree,5779 .root_src_path = root_src_path,
6086 .tree_loaded = true,
6087 .zir = undefined,
6088 .mod = undefined,
6089 .root_decl = .none,
6090 };5780 };
60915781
6092 file.mod = try Package.Module.createLimited(fmt.arena, .{5782 const config = try Compilation.Config.resolve(.{
6093 .root = Package.Path.cwd(),5783 .output_mode = .Exe,
6094 .root_src_path = file.sub_file_path,5784 .root_optimize_mode = optimize_mode,
6095 .fully_qualified_name = "root",5785 .resolved_target = resolved_target,
5786 .have_zcu = true,
5787 .emit_bin = true,
5788 .is_test = false,
6096 });5789 });
60975790
6098 if (stat.size > max_src_size)5791 const root_mod = try Package.Module.create(arena, .{
6099 return error.FileTooBig;5792 .global_cache_directory = global_cache_directory,
5793 .paths = main_mod_paths,
5794 .fully_qualified_name = "root",
5795 .cc_argv = &.{},
5796 .inherited = .{
5797 .resolved_target = resolved_target,
5798 .optimize_mode = optimize_mode,
5799 },
5800 .global = config,
5801 .parent = null,
5802 .builtin_mod = null,
5803 });
61005804
6101 file.zir = try AstGen.generate(gpa, file.tree);5805 const comp = Compilation.create(gpa, arena, .{
6102 file.zir_loaded = true;5806 .zig_lib_directory = zig_lib_directory,
6103 defer file.zir.deinit(gpa);5807 .local_cache_directory = global_cache_directory,
61045808 .global_cache_directory = global_cache_directory,
6105 if (file.zir.hasCompileErrors()) {5809 .root_name = cmd_name,
6106 var wip_errors: std.zig.ErrorBundle.Wip = undefined;5810 .config = config,
6107 try wip_errors.init(gpa);5811 .root_mod = root_mod,
6108 defer wip_errors.deinit();5812 .main_mod = root_mod,
6109 try Compilation.addZirErrorMessages(&wip_errors, &file);5813 .emit_bin = emit_bin,
6110 var error_bundle = try wip_errors.toOwnedBundle("");5814 .emit_h = null,
6111 defer error_bundle.deinit(gpa);5815 .self_exe_path = self_exe_path,
6112 error_bundle.renderToStdErr(renderOptions(fmt.color));5816 .thread_pool = &thread_pool,
6113 fmt.any_error = true;5817 .cache_mode = .whole,
6114 }5818 }) catch |err| {
6115 }5819 fatal("unable to create compilation: {s}", .{@errorName(err)});
5820 };
5821 defer comp.destroy();
61165822
6117 // As a heuristic, we make enough capacity for the same as the input source.5823 updateModule(comp, color) catch |err| switch (err) {
6118 fmt.out_buffer.shrinkRetainingCapacity(0);5824 error.SemanticAnalyzeFail => process.exit(2),
6119 try fmt.out_buffer.ensureTotalCapacity(source_code.len);5825 else => |e| return e,
5826 };
61205827
6121 try tree.renderToArrayList(&fmt.out_buffer, .{});5828 const exe_path = try global_cache_directory.join(arena, &.{comp.cache_use.whole.bin_sub_path.?});
6122 if (mem.eql(u8, fmt.out_buffer.items, source_code))5829 child_argv.appendAssumeCapacity(exe_path);
6123 return;5830 }
61245831
6125 if (check_mode) {5832 child_argv.appendSliceAssumeCapacity(args);
6126 const stdout = io.getStdOut().writer();
6127 try stdout.print("{s}\n", .{file_path});
6128 fmt.any_error = true;
6129 } else {
6130 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
6131 defer af.deinit();
61325833
6133 try af.file.writeAll(fmt.out_buffer.items);5834 if (process.can_execv) {
6134 try af.finish();5835 const err = process.execv(gpa, child_argv.items);
6135 const stdout = io.getStdOut().writer();5836 const cmd = try std.mem.join(arena, " ", child_argv.items);
6136 try stdout.print("{s}\n", .{file_path});5837 fatal("the following command failed to execve with '{s}':\n{s}", .{
5838 @errorName(err),
5839 cmd,
5840 });
6137 }5841 }
6138}
6139
6140fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void {
6141 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6142 try wip_errors.init(gpa);
6143 defer wip_errors.deinit();
61445842
6145 try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors);5843 if (!process.can_spawn) {
5844 const cmd = try std.mem.join(arena, " ", child_argv.items);
5845 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{
5846 @tagName(builtin.os.tag), cmd,
5847 });
5848 }
61465849
6147 var error_bundle = try wip_errors.toOwnedBundle("");5850 var child = std.ChildProcess.init(child_argv.items, gpa);
6148 defer error_bundle.deinit(gpa);5851 child.stdin_behavior = .Inherit;
6149 error_bundle.renderToStdErr(renderOptions(color));5852 child.stdout_behavior = .Inherit;
6150}5853 child.stderr_behavior = .Inherit;
61515854
6152pub fn putAstErrorsIntoBundle(5855 const term = try child.spawnAndWait();
6153 gpa: Allocator,5856 switch (term) {
6154 tree: Ast,5857 .Exited => |code| {
6155 path: []const u8,5858 if (code == 0) return cleanExit();
6156 wip_errors: *std.zig.ErrorBundle.Wip,5859 const cmd = try std.mem.join(arena, " ", child_argv.items);
6157) Allocator.Error!void {5860 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
6158 var file: Module.File = .{
6159 .status = .never_loaded,
6160 .source_loaded = true,
6161 .zir_loaded = false,
6162 .sub_file_path = path,
6163 .source = tree.source,
6164 .stat = .{
6165 .size = 0,
6166 .inode = 0,
6167 .mtime = 0,
6168 },5861 },
6169 .tree = tree,5862 else => {
6170 .tree_loaded = true,5863 const cmd = try std.mem.join(arena, " ", child_argv.items);
6171 .zir = undefined,5864 fatal("the following build command crashed:\n{s}", .{cmd});
6172 .mod = try Package.Module.createLimited(gpa, .{5865 },
6173 .root = Package.Path.cwd(),5866 }
6174 .root_src_path = path,
6175 .fully_qualified_name = "root",
6176 }),
6177 .root_decl = .none,
6178 };
6179 defer gpa.destroy(file.mod);
6180
6181 file.zir = try AstGen.generate(gpa, file.tree);
6182 file.zir_loaded = true;
6183 defer file.zir.deinit(gpa);
6184
6185 try Compilation.addZirErrorMessages(wip_errors, &file);
6186}5867}
61875868
6188const info_zen =5869const info_zen =
...@@ -6655,7 +6336,7 @@ fn cmdAstCheck(...@@ -6655,7 +6336,7 @@ fn cmdAstCheck(
6655 arena: Allocator,6336 arena: Allocator,
6656 args: []const []const u8,6337 args: []const []const u8,
6657) !void {6338) !void {
6658 const Zir = @import("Zir.zig");6339 const Zir = std.zig.Zir;
66596340
6660 var color: Color = .auto;6341 var color: Color = .auto;
6661 var want_output_text = false;6342 var want_output_text = false;
...@@ -6710,7 +6391,7 @@ fn cmdAstCheck(...@@ -6710,7 +6391,7 @@ fn cmdAstCheck(
67106391
6711 const stat = try f.stat();6392 const stat = try f.stat();
67126393
6713 if (stat.size > max_src_size)6394 if (stat.size > std.zig.max_src_size)
6714 return error.FileTooBig;6395 return error.FileTooBig;
67156396
6716 const source = try arena.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);6397 const source = try arena.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
...@@ -6728,7 +6409,7 @@ fn cmdAstCheck(...@@ -6728,7 +6409,7 @@ fn cmdAstCheck(
6728 };6409 };
6729 } else {6410 } else {
6730 const stdin = io.getStdIn();6411 const stdin = io.getStdIn();
6731 const source = readSourceFileToEndAlloc(arena, &stdin, null) catch |err| {6412 const source = std.zig.readSourceFileToEndAlloc(arena, stdin, null) catch |err| {
6732 fatal("unable to read stdin: {}", .{err});6413 fatal("unable to read stdin: {}", .{err});
6733 };6414 };
6734 file.sub_file_path = "<stdin>";6415 file.sub_file_path = "<stdin>";
...@@ -6758,7 +6439,7 @@ fn cmdAstCheck(...@@ -6758,7 +6439,7 @@ fn cmdAstCheck(
6758 try Compilation.addZirErrorMessages(&wip_errors, &file);6439 try Compilation.addZirErrorMessages(&wip_errors, &file);
6759 var error_bundle = try wip_errors.toOwnedBundle("");6440 var error_bundle = try wip_errors.toOwnedBundle("");
6760 defer error_bundle.deinit(gpa);6441 defer error_bundle.deinit(gpa);
6761 error_bundle.renderToStdErr(renderOptions(color));6442 error_bundle.renderToStdErr(color.renderOptions());
6762 process.exit(1);6443 process.exit(1);
6763 }6444 }
67646445
...@@ -6817,7 +6498,7 @@ fn cmdDumpZir(...@@ -6817,7 +6498,7 @@ fn cmdDumpZir(
6817 args: []const []const u8,6498 args: []const []const u8,
6818) !void {6499) !void {
6819 _ = arena;6500 _ = arena;
6820 const Zir = @import("Zir.zig");6501 const Zir = std.zig.Zir;
68216502
6822 const cache_file = args[0];6503 const cache_file = args[0];
68236504
...@@ -6877,7 +6558,7 @@ fn cmdChangelist(...@@ -6877,7 +6558,7 @@ fn cmdChangelist(
6877 args: []const []const u8,6558 args: []const []const u8,
6878) !void {6559) !void {
6879 const color: Color = .auto;6560 const color: Color = .auto;
6880 const Zir = @import("Zir.zig");6561 const Zir = std.zig.Zir;
68816562
6882 const old_source_file = args[0];6563 const old_source_file = args[0];
6883 const new_source_file = args[1];6564 const new_source_file = args[1];
...@@ -6889,7 +6570,7 @@ fn cmdChangelist(...@@ -6889,7 +6570,7 @@ fn cmdChangelist(
68896570
6890 const stat = try f.stat();6571 const stat = try f.stat();
68916572
6892 if (stat.size > max_src_size)6573 if (stat.size > std.zig.max_src_size)
6893 return error.FileTooBig;6574 return error.FileTooBig;
68946575
6895 var file: Module.File = .{6576 var file: Module.File = .{
...@@ -6938,7 +6619,7 @@ fn cmdChangelist(...@@ -6938,7 +6619,7 @@ fn cmdChangelist(
6938 try Compilation.addZirErrorMessages(&wip_errors, &file);6619 try Compilation.addZirErrorMessages(&wip_errors, &file);
6939 var error_bundle = try wip_errors.toOwnedBundle("");6620 var error_bundle = try wip_errors.toOwnedBundle("");
6940 defer error_bundle.deinit(gpa);6621 defer error_bundle.deinit(gpa);
6941 error_bundle.renderToStdErr(renderOptions(color));6622 error_bundle.renderToStdErr(color.renderOptions());
6942 process.exit(1);6623 process.exit(1);
6943 }6624 }
69446625
...@@ -6949,7 +6630,7 @@ fn cmdChangelist(...@@ -6949,7 +6630,7 @@ fn cmdChangelist(
69496630
6950 const new_stat = try new_f.stat();6631 const new_stat = try new_f.stat();
69516632
6952 if (new_stat.size > max_src_size)6633 if (new_stat.size > std.zig.max_src_size)
6953 return error.FileTooBig;6634 return error.FileTooBig;
69546635
6955 const new_source = try arena.allocSentinel(u8, @as(usize, @intCast(new_stat.size)), 0);6636 const new_source = try arena.allocSentinel(u8, @as(usize, @intCast(new_stat.size)), 0);
...@@ -6973,7 +6654,7 @@ fn cmdChangelist(...@@ -6973,7 +6654,7 @@ fn cmdChangelist(
6973 try Compilation.addZirErrorMessages(&wip_errors, &file);6654 try Compilation.addZirErrorMessages(&wip_errors, &file);
6974 var error_bundle = try wip_errors.toOwnedBundle("");6655 var error_bundle = try wip_errors.toOwnedBundle("");
6975 defer error_bundle.deinit(gpa);6656 defer error_bundle.deinit(gpa);
6976 error_bundle.renderToStdErr(renderOptions(color));6657 error_bundle.renderToStdErr(color.renderOptions());
6977 process.exit(1);6658 process.exit(1);
6978 }6659 }
69796660
...@@ -7241,23 +6922,6 @@ const ClangSearchSanitizer = struct {...@@ -7241,23 +6922,6 @@ const ClangSearchSanitizer = struct {
7241 };6922 };
7242};6923};
72436924
7244fn get_tty_conf(color: Color) std.io.tty.Config {
7245 return switch (color) {
7246 .auto => std.io.tty.detectConfig(std.io.getStdErr()),
7247 .on => .escape_codes,
7248 .off => .no_color,
7249 };
7250}
7251
7252fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions {
7253 const ttyconf = get_tty_conf(color);
7254 return .{
7255 .ttyconf = ttyconf,
7256 .include_source_line = ttyconf != .no_color,
7257 .include_reference_trace = ttyconf != .no_color,
7258 };
7259}
7260
7261fn accessLibPath(6925fn accessLibPath(
7262 test_path: *std.ArrayList(u8),6926 test_path: *std.ArrayList(u8),
7263 checked_paths: *std.ArrayList(u8),6927 checked_paths: *std.ArrayList(u8),
...@@ -7498,7 +7162,7 @@ fn cmdFetch(...@@ -7498,7 +7162,7 @@ fn cmdFetch(
74987162
7499 if (fetch.error_bundle.root_list.items.len > 0) {7163 if (fetch.error_bundle.root_list.items.len > 0) {
7500 var errors = try fetch.error_bundle.toOwnedBundle("");7164 var errors = try fetch.error_bundle.toOwnedBundle("");
7501 errors.renderToStdErr(renderOptions(color));7165 errors.renderToStdErr(color.renderOptions());
7502 process.exit(1);7166 process.exit(1);
7503 }7167 }
75047168
...@@ -7790,7 +7454,7 @@ fn loadManifest(...@@ -7790,7 +7454,7 @@ fn loadManifest(
7790 errdefer ast.deinit(gpa);7454 errdefer ast.deinit(gpa);
77917455
7792 if (ast.errors.len > 0) {7456 if (ast.errors.len > 0) {
7793 try printAstErrorsToStderr(gpa, ast, Package.Manifest.basename, options.color);7457 try std.zig.printAstErrorsToStderr(gpa, ast, Package.Manifest.basename, options.color);
7794 process.exit(2);7458 process.exit(2);
7795 }7459 }
77967460
...@@ -7807,7 +7471,7 @@ fn loadManifest(...@@ -7807,7 +7471,7 @@ fn loadManifest(
78077471
7808 var error_bundle = try wip_errors.toOwnedBundle("");7472 var error_bundle = try wip_errors.toOwnedBundle("");
7809 defer error_bundle.deinit(gpa);7473 defer error_bundle.deinit(gpa);
7810 error_bundle.renderToStdErr(renderOptions(options.color));7474 error_bundle.renderToStdErr(options.color.renderOptions());
78117475
7812 process.exit(2);7476 process.exit(2);
7813 }7477 }
src/print_zir.zig+2-2
...@@ -5,9 +5,9 @@ const assert = std.debug.assert;...@@ -5,9 +5,9 @@ const assert = std.debug.assert;
5const Ast = std.zig.Ast;5const Ast = std.zig.Ast;
6const InternPool = @import("InternPool.zig");6const InternPool = @import("InternPool.zig");
77
8const Zir = @import("Zir.zig");8const Zir = std.zig.Zir;
9const Module = @import("Module.zig");9const Module = @import("Module.zig");
10const LazySrcLoc = Module.LazySrcLoc;10const LazySrcLoc = std.zig.LazySrcLoc;
1111
12/// Write human-readable, debug formatted ZIR code to a file.12/// Write human-readable, debug formatted ZIR code to a file.
13pub fn renderAsTextToFile(13pub fn renderAsTextToFile(
src/reduce.zig deleted-413
...@@ -1,413 +0,0 @@
1const std = @import("std");
2const mem = std.mem;
3const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;
5const fatal = @import("./main.zig").fatal;
6const Ast = std.zig.Ast;
7const Walk = @import("reduce/Walk.zig");
8const AstGen = @import("AstGen.zig");
9const Zir = @import("Zir.zig");
10
11const usage =
12 \\zig reduce [options] ./checker root_source_file.zig [-- [argv]]
13 \\
14 \\root_source_file.zig is relative to --main-mod-path.
15 \\
16 \\checker:
17 \\ An executable that communicates interestingness by returning these exit codes:
18 \\ exit(0): interesting
19 \\ exit(1): unknown (infinite loop or other mishap)
20 \\ exit(other): not interesting
21 \\
22 \\options:
23 \\ --seed [integer] Override the random seed. Defaults to 0
24 \\ --skip-smoke-test Skip interestingness check smoke test
25 \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name
26 \\ deps: [dep],[dep],...
27 \\ dep: [[import=]name]
28 \\ --deps [dep],[dep],... Set dependency names for the root package
29 \\ dep: [[import=]name]
30 \\ --main-mod-path Set the directory of the root module
31 \\
32 \\argv:
33 \\ Forwarded directly to the interestingness script.
34 \\
35;
36
37const Interestingness = enum { interesting, unknown, boring };
38
39// Roadmap:
40// - add thread pool
41// - add support for parsing the module flags
42// - more fancy transformations
43// - @import inlining of modules
44// - removing statements or blocks of code
45// - replacing operands of `and` and `or` with `true` and `false`
46// - replacing if conditions with `true` and `false`
47// - reduce flags sent to the compiler
48// - integrate with the build system?
49
50pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
51 var opt_checker_path: ?[]const u8 = null;
52 var opt_root_source_file_path: ?[]const u8 = null;
53 var argv: []const []const u8 = &.{};
54 var seed: u32 = 0;
55 var skip_smoke_test = false;
56
57 {
58 var i: usize = 2; // skip over "zig" and "reduce"
59 while (i < args.len) : (i += 1) {
60 const arg = args[i];
61 if (mem.startsWith(u8, arg, "-")) {
62 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
63 const stdout = std.io.getStdOut().writer();
64 try stdout.writeAll(usage);
65 return std.process.cleanExit();
66 } else if (mem.eql(u8, arg, "--")) {
67 argv = args[i + 1 ..];
68 break;
69 } else if (mem.eql(u8, arg, "--skip-smoke-test")) {
70 skip_smoke_test = true;
71 } else if (mem.eql(u8, arg, "--main-mod-path")) {
72 @panic("TODO: implement --main-mod-path");
73 } else if (mem.eql(u8, arg, "--mod")) {
74 @panic("TODO: implement --mod");
75 } else if (mem.eql(u8, arg, "--deps")) {
76 @panic("TODO: implement --deps");
77 } else if (mem.eql(u8, arg, "--seed")) {
78 i += 1;
79 if (i >= args.len) fatal("expected 32-bit integer after {s}", .{arg});
80 const next_arg = args[i];
81 seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| {
82 fatal("unable to parse seed '{s}' as 32-bit integer: {s}", .{
83 next_arg, @errorName(err),
84 });
85 };
86 } else {
87 fatal("unrecognized parameter: '{s}'", .{arg});
88 }
89 } else if (opt_checker_path == null) {
90 opt_checker_path = arg;
91 } else if (opt_root_source_file_path == null) {
92 opt_root_source_file_path = arg;
93 } else {
94 fatal("unexpected extra parameter: '{s}'", .{arg});
95 }
96 }
97 }
98
99 const checker_path = opt_checker_path orelse
100 fatal("missing interestingness checker argument; see -h for usage", .{});
101 const root_source_file_path = opt_root_source_file_path orelse
102 fatal("missing root source file path argument; see -h for usage", .{});
103
104 var interestingness_argv: std.ArrayListUnmanaged([]const u8) = .{};
105 try interestingness_argv.ensureUnusedCapacity(arena, argv.len + 1);
106 interestingness_argv.appendAssumeCapacity(checker_path);
107 interestingness_argv.appendSliceAssumeCapacity(argv);
108
109 var rendered = std.ArrayList(u8).init(gpa);
110 defer rendered.deinit();
111
112 var astgen_input = std.ArrayList(u8).init(gpa);
113 defer astgen_input.deinit();
114
115 var tree = try parse(gpa, root_source_file_path);
116 defer {
117 gpa.free(tree.source);
118 tree.deinit(gpa);
119 }
120
121 if (!skip_smoke_test) {
122 std.debug.print("smoke testing the interestingness check...\n", .{});
123 switch (try runCheck(arena, interestingness_argv.items)) {
124 .interesting => {},
125 .boring, .unknown => |t| {
126 fatal("interestingness check returned {s} for unmodified input\n", .{
127 @tagName(t),
128 });
129 },
130 }
131 }
132
133 var fixups: Ast.Fixups = .{};
134 defer fixups.deinit(gpa);
135
136 var more_fixups: Ast.Fixups = .{};
137 defer more_fixups.deinit(gpa);
138
139 var rng = std.Random.DefaultPrng.init(seed);
140
141 // 1. Walk the AST of the source file looking for independent
142 // reductions and collecting them all into an array list.
143 // 2. Randomize the list of transformations. A future enhancement will add
144 // priority weights to the sorting but for now they are completely
145 // shuffled.
146 // 3. Apply a subset consisting of 1/2 of the transformations and check for
147 // interestingness.
148 // 4. If not interesting, half the subset size again and check again.
149 // 5. Repeat until the subset size is 1, then march the transformation
150 // index forward by 1 with each non-interesting attempt.
151 //
152 // At any point if a subset of transformations succeeds in producing an interesting
153 // result, restart the whole process, reparsing the AST and re-generating the list
154 // of all possible transformations and shuffling it again.
155
156 var transformations = std.ArrayList(Walk.Transformation).init(gpa);
157 defer transformations.deinit();
158 try Walk.findTransformations(arena, &tree, &transformations);
159 sortTransformations(transformations.items, rng.random());
160
161 fresh: while (transformations.items.len > 0) {
162 std.debug.print("found {d} possible transformations\n", .{
163 transformations.items.len,
164 });
165 var subset_size: usize = transformations.items.len;
166 var start_index: usize = 0;
167
168 while (start_index < transformations.items.len) {
169 const prev_subset_size = subset_size;
170 subset_size = @max(1, subset_size * 3 / 4);
171 if (prev_subset_size > 1 and subset_size == 1)
172 start_index = 0;
173
174 const this_set = transformations.items[start_index..][0..subset_size];
175 std.debug.print("trying {d} random transformations: ", .{subset_size});
176 for (this_set[0..@min(this_set.len, 20)]) |t| {
177 std.debug.print("{s} ", .{@tagName(t)});
178 }
179 std.debug.print("\n", .{});
180 try transformationsToFixups(gpa, arena, root_source_file_path, this_set, &fixups);
181
182 rendered.clearRetainingCapacity();
183 try tree.renderToArrayList(&rendered, fixups);
184
185 // The transformations we applied may have resulted in unused locals,
186 // in which case we would like to add the respective discards.
187 {
188 try astgen_input.resize(rendered.items.len);
189 @memcpy(astgen_input.items, rendered.items);
190 try astgen_input.append(0);
191 const source_with_null = astgen_input.items[0 .. astgen_input.items.len - 1 :0];
192 var astgen_tree = try Ast.parse(gpa, source_with_null, .zig);
193 defer astgen_tree.deinit(gpa);
194 if (astgen_tree.errors.len != 0) {
195 @panic("syntax errors occurred");
196 }
197 var zir = try AstGen.generate(gpa, astgen_tree);
198 defer zir.deinit(gpa);
199
200 if (zir.hasCompileErrors()) {
201 more_fixups.clearRetainingCapacity();
202 const payload_index = zir.extra[@intFromEnum(Zir.ExtraIndex.compile_errors)];
203 assert(payload_index != 0);
204 const header = zir.extraData(Zir.Inst.CompileErrors, payload_index);
205 var extra_index = header.end;
206 for (0..header.data.items_len) |_| {
207 const item = zir.extraData(Zir.Inst.CompileErrors.Item, extra_index);
208 extra_index = item.end;
209 const msg = zir.nullTerminatedString(item.data.msg);
210 if (mem.eql(u8, msg, "unused local constant") or
211 mem.eql(u8, msg, "unused local variable") or
212 mem.eql(u8, msg, "unused function parameter") or
213 mem.eql(u8, msg, "unused capture"))
214 {
215 const ident_token = item.data.token;
216 try more_fixups.unused_var_decls.put(gpa, ident_token, {});
217 } else {
218 std.debug.print("found other ZIR error: '{s}'\n", .{msg});
219 }
220 }
221 if (more_fixups.count() != 0) {
222 rendered.clearRetainingCapacity();
223 try astgen_tree.renderToArrayList(&rendered, more_fixups);
224 }
225 }
226 }
227
228 try std.fs.cwd().writeFile(root_source_file_path, rendered.items);
229 // std.debug.print("trying this code:\n{s}\n", .{rendered.items});
230
231 const interestingness = try runCheck(arena, interestingness_argv.items);
232 std.debug.print("{d} random transformations: {s}. {d}/{d}\n", .{
233 subset_size, @tagName(interestingness), start_index, transformations.items.len,
234 });
235 switch (interestingness) {
236 .interesting => {
237 const new_tree = try parse(gpa, root_source_file_path);
238 gpa.free(tree.source);
239 tree.deinit(gpa);
240 tree = new_tree;
241
242 try Walk.findTransformations(arena, &tree, &transformations);
243 sortTransformations(transformations.items, rng.random());
244
245 continue :fresh;
246 },
247 .unknown, .boring => {
248 // Continue to try the next set of transformations.
249 // If we tested only one transformation, move on to the next one.
250 if (subset_size == 1) {
251 start_index += 1;
252 } else {
253 start_index += subset_size;
254 if (start_index + subset_size > transformations.items.len) {
255 start_index = 0;
256 }
257 }
258 },
259 }
260 }
261 std.debug.print("all {d} remaining transformations are uninteresting\n", .{
262 transformations.items.len,
263 });
264
265 // Revert the source back to not be transformed.
266 fixups.clearRetainingCapacity();
267 rendered.clearRetainingCapacity();
268 try tree.renderToArrayList(&rendered, fixups);
269 try std.fs.cwd().writeFile(root_source_file_path, rendered.items);
270
271 return std.process.cleanExit();
272 }
273 std.debug.print("no more transformations found\n", .{});
274 return std.process.cleanExit();
275}
276
277fn sortTransformations(transformations: []Walk.Transformation, rng: std.Random) void {
278 rng.shuffle(Walk.Transformation, transformations);
279 // Stable sort based on priority to keep randomness as the secondary sort.
280 // TODO: introduce transformation priorities
281 // std.mem.sort(transformations);
282}
283
284fn termToInteresting(term: std.process.Child.Term) Interestingness {
285 return switch (term) {
286 .Exited => |code| switch (code) {
287 0 => .interesting,
288 1 => .unknown,
289 else => .boring,
290 },
291 else => b: {
292 std.debug.print("interestingness check aborted unexpectedly\n", .{});
293 break :b .boring;
294 },
295 };
296}
297
298fn runCheck(arena: std.mem.Allocator, argv: []const []const u8) !Interestingness {
299 const result = try std.process.Child.run(.{
300 .allocator = arena,
301 .argv = argv,
302 });
303 if (result.stderr.len != 0)
304 std.debug.print("{s}", .{result.stderr});
305 return termToInteresting(result.term);
306}
307
308fn transformationsToFixups(
309 gpa: Allocator,
310 arena: Allocator,
311 root_source_file_path: []const u8,
312 transforms: []const Walk.Transformation,
313 fixups: *Ast.Fixups,
314) !void {
315 fixups.clearRetainingCapacity();
316
317 for (transforms) |t| switch (t) {
318 .gut_function => |fn_decl_node| {
319 try fixups.gut_functions.put(gpa, fn_decl_node, {});
320 },
321 .delete_node => |decl_node| {
322 try fixups.omit_nodes.put(gpa, decl_node, {});
323 },
324 .delete_var_decl => |delete_var_decl| {
325 try fixups.omit_nodes.put(gpa, delete_var_decl.var_decl_node, {});
326 for (delete_var_decl.references.items) |ident_node| {
327 try fixups.replace_nodes_with_string.put(gpa, ident_node, "undefined");
328 }
329 },
330 .replace_with_undef => |node| {
331 try fixups.replace_nodes_with_string.put(gpa, node, "undefined");
332 },
333 .replace_with_true => |node| {
334 try fixups.replace_nodes_with_string.put(gpa, node, "true");
335 },
336 .replace_with_false => |node| {
337 try fixups.replace_nodes_with_string.put(gpa, node, "false");
338 },
339 .replace_node => |r| {
340 try fixups.replace_nodes_with_node.put(gpa, r.to_replace, r.replacement);
341 },
342 .inline_imported_file => |inline_imported_file| {
343 const full_imported_path = try std.fs.path.join(gpa, &.{
344 std.fs.path.dirname(root_source_file_path) orelse ".",
345 inline_imported_file.imported_string,
346 });
347 defer gpa.free(full_imported_path);
348 var other_file_ast = try parse(gpa, full_imported_path);
349 defer {
350 gpa.free(other_file_ast.source);
351 other_file_ast.deinit(gpa);
352 }
353
354 var inlined_fixups: Ast.Fixups = .{};
355 defer inlined_fixups.deinit(gpa);
356 if (std.fs.path.dirname(inline_imported_file.imported_string)) |dirname| {
357 inlined_fixups.rebase_imported_paths = dirname;
358 }
359 for (inline_imported_file.in_scope_names.keys()) |name| {
360 // This name needs to be mangled in order to not cause an
361 // ambiguous reference error.
362 var i: u32 = 2;
363 const mangled = while (true) : (i += 1) {
364 const mangled = try std.fmt.allocPrint(gpa, "{s}{d}", .{ name, i });
365 if (!inline_imported_file.in_scope_names.contains(mangled))
366 break mangled;
367 gpa.free(mangled);
368 };
369 try inlined_fixups.rename_identifiers.put(gpa, name, mangled);
370 }
371 defer {
372 for (inlined_fixups.rename_identifiers.values()) |v| {
373 gpa.free(v);
374 }
375 }
376
377 var other_source = std.ArrayList(u8).init(gpa);
378 defer other_source.deinit();
379 try other_source.appendSlice("struct {\n");
380 try other_file_ast.renderToArrayList(&other_source, inlined_fixups);
381 try other_source.appendSlice("}");
382
383 try fixups.replace_nodes_with_string.put(
384 gpa,
385 inline_imported_file.builtin_call_node,
386 try arena.dupe(u8, other_source.items),
387 );
388 },
389 };
390}
391
392fn parse(gpa: Allocator, file_path: []const u8) !Ast {
393 const source_code = std.fs.cwd().readFileAllocOptions(
394 gpa,
395 file_path,
396 std.math.maxInt(u32),
397 null,
398 1,
399 0,
400 ) catch |err| {
401 fatal("unable to open '{s}': {s}", .{ file_path, @errorName(err) });
402 };
403 errdefer gpa.free(source_code);
404
405 var tree = try Ast.parse(gpa, source_code, .zig);
406 errdefer tree.deinit(gpa);
407
408 if (tree.errors.len != 0) {
409 @panic("syntax errors occurred");
410 }
411
412 return tree;
413}
src/reduce/Walk.zig deleted-1102
...@@ -1,1102 +0,0 @@
1const std = @import("std");
2const Ast = std.zig.Ast;
3const Walk = @This();
4const assert = std.debug.assert;
5const BuiltinFn = std.zig.BuiltinFn;
6
7ast: *const Ast,
8transformations: *std.ArrayList(Transformation),
9unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index),
10in_scope_names: std.StringArrayHashMapUnmanaged(u32),
11replace_names: std.StringArrayHashMapUnmanaged(u32),
12gpa: std.mem.Allocator,
13arena: std.mem.Allocator,
14
15pub const Transformation = union(enum) {
16 /// Replace the fn decl AST Node with one whose body is only `@trap()` with
17 /// discarded parameters.
18 gut_function: Ast.Node.Index,
19 /// Omit a global declaration.
20 delete_node: Ast.Node.Index,
21 /// Delete a local variable declaration and replace all of its references
22 /// with `undefined`.
23 delete_var_decl: struct {
24 var_decl_node: Ast.Node.Index,
25 /// Identifier nodes that reference the variable.
26 references: std.ArrayListUnmanaged(Ast.Node.Index),
27 },
28 /// Replace an expression with `undefined`.
29 replace_with_undef: Ast.Node.Index,
30 /// Replace an expression with `true`.
31 replace_with_true: Ast.Node.Index,
32 /// Replace an expression with `false`.
33 replace_with_false: Ast.Node.Index,
34 /// Replace a node with another node.
35 replace_node: struct {
36 to_replace: Ast.Node.Index,
37 replacement: Ast.Node.Index,
38 },
39 /// Replace an `@import` with the imported file contents wrapped in a struct.
40 inline_imported_file: InlineImportedFile,
41
42 pub const InlineImportedFile = struct {
43 builtin_call_node: Ast.Node.Index,
44 imported_string: []const u8,
45 /// Identifier names that must be renamed in the inlined code or else
46 /// will cause ambiguous reference errors.
47 in_scope_names: std.StringArrayHashMapUnmanaged(void),
48 };
49};
50
51pub const Error = error{OutOfMemory};
52
53/// The result will be priority shuffled.
54pub fn findTransformations(
55 arena: std.mem.Allocator,
56 ast: *const Ast,
57 transformations: *std.ArrayList(Transformation),
58) !void {
59 transformations.clearRetainingCapacity();
60
61 var walk: Walk = .{
62 .ast = ast,
63 .transformations = transformations,
64 .gpa = transformations.allocator,
65 .arena = arena,
66 .unreferenced_globals = .{},
67 .in_scope_names = .{},
68 .replace_names = .{},
69 };
70 defer {
71 walk.unreferenced_globals.deinit(walk.gpa);
72 walk.in_scope_names.deinit(walk.gpa);
73 walk.replace_names.deinit(walk.gpa);
74 }
75
76 try walkMembers(&walk, walk.ast.rootDecls());
77
78 const unreferenced_globals = walk.unreferenced_globals.values();
79 try transformations.ensureUnusedCapacity(unreferenced_globals.len);
80 for (unreferenced_globals) |node| {
81 transformations.appendAssumeCapacity(.{ .delete_node = node });
82 }
83}
84
85fn walkMembers(w: *Walk, members: []const Ast.Node.Index) Error!void {
86 // First we scan for globals so that we can delete them while walking.
87 try scanDecls(w, members, .add);
88
89 for (members) |member| {
90 try walkMember(w, member);
91 }
92
93 try scanDecls(w, members, .remove);
94}
95
96const ScanDeclsAction = enum { add, remove };
97
98fn scanDecls(w: *Walk, members: []const Ast.Node.Index, action: ScanDeclsAction) Error!void {
99 const ast = w.ast;
100 const gpa = w.gpa;
101 const node_tags = ast.nodes.items(.tag);
102 const main_tokens = ast.nodes.items(.main_token);
103 const token_tags = ast.tokens.items(.tag);
104
105 for (members) |member_node| {
106 const name_token = switch (node_tags[member_node]) {
107 .global_var_decl,
108 .local_var_decl,
109 .simple_var_decl,
110 .aligned_var_decl,
111 => main_tokens[member_node] + 1,
112
113 .fn_proto_simple,
114 .fn_proto_multi,
115 .fn_proto_one,
116 .fn_proto,
117 .fn_decl,
118 => main_tokens[member_node] + 1,
119
120 else => continue,
121 };
122
123 assert(token_tags[name_token] == .identifier);
124 const name_bytes = ast.tokenSlice(name_token);
125
126 switch (action) {
127 .add => {
128 try w.unreferenced_globals.put(gpa, name_bytes, member_node);
129
130 const gop = try w.in_scope_names.getOrPut(gpa, name_bytes);
131 if (!gop.found_existing) gop.value_ptr.* = 0;
132 gop.value_ptr.* += 1;
133 },
134 .remove => {
135 const entry = w.in_scope_names.getEntry(name_bytes).?;
136 if (entry.value_ptr.* <= 1) {
137 assert(w.in_scope_names.swapRemove(name_bytes));
138 } else {
139 entry.value_ptr.* -= 1;
140 }
141 },
142 }
143 }
144}
145
146fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {
147 const ast = w.ast;
148 const datas = ast.nodes.items(.data);
149 switch (ast.nodes.items(.tag)[decl]) {
150 .fn_decl => {
151 const fn_proto = datas[decl].lhs;
152 try walkExpression(w, fn_proto);
153 const body_node = datas[decl].rhs;
154 if (!isFnBodyGutted(ast, body_node)) {
155 w.replace_names.clearRetainingCapacity();
156 try w.transformations.append(.{ .gut_function = decl });
157 try walkExpression(w, body_node);
158 }
159 },
160 .fn_proto_simple,
161 .fn_proto_multi,
162 .fn_proto_one,
163 .fn_proto,
164 => {
165 try walkExpression(w, decl);
166 },
167
168 .@"usingnamespace" => {
169 try w.transformations.append(.{ .delete_node = decl });
170 const expr = datas[decl].lhs;
171 try walkExpression(w, expr);
172 },
173
174 .global_var_decl,
175 .local_var_decl,
176 .simple_var_decl,
177 .aligned_var_decl,
178 => try walkGlobalVarDecl(w, decl, ast.fullVarDecl(decl).?),
179
180 .test_decl => {
181 try w.transformations.append(.{ .delete_node = decl });
182 try walkExpression(w, datas[decl].rhs);
183 },
184
185 .container_field_init,
186 .container_field_align,
187 .container_field,
188 => {
189 try w.transformations.append(.{ .delete_node = decl });
190 try walkContainerField(w, ast.fullContainerField(decl).?);
191 },
192
193 .@"comptime" => {
194 try w.transformations.append(.{ .delete_node = decl });
195 try walkExpression(w, decl);
196 },
197
198 .root => unreachable,
199 else => unreachable,
200 }
201}
202
203fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
204 const ast = w.ast;
205 const token_tags = ast.tokens.items(.tag);
206 const main_tokens = ast.nodes.items(.main_token);
207 const node_tags = ast.nodes.items(.tag);
208 const datas = ast.nodes.items(.data);
209 switch (node_tags[node]) {
210 .identifier => {
211 const name_ident = main_tokens[node];
212 assert(token_tags[name_ident] == .identifier);
213 const name_bytes = ast.tokenSlice(name_ident);
214 _ = w.unreferenced_globals.swapRemove(name_bytes);
215 if (w.replace_names.get(name_bytes)) |index| {
216 try w.transformations.items[index].delete_var_decl.references.append(w.arena, node);
217 }
218 },
219
220 .number_literal,
221 .char_literal,
222 .unreachable_literal,
223 .anyframe_literal,
224 .string_literal,
225 => {},
226
227 .multiline_string_literal => {},
228
229 .error_value => {},
230
231 .block_two,
232 .block_two_semicolon,
233 => {
234 const statements = [2]Ast.Node.Index{ datas[node].lhs, datas[node].rhs };
235 if (datas[node].lhs == 0) {
236 return walkBlock(w, node, statements[0..0]);
237 } else if (datas[node].rhs == 0) {
238 return walkBlock(w, node, statements[0..1]);
239 } else {
240 return walkBlock(w, node, statements[0..2]);
241 }
242 },
243 .block,
244 .block_semicolon,
245 => {
246 const statements = ast.extra_data[datas[node].lhs..datas[node].rhs];
247 return walkBlock(w, node, statements);
248 },
249
250 .@"errdefer" => {
251 const expr = datas[node].rhs;
252 return walkExpression(w, expr);
253 },
254
255 .@"defer" => {
256 const expr = datas[node].rhs;
257 return walkExpression(w, expr);
258 },
259 .@"comptime", .@"nosuspend" => {
260 const block = datas[node].lhs;
261 return walkExpression(w, block);
262 },
263
264 .@"suspend" => {
265 const body = datas[node].lhs;
266 return walkExpression(w, body);
267 },
268
269 .@"catch" => {
270 try walkExpression(w, datas[node].lhs); // target
271 try walkExpression(w, datas[node].rhs); // fallback
272 },
273
274 .field_access => {
275 const field_access = datas[node];
276 try walkExpression(w, field_access.lhs);
277 },
278
279 .error_union,
280 .switch_range,
281 => {
282 const infix = datas[node];
283 try walkExpression(w, infix.lhs);
284 return walkExpression(w, infix.rhs);
285 },
286 .for_range => {
287 const infix = datas[node];
288 try walkExpression(w, infix.lhs);
289 if (infix.rhs != 0) {
290 return walkExpression(w, infix.rhs);
291 }
292 },
293
294 .add,
295 .add_wrap,
296 .add_sat,
297 .array_cat,
298 .array_mult,
299 .assign,
300 .assign_bit_and,
301 .assign_bit_or,
302 .assign_shl,
303 .assign_shl_sat,
304 .assign_shr,
305 .assign_bit_xor,
306 .assign_div,
307 .assign_sub,
308 .assign_sub_wrap,
309 .assign_sub_sat,
310 .assign_mod,
311 .assign_add,
312 .assign_add_wrap,
313 .assign_add_sat,
314 .assign_mul,
315 .assign_mul_wrap,
316 .assign_mul_sat,
317 .bang_equal,
318 .bit_and,
319 .bit_or,
320 .shl,
321 .shl_sat,
322 .shr,
323 .bit_xor,
324 .bool_and,
325 .bool_or,
326 .div,
327 .equal_equal,
328 .greater_or_equal,
329 .greater_than,
330 .less_or_equal,
331 .less_than,
332 .merge_error_sets,
333 .mod,
334 .mul,
335 .mul_wrap,
336 .mul_sat,
337 .sub,
338 .sub_wrap,
339 .sub_sat,
340 .@"orelse",
341 => {
342 const infix = datas[node];
343 try walkExpression(w, infix.lhs);
344 try walkExpression(w, infix.rhs);
345 },
346
347 .assign_destructure => {
348 const lhs_count = ast.extra_data[datas[node].lhs];
349 assert(lhs_count > 1);
350 const lhs_exprs = ast.extra_data[datas[node].lhs + 1 ..][0..lhs_count];
351 const rhs = datas[node].rhs;
352
353 for (lhs_exprs) |lhs_node| {
354 switch (node_tags[lhs_node]) {
355 .global_var_decl,
356 .local_var_decl,
357 .simple_var_decl,
358 .aligned_var_decl,
359 => try walkLocalVarDecl(w, ast.fullVarDecl(lhs_node).?),
360
361 else => try walkExpression(w, lhs_node),
362 }
363 }
364 return walkExpression(w, rhs);
365 },
366
367 .bit_not,
368 .bool_not,
369 .negation,
370 .negation_wrap,
371 .optional_type,
372 .address_of,
373 => {
374 return walkExpression(w, datas[node].lhs);
375 },
376
377 .@"try",
378 .@"resume",
379 .@"await",
380 => {
381 return walkExpression(w, datas[node].lhs);
382 },
383
384 .array_type,
385 .array_type_sentinel,
386 => {},
387
388 .ptr_type_aligned,
389 .ptr_type_sentinel,
390 .ptr_type,
391 .ptr_type_bit_range,
392 => {},
393
394 .array_init_one,
395 .array_init_one_comma,
396 .array_init_dot_two,
397 .array_init_dot_two_comma,
398 .array_init_dot,
399 .array_init_dot_comma,
400 .array_init,
401 .array_init_comma,
402 => {
403 var elements: [2]Ast.Node.Index = undefined;
404 return walkArrayInit(w, ast.fullArrayInit(&elements, node).?);
405 },
406
407 .struct_init_one,
408 .struct_init_one_comma,
409 .struct_init_dot_two,
410 .struct_init_dot_two_comma,
411 .struct_init_dot,
412 .struct_init_dot_comma,
413 .struct_init,
414 .struct_init_comma,
415 => {
416 var buf: [2]Ast.Node.Index = undefined;
417 return walkStructInit(w, node, ast.fullStructInit(&buf, node).?);
418 },
419
420 .call_one,
421 .call_one_comma,
422 .async_call_one,
423 .async_call_one_comma,
424 .call,
425 .call_comma,
426 .async_call,
427 .async_call_comma,
428 => {
429 var buf: [1]Ast.Node.Index = undefined;
430 return walkCall(w, ast.fullCall(&buf, node).?);
431 },
432
433 .array_access => {
434 const suffix = datas[node];
435 try walkExpression(w, suffix.lhs);
436 try walkExpression(w, suffix.rhs);
437 },
438
439 .slice_open, .slice, .slice_sentinel => return walkSlice(w, node, ast.fullSlice(node).?),
440
441 .deref => {
442 try walkExpression(w, datas[node].lhs);
443 },
444
445 .unwrap_optional => {
446 try walkExpression(w, datas[node].lhs);
447 },
448
449 .@"break" => {
450 const label_token = datas[node].lhs;
451 const target = datas[node].rhs;
452 if (label_token == 0 and target == 0) {
453 // no expressions
454 } else if (label_token == 0 and target != 0) {
455 try walkExpression(w, target);
456 } else if (label_token != 0 and target == 0) {
457 try walkIdentifier(w, label_token);
458 } else if (label_token != 0 and target != 0) {
459 try walkExpression(w, target);
460 }
461 },
462
463 .@"continue" => {
464 const label = datas[node].lhs;
465 if (label != 0) {
466 return walkIdentifier(w, label); // label
467 }
468 },
469
470 .@"return" => {
471 if (datas[node].lhs != 0) {
472 try walkExpression(w, datas[node].lhs);
473 }
474 },
475
476 .grouped_expression => {
477 try walkExpression(w, datas[node].lhs);
478 },
479
480 .container_decl,
481 .container_decl_trailing,
482 .container_decl_arg,
483 .container_decl_arg_trailing,
484 .container_decl_two,
485 .container_decl_two_trailing,
486 .tagged_union,
487 .tagged_union_trailing,
488 .tagged_union_enum_tag,
489 .tagged_union_enum_tag_trailing,
490 .tagged_union_two,
491 .tagged_union_two_trailing,
492 => {
493 var buf: [2]Ast.Node.Index = undefined;
494 return walkContainerDecl(w, node, ast.fullContainerDecl(&buf, node).?);
495 },
496
497 .error_set_decl => {
498 const error_token = main_tokens[node];
499 const lbrace = error_token + 1;
500 const rbrace = datas[node].rhs;
501
502 var i = lbrace + 1;
503 while (i < rbrace) : (i += 1) {
504 switch (token_tags[i]) {
505 .doc_comment => unreachable, // TODO
506 .identifier => try walkIdentifier(w, i),
507 .comma => {},
508 else => unreachable,
509 }
510 }
511 },
512
513 .builtin_call_two, .builtin_call_two_comma => {
514 if (datas[node].lhs == 0) {
515 return walkBuiltinCall(w, node, &.{});
516 } else if (datas[node].rhs == 0) {
517 return walkBuiltinCall(w, node, &.{datas[node].lhs});
518 } else {
519 return walkBuiltinCall(w, node, &.{ datas[node].lhs, datas[node].rhs });
520 }
521 },
522 .builtin_call, .builtin_call_comma => {
523 const params = ast.extra_data[datas[node].lhs..datas[node].rhs];
524 return walkBuiltinCall(w, node, params);
525 },
526
527 .fn_proto_simple,
528 .fn_proto_multi,
529 .fn_proto_one,
530 .fn_proto,
531 => {
532 var buf: [1]Ast.Node.Index = undefined;
533 return walkFnProto(w, ast.fullFnProto(&buf, node).?);
534 },
535
536 .anyframe_type => {
537 if (datas[node].rhs != 0) {
538 return walkExpression(w, datas[node].rhs);
539 }
540 },
541
542 .@"switch",
543 .switch_comma,
544 => {
545 const condition = datas[node].lhs;
546 const extra = ast.extraData(datas[node].rhs, Ast.Node.SubRange);
547 const cases = ast.extra_data[extra.start..extra.end];
548
549 try walkExpression(w, condition); // condition expression
550 try walkExpressions(w, cases);
551 },
552
553 .switch_case_one,
554 .switch_case_inline_one,
555 .switch_case,
556 .switch_case_inline,
557 => return walkSwitchCase(w, ast.fullSwitchCase(node).?),
558
559 .while_simple,
560 .while_cont,
561 .@"while",
562 => return walkWhile(w, node, ast.fullWhile(node).?),
563
564 .for_simple,
565 .@"for",
566 => return walkFor(w, ast.fullFor(node).?),
567
568 .if_simple,
569 .@"if",
570 => return walkIf(w, node, ast.fullIf(node).?),
571
572 .asm_simple,
573 .@"asm",
574 => return walkAsm(w, ast.fullAsm(node).?),
575
576 .enum_literal => {
577 return walkIdentifier(w, main_tokens[node]); // name
578 },
579
580 .fn_decl => unreachable,
581 .container_field => unreachable,
582 .container_field_init => unreachable,
583 .container_field_align => unreachable,
584 .root => unreachable,
585 .global_var_decl => unreachable,
586 .local_var_decl => unreachable,
587 .simple_var_decl => unreachable,
588 .aligned_var_decl => unreachable,
589 .@"usingnamespace" => unreachable,
590 .test_decl => unreachable,
591 .asm_output => unreachable,
592 .asm_input => unreachable,
593 }
594}
595
596fn walkGlobalVarDecl(w: *Walk, decl_node: Ast.Node.Index, var_decl: Ast.full.VarDecl) Error!void {
597 _ = decl_node;
598
599 if (var_decl.ast.type_node != 0) {
600 try walkExpression(w, var_decl.ast.type_node);
601 }
602
603 if (var_decl.ast.align_node != 0) {
604 try walkExpression(w, var_decl.ast.align_node);
605 }
606
607 if (var_decl.ast.addrspace_node != 0) {
608 try walkExpression(w, var_decl.ast.addrspace_node);
609 }
610
611 if (var_decl.ast.section_node != 0) {
612 try walkExpression(w, var_decl.ast.section_node);
613 }
614
615 if (var_decl.ast.init_node != 0) {
616 if (!isUndefinedIdent(w.ast, var_decl.ast.init_node)) {
617 try w.transformations.append(.{ .replace_with_undef = var_decl.ast.init_node });
618 }
619 try walkExpression(w, var_decl.ast.init_node);
620 }
621}
622
623fn walkLocalVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void {
624 try walkIdentifierNew(w, var_decl.ast.mut_token + 1); // name
625
626 if (var_decl.ast.type_node != 0) {
627 try walkExpression(w, var_decl.ast.type_node);
628 }
629
630 if (var_decl.ast.align_node != 0) {
631 try walkExpression(w, var_decl.ast.align_node);
632 }
633
634 if (var_decl.ast.addrspace_node != 0) {
635 try walkExpression(w, var_decl.ast.addrspace_node);
636 }
637
638 if (var_decl.ast.section_node != 0) {
639 try walkExpression(w, var_decl.ast.section_node);
640 }
641
642 if (var_decl.ast.init_node != 0) {
643 if (!isUndefinedIdent(w.ast, var_decl.ast.init_node)) {
644 try w.transformations.append(.{ .replace_with_undef = var_decl.ast.init_node });
645 }
646 try walkExpression(w, var_decl.ast.init_node);
647 }
648}
649
650fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void {
651 if (field.ast.type_expr != 0) {
652 try walkExpression(w, field.ast.type_expr); // type
653 }
654 if (field.ast.align_expr != 0) {
655 try walkExpression(w, field.ast.align_expr); // alignment
656 }
657 if (field.ast.value_expr != 0) {
658 try walkExpression(w, field.ast.value_expr); // value
659 }
660}
661
662fn walkBlock(
663 w: *Walk,
664 block_node: Ast.Node.Index,
665 statements: []const Ast.Node.Index,
666) Error!void {
667 _ = block_node;
668 const ast = w.ast;
669 const node_tags = ast.nodes.items(.tag);
670
671 for (statements) |stmt| {
672 switch (node_tags[stmt]) {
673 .global_var_decl,
674 .local_var_decl,
675 .simple_var_decl,
676 .aligned_var_decl,
677 => {
678 const var_decl = ast.fullVarDecl(stmt).?;
679 if (var_decl.ast.init_node != 0 and
680 isUndefinedIdent(w.ast, var_decl.ast.init_node))
681 {
682 try w.transformations.append(.{ .delete_var_decl = .{
683 .var_decl_node = stmt,
684 .references = .{},
685 } });
686 const name_tok = var_decl.ast.mut_token + 1;
687 const name_bytes = ast.tokenSlice(name_tok);
688 try w.replace_names.put(w.gpa, name_bytes, @intCast(w.transformations.items.len - 1));
689 } else {
690 try walkLocalVarDecl(w, var_decl);
691 }
692 },
693
694 else => {
695 switch (categorizeStmt(ast, stmt)) {
696 // Don't try to remove `_ = foo;` discards; those are handled separately.
697 .discard_identifier => {},
698 // definitely try to remove `_ = undefined;` though.
699 .discard_undefined, .trap_call, .other => {
700 try w.transformations.append(.{ .delete_node = stmt });
701 },
702 }
703 try walkExpression(w, stmt);
704 },
705 }
706 }
707}
708
709fn walkArrayType(w: *Walk, array_type: Ast.full.ArrayType) Error!void {
710 try walkExpression(w, array_type.ast.elem_count);
711 if (array_type.ast.sentinel != 0) {
712 try walkExpression(w, array_type.ast.sentinel);
713 }
714 return walkExpression(w, array_type.ast.elem_type);
715}
716
717fn walkArrayInit(w: *Walk, array_init: Ast.full.ArrayInit) Error!void {
718 if (array_init.ast.type_expr != 0) {
719 try walkExpression(w, array_init.ast.type_expr); // T
720 }
721 for (array_init.ast.elements) |elem_init| {
722 try walkExpression(w, elem_init);
723 }
724}
725
726fn walkStructInit(
727 w: *Walk,
728 struct_node: Ast.Node.Index,
729 struct_init: Ast.full.StructInit,
730) Error!void {
731 _ = struct_node;
732 if (struct_init.ast.type_expr != 0) {
733 try walkExpression(w, struct_init.ast.type_expr); // T
734 }
735 for (struct_init.ast.fields) |field_init| {
736 try walkExpression(w, field_init);
737 }
738}
739
740fn walkCall(w: *Walk, call: Ast.full.Call) Error!void {
741 try walkExpression(w, call.ast.fn_expr);
742 try walkParamList(w, call.ast.params);
743}
744
745fn walkSlice(
746 w: *Walk,
747 slice_node: Ast.Node.Index,
748 slice: Ast.full.Slice,
749) Error!void {
750 _ = slice_node;
751 try walkExpression(w, slice.ast.sliced);
752 try walkExpression(w, slice.ast.start);
753 if (slice.ast.end != 0) {
754 try walkExpression(w, slice.ast.end);
755 }
756 if (slice.ast.sentinel != 0) {
757 try walkExpression(w, slice.ast.sentinel);
758 }
759}
760
761fn walkIdentifier(w: *Walk, name_ident: Ast.TokenIndex) Error!void {
762 const ast = w.ast;
763 const token_tags = ast.tokens.items(.tag);
764 assert(token_tags[name_ident] == .identifier);
765 const name_bytes = ast.tokenSlice(name_ident);
766 _ = w.unreferenced_globals.swapRemove(name_bytes);
767}
768
769fn walkIdentifierNew(w: *Walk, name_ident: Ast.TokenIndex) Error!void {
770 _ = w;
771 _ = name_ident;
772}
773
774fn walkContainerDecl(
775 w: *Walk,
776 container_decl_node: Ast.Node.Index,
777 container_decl: Ast.full.ContainerDecl,
778) Error!void {
779 _ = container_decl_node;
780 if (container_decl.ast.arg != 0) {
781 try walkExpression(w, container_decl.ast.arg);
782 }
783 try walkMembers(w, container_decl.ast.members);
784}
785
786fn walkBuiltinCall(
787 w: *Walk,
788 call_node: Ast.Node.Index,
789 params: []const Ast.Node.Index,
790) Error!void {
791 const ast = w.ast;
792 const main_tokens = ast.nodes.items(.main_token);
793 const builtin_token = main_tokens[call_node];
794 const builtin_name = ast.tokenSlice(builtin_token);
795 const info = BuiltinFn.list.get(builtin_name).?;
796 switch (info.tag) {
797 .import => {
798 const operand_node = params[0];
799 const str_lit_token = main_tokens[operand_node];
800 const token_bytes = ast.tokenSlice(str_lit_token);
801 if (std.mem.endsWith(u8, token_bytes, ".zig\"")) {
802 const imported_string = std.zig.string_literal.parseAlloc(w.arena, token_bytes) catch
803 unreachable;
804 try w.transformations.append(.{ .inline_imported_file = .{
805 .builtin_call_node = call_node,
806 .imported_string = imported_string,
807 .in_scope_names = try std.StringArrayHashMapUnmanaged(void).init(
808 w.arena,
809 w.in_scope_names.keys(),
810 &.{},
811 ),
812 } });
813 }
814 },
815 else => {},
816 }
817 for (params) |param_node| {
818 try walkExpression(w, param_node);
819 }
820}
821
822fn walkFnProto(w: *Walk, fn_proto: Ast.full.FnProto) Error!void {
823 const ast = w.ast;
824
825 {
826 var it = fn_proto.iterate(ast);
827 while (it.next()) |param| {
828 if (param.type_expr != 0) {
829 try walkExpression(w, param.type_expr);
830 }
831 }
832 }
833
834 if (fn_proto.ast.align_expr != 0) {
835 try walkExpression(w, fn_proto.ast.align_expr);
836 }
837
838 if (fn_proto.ast.addrspace_expr != 0) {
839 try walkExpression(w, fn_proto.ast.addrspace_expr);
840 }
841
842 if (fn_proto.ast.section_expr != 0) {
843 try walkExpression(w, fn_proto.ast.section_expr);
844 }
845
846 if (fn_proto.ast.callconv_expr != 0) {
847 try walkExpression(w, fn_proto.ast.callconv_expr);
848 }
849
850 try walkExpression(w, fn_proto.ast.return_type);
851}
852
853fn walkExpressions(w: *Walk, expressions: []const Ast.Node.Index) Error!void {
854 for (expressions) |expression| {
855 try walkExpression(w, expression);
856 }
857}
858
859fn walkSwitchCase(w: *Walk, switch_case: Ast.full.SwitchCase) Error!void {
860 for (switch_case.ast.values) |value_expr| {
861 try walkExpression(w, value_expr);
862 }
863 try walkExpression(w, switch_case.ast.target_expr);
864}
865
866fn walkWhile(w: *Walk, node_index: Ast.Node.Index, while_node: Ast.full.While) Error!void {
867 assert(while_node.ast.cond_expr != 0);
868 assert(while_node.ast.then_expr != 0);
869
870 // Perform these transformations in this priority order:
871 // 1. If the `else` expression is missing or an empty block, replace the condition with `if (true)` if it is not already.
872 // 2. If the `then` block is empty, replace the condition with `if (false)` if it is not already.
873 // 3. If the condition is `if (true)`, replace the `if` expression with the contents of the `then` expression.
874 // 4. If the condition is `if (false)`, replace the `if` expression with the contents of the `else` expression.
875 if (!isTrueIdent(w.ast, while_node.ast.cond_expr) and
876 (while_node.ast.else_expr == 0 or isEmptyBlock(w.ast, while_node.ast.else_expr)))
877 {
878 try w.transformations.ensureUnusedCapacity(1);
879 w.transformations.appendAssumeCapacity(.{ .replace_with_true = while_node.ast.cond_expr });
880 } else if (!isFalseIdent(w.ast, while_node.ast.cond_expr) and isEmptyBlock(w.ast, while_node.ast.then_expr)) {
881 try w.transformations.ensureUnusedCapacity(1);
882 w.transformations.appendAssumeCapacity(.{ .replace_with_false = while_node.ast.cond_expr });
883 } else if (isTrueIdent(w.ast, while_node.ast.cond_expr)) {
884 try w.transformations.ensureUnusedCapacity(1);
885 w.transformations.appendAssumeCapacity(.{ .replace_node = .{
886 .to_replace = node_index,
887 .replacement = while_node.ast.then_expr,
888 } });
889 } else if (isFalseIdent(w.ast, while_node.ast.cond_expr)) {
890 try w.transformations.ensureUnusedCapacity(1);
891 w.transformations.appendAssumeCapacity(.{ .replace_node = .{
892 .to_replace = node_index,
893 .replacement = while_node.ast.else_expr,
894 } });
895 }
896
897 try walkExpression(w, while_node.ast.cond_expr); // condition
898
899 if (while_node.ast.cont_expr != 0) {
900 try walkExpression(w, while_node.ast.cont_expr);
901 }
902
903 if (while_node.ast.then_expr != 0) {
904 try walkExpression(w, while_node.ast.then_expr);
905 }
906 if (while_node.ast.else_expr != 0) {
907 try walkExpression(w, while_node.ast.else_expr);
908 }
909}
910
911fn walkFor(w: *Walk, for_node: Ast.full.For) Error!void {
912 try walkParamList(w, for_node.ast.inputs);
913 if (for_node.ast.then_expr != 0) {
914 try walkExpression(w, for_node.ast.then_expr);
915 }
916 if (for_node.ast.else_expr != 0) {
917 try walkExpression(w, for_node.ast.else_expr);
918 }
919}
920
921fn walkIf(w: *Walk, node_index: Ast.Node.Index, if_node: Ast.full.If) Error!void {
922 assert(if_node.ast.cond_expr != 0);
923 assert(if_node.ast.then_expr != 0);
924
925 // Perform these transformations in this priority order:
926 // 1. If the `else` expression is missing or an empty block, replace the condition with `if (true)` if it is not already.
927 // 2. If the `then` block is empty, replace the condition with `if (false)` if it is not already.
928 // 3. If the condition is `if (true)`, replace the `if` expression with the contents of the `then` expression.
929 // 4. If the condition is `if (false)`, replace the `if` expression with the contents of the `else` expression.
930 if (!isTrueIdent(w.ast, if_node.ast.cond_expr) and
931 (if_node.ast.else_expr == 0 or isEmptyBlock(w.ast, if_node.ast.else_expr)))
932 {
933 try w.transformations.ensureUnusedCapacity(1);
934 w.transformations.appendAssumeCapacity(.{ .replace_with_true = if_node.ast.cond_expr });
935 } else if (!isFalseIdent(w.ast, if_node.ast.cond_expr) and isEmptyBlock(w.ast, if_node.ast.then_expr)) {
936 try w.transformations.ensureUnusedCapacity(1);
937 w.transformations.appendAssumeCapacity(.{ .replace_with_false = if_node.ast.cond_expr });
938 } else if (isTrueIdent(w.ast, if_node.ast.cond_expr)) {
939 try w.transformations.ensureUnusedCapacity(1);
940 w.transformations.appendAssumeCapacity(.{ .replace_node = .{
941 .to_replace = node_index,
942 .replacement = if_node.ast.then_expr,
943 } });
944 } else if (isFalseIdent(w.ast, if_node.ast.cond_expr)) {
945 try w.transformations.ensureUnusedCapacity(1);
946 w.transformations.appendAssumeCapacity(.{ .replace_node = .{
947 .to_replace = node_index,
948 .replacement = if_node.ast.else_expr,
949 } });
950 }
951
952 try walkExpression(w, if_node.ast.cond_expr); // condition
953
954 if (if_node.ast.then_expr != 0) {
955 try walkExpression(w, if_node.ast.then_expr);
956 }
957 if (if_node.ast.else_expr != 0) {
958 try walkExpression(w, if_node.ast.else_expr);
959 }
960}
961
962fn walkAsm(w: *Walk, asm_node: Ast.full.Asm) Error!void {
963 try walkExpression(w, asm_node.ast.template);
964 for (asm_node.ast.items) |item| {
965 try walkExpression(w, item);
966 }
967}
968
969fn walkParamList(w: *Walk, params: []const Ast.Node.Index) Error!void {
970 for (params) |param_node| {
971 try walkExpression(w, param_node);
972 }
973}
974
975/// Check if it is already gutted (i.e. its body replaced with `@trap()`).
976fn isFnBodyGutted(ast: *const Ast, body_node: Ast.Node.Index) bool {
977 // skip over discards
978 const node_tags = ast.nodes.items(.tag);
979 const datas = ast.nodes.items(.data);
980 var statements_buf: [2]Ast.Node.Index = undefined;
981 const statements = switch (node_tags[body_node]) {
982 .block_two,
983 .block_two_semicolon,
984 => blk: {
985 statements_buf[0..2].* = .{ datas[body_node].lhs, datas[body_node].rhs };
986 break :blk if (datas[body_node].lhs == 0)
987 statements_buf[0..0]
988 else if (datas[body_node].rhs == 0)
989 statements_buf[0..1]
990 else
991 statements_buf[0..2];
992 },
993
994 .block,
995 .block_semicolon,
996 => ast.extra_data[datas[body_node].lhs..datas[body_node].rhs],
997
998 else => return false,
999 };
1000 var i: usize = 0;
1001 while (i < statements.len) : (i += 1) {
1002 switch (categorizeStmt(ast, statements[i])) {
1003 .discard_identifier => continue,
1004 .trap_call => return i + 1 == statements.len,
1005 else => return false,
1006 }
1007 }
1008 return false;
1009}
1010
1011const StmtCategory = enum {
1012 discard_undefined,
1013 discard_identifier,
1014 trap_call,
1015 other,
1016};
1017
1018fn categorizeStmt(ast: *const Ast, stmt: Ast.Node.Index) StmtCategory {
1019 const node_tags = ast.nodes.items(.tag);
1020 const datas = ast.nodes.items(.data);
1021 const main_tokens = ast.nodes.items(.main_token);
1022 switch (node_tags[stmt]) {
1023 .builtin_call_two, .builtin_call_two_comma => {
1024 if (datas[stmt].lhs == 0) {
1025 return categorizeBuiltinCall(ast, main_tokens[stmt], &.{});
1026 } else if (datas[stmt].rhs == 0) {
1027 return categorizeBuiltinCall(ast, main_tokens[stmt], &.{datas[stmt].lhs});
1028 } else {
1029 return categorizeBuiltinCall(ast, main_tokens[stmt], &.{ datas[stmt].lhs, datas[stmt].rhs });
1030 }
1031 },
1032 .builtin_call, .builtin_call_comma => {
1033 const params = ast.extra_data[datas[stmt].lhs..datas[stmt].rhs];
1034 return categorizeBuiltinCall(ast, main_tokens[stmt], params);
1035 },
1036 .assign => {
1037 const infix = datas[stmt];
1038 if (isDiscardIdent(ast, infix.lhs) and node_tags[infix.rhs] == .identifier) {
1039 const name_bytes = ast.tokenSlice(main_tokens[infix.rhs]);
1040 if (std.mem.eql(u8, name_bytes, "undefined")) {
1041 return .discard_undefined;
1042 } else {
1043 return .discard_identifier;
1044 }
1045 }
1046 return .other;
1047 },
1048 else => return .other,
1049 }
1050}
1051
1052fn categorizeBuiltinCall(
1053 ast: *const Ast,
1054 builtin_token: Ast.TokenIndex,
1055 params: []const Ast.Node.Index,
1056) StmtCategory {
1057 if (params.len != 0) return .other;
1058 const name_bytes = ast.tokenSlice(builtin_token);
1059 if (std.mem.eql(u8, name_bytes, "@trap"))
1060 return .trap_call;
1061 return .other;
1062}
1063
1064fn isDiscardIdent(ast: *const Ast, node: Ast.Node.Index) bool {
1065 return isMatchingIdent(ast, node, "_");
1066}
1067
1068fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool {
1069 return isMatchingIdent(ast, node, "undefined");
1070}
1071
1072fn isTrueIdent(ast: *const Ast, node: Ast.Node.Index) bool {
1073 return isMatchingIdent(ast, node, "true");
1074}
1075
1076fn isFalseIdent(ast: *const Ast, node: Ast.Node.Index) bool {
1077 return isMatchingIdent(ast, node, "false");
1078}
1079
1080fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bool {
1081 const node_tags = ast.nodes.items(.tag);
1082 const main_tokens = ast.nodes.items(.main_token);
1083 switch (node_tags[node]) {
1084 .identifier => {
1085 const token_index = main_tokens[node];
1086 const name_bytes = ast.tokenSlice(token_index);
1087 return std.mem.eql(u8, name_bytes, string);
1088 },
1089 else => return false,
1090 }
1091}
1092
1093fn isEmptyBlock(ast: *const Ast, node: Ast.Node.Index) bool {
1094 const node_tags = ast.nodes.items(.tag);
1095 const node_data = ast.nodes.items(.data);
1096 switch (node_tags[node]) {
1097 .block_two => {
1098 return node_data[node].lhs == 0 and node_data[node].rhs == 0;
1099 },
1100 else => return false,
1101 }
1102}
stage1/config.zig.in-1
...@@ -13,4 +13,3 @@ pub const skip_non_native = false;...@@ -13,4 +13,3 @@ pub const skip_non_native = false;
13pub const only_c = false;13pub const only_c = false;
14pub const force_gpa = false;14pub const force_gpa = false;
15pub const only_core_functionality = true;15pub const only_core_functionality = true;
16pub const only_reduce = false;