aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-02-26 23:14:57 -0700
committerAndrew Kelley <andrew@ziglang.org>2024-02-26 23:43:56 -0700
commitdfe430e9f488536c6ce4be23473f60aa5e89ab5a (patch)
tree659fda01e693b129a6c28313d4b3c8b075d3e465 /lib/std
parent0157e1196c77702f07d44c63c71246ff5e5616f1 (diff)
downloadzig-dfe430e9f488536c6ce4be23473f60aa5e89ab5a.tar.gz
zig-dfe430e9f488536c6ce4be23473f60aa5e89ab5a.zip
move lazily compiled source files to lib/compiler/
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/zig/fmt.zig342
-rw-r--r--lib/std/zig/reduce.zig426
-rw-r--r--lib/std/zig/reduce/Walk.zig1102
3 files changed, 0 insertions, 1870 deletions
diff --git a/lib/std/zig/fmt.zig b/lib/std/zig/fmt.zig
deleted file mode 100644
index 2fc04b7935..0000000000
--- a/lib/std/zig/fmt.zig
+++ /dev/null
@@ -1,342 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const fs = std.fs;
-const process = std.process;
-const Allocator = std.mem.Allocator;
-const warn = std.log.warn;
-const Color = std.zig.Color;
-
-const usage_fmt =
- \\Usage: zig fmt [file]...
- \\
- \\ Formats the input files and modifies them in-place.
- \\ Arguments can be files or directories, which are searched
- \\ recursively.
- \\
- \\Options:
- \\ -h, --help Print this help and exit
- \\ --color [auto|off|on] Enable or disable colored error messages
- \\ --stdin Format code from stdin; output to stdout
- \\ --check List non-conforming files and exit with an error
- \\ if the list is non-empty
- \\ --ast-check Run zig ast-check on every file
- \\ --exclude [file] Exclude file or directory from formatting
- \\
- \\
-;
-
-const Fmt = struct {
- seen: SeenMap,
- any_error: bool,
- check_ast: bool,
- color: Color,
- gpa: Allocator,
- arena: Allocator,
- out_buffer: std.ArrayList(u8),
-
- const SeenMap = std.AutoHashMap(fs.File.INode, void);
-};
-
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
- const gpa = arena;
-
- const args = try process.argsAlloc(arena);
-
- var color: Color = .auto;
- var stdin_flag: bool = false;
- var check_flag: bool = false;
- var check_ast_flag: bool = false;
- var input_files = std.ArrayList([]const u8).init(gpa);
- defer input_files.deinit();
- var excluded_files = std.ArrayList([]const u8).init(gpa);
- defer excluded_files.deinit();
-
- {
- var i: usize = 1;
- while (i < args.len) : (i += 1) {
- const arg = args[i];
- if (mem.startsWith(u8, arg, "-")) {
- if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
- const stdout = std.io.getStdOut().writer();
- try stdout.writeAll(usage_fmt);
- return process.cleanExit();
- } else if (mem.eql(u8, arg, "--color")) {
- if (i + 1 >= args.len) {
- fatal("expected [auto|on|off] after --color", .{});
- }
- i += 1;
- const next_arg = args[i];
- color = std.meta.stringToEnum(Color, next_arg) orelse {
- fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
- };
- } else if (mem.eql(u8, arg, "--stdin")) {
- stdin_flag = true;
- } else if (mem.eql(u8, arg, "--check")) {
- check_flag = true;
- } else if (mem.eql(u8, arg, "--ast-check")) {
- check_ast_flag = true;
- } else if (mem.eql(u8, arg, "--exclude")) {
- if (i + 1 >= args.len) {
- fatal("expected parameter after --exclude", .{});
- }
- i += 1;
- const next_arg = args[i];
- try excluded_files.append(next_arg);
- } else {
- fatal("unrecognized parameter: '{s}'", .{arg});
- }
- } else {
- try input_files.append(arg);
- }
- }
- }
-
- if (stdin_flag) {
- if (input_files.items.len != 0) {
- fatal("cannot use --stdin with positional arguments", .{});
- }
-
- const stdin = std.io.getStdIn();
- const source_code = std.zig.readSourceFileToEndAlloc(gpa, stdin, null) catch |err| {
- fatal("unable to read stdin: {}", .{err});
- };
- defer gpa.free(source_code);
-
- var tree = std.zig.Ast.parse(gpa, source_code, .zig) catch |err| {
- fatal("error parsing stdin: {}", .{err});
- };
- defer tree.deinit(gpa);
-
- if (check_ast_flag) {
- var zir = try std.zig.AstGen.generate(gpa, tree);
-
- if (zir.hasCompileErrors()) {
- var wip_errors: std.zig.ErrorBundle.Wip = undefined;
- try wip_errors.init(gpa);
- defer wip_errors.deinit();
- try wip_errors.addZirErrorMessages(zir, tree, source_code, "<stdin>");
- var error_bundle = try wip_errors.toOwnedBundle("");
- defer error_bundle.deinit(gpa);
- error_bundle.renderToStdErr(color.renderOptions());
- process.exit(2);
- }
- } else if (tree.errors.len != 0) {
- try std.zig.printAstErrorsToStderr(gpa, tree, "<stdin>", color);
- process.exit(2);
- }
- const formatted = try tree.render(gpa);
- defer gpa.free(formatted);
-
- if (check_flag) {
- const code: u8 = @intFromBool(mem.eql(u8, formatted, source_code));
- process.exit(code);
- }
-
- return std.io.getStdOut().writeAll(formatted);
- }
-
- if (input_files.items.len == 0) {
- fatal("expected at least one source file argument", .{});
- }
-
- var fmt = Fmt{
- .gpa = gpa,
- .arena = arena,
- .seen = Fmt.SeenMap.init(gpa),
- .any_error = false,
- .check_ast = check_ast_flag,
- .color = color,
- .out_buffer = std.ArrayList(u8).init(gpa),
- };
- defer fmt.seen.deinit();
- defer fmt.out_buffer.deinit();
-
- // Mark any excluded files/directories as already seen,
- // so that they are skipped later during actual processing
- for (excluded_files.items) |file_path| {
- const stat = fs.cwd().statFile(file_path) catch |err| switch (err) {
- error.FileNotFound => continue,
- // On Windows, statFile does not work for directories
- error.IsDir => dir: {
- var dir = try fs.cwd().openDir(file_path, .{});
- defer dir.close();
- break :dir try dir.stat();
- },
- else => |e| return e,
- };
- try fmt.seen.put(stat.inode, {});
- }
-
- for (input_files.items) |file_path| {
- try fmtPath(&fmt, file_path, check_flag, fs.cwd(), file_path);
- }
- if (fmt.any_error) {
- process.exit(1);
- }
-}
-
-const FmtError = error{
- SystemResources,
- OperationAborted,
- IoPending,
- BrokenPipe,
- Unexpected,
- WouldBlock,
- FileClosed,
- DestinationAddressRequired,
- DiskQuota,
- FileTooBig,
- InputOutput,
- NoSpaceLeft,
- AccessDenied,
- OutOfMemory,
- RenameAcrossMountPoints,
- ReadOnlyFileSystem,
- LinkQuotaExceeded,
- FileBusy,
- EndOfStream,
- Unseekable,
- NotOpenForWriting,
- UnsupportedEncoding,
- ConnectionResetByPeer,
- SocketNotConnected,
- LockViolation,
- NetNameDeleted,
- InvalidArgument,
-} || fs.File.OpenError;
-
-fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
- fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
- error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
- else => {
- warn("unable to format '{s}': {s}", .{ file_path, @errorName(err) });
- fmt.any_error = true;
- return;
- },
- };
-}
-
-fn fmtPathDir(
- fmt: *Fmt,
- file_path: []const u8,
- check_mode: bool,
- parent_dir: fs.Dir,
- parent_sub_path: []const u8,
-) FmtError!void {
- var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
- defer dir.close();
-
- const stat = try dir.stat();
- if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
-
- var dir_it = dir.iterate();
- while (try dir_it.next()) |entry| {
- const is_dir = entry.kind == .directory;
-
- if (is_dir and (mem.eql(u8, entry.name, "zig-cache") or mem.eql(u8, entry.name, "zig-out"))) continue;
-
- if (is_dir or entry.kind == .file and (mem.endsWith(u8, entry.name, ".zig") or mem.endsWith(u8, entry.name, ".zon"))) {
- const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
- defer fmt.gpa.free(full_path);
-
- if (is_dir) {
- try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
- } else {
- fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
- warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) });
- fmt.any_error = true;
- return;
- };
- }
- }
- }
-}
-
-fn fmtPathFile(
- fmt: *Fmt,
- file_path: []const u8,
- check_mode: bool,
- dir: fs.Dir,
- sub_path: []const u8,
-) FmtError!void {
- const source_file = try dir.openFile(sub_path, .{});
- var file_closed = false;
- errdefer if (!file_closed) source_file.close();
-
- const stat = try source_file.stat();
-
- if (stat.kind == .directory)
- return error.IsDir;
-
- const gpa = fmt.gpa;
- const source_code = try std.zig.readSourceFileToEndAlloc(
- gpa,
- source_file,
- std.math.cast(usize, stat.size) orelse return error.FileTooBig,
- );
- defer gpa.free(source_code);
-
- source_file.close();
- file_closed = true;
-
- // Add to set after no longer possible to get error.IsDir.
- if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
-
- var tree = try std.zig.Ast.parse(gpa, source_code, .zig);
- defer tree.deinit(gpa);
-
- if (tree.errors.len != 0) {
- try std.zig.printAstErrorsToStderr(gpa, tree, file_path, fmt.color);
- fmt.any_error = true;
- return;
- }
-
- if (fmt.check_ast) {
- if (stat.size > std.zig.max_src_size)
- return error.FileTooBig;
-
- var zir = try std.zig.AstGen.generate(gpa, tree);
- defer zir.deinit(gpa);
-
- if (zir.hasCompileErrors()) {
- var wip_errors: std.zig.ErrorBundle.Wip = undefined;
- try wip_errors.init(gpa);
- defer wip_errors.deinit();
- try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path);
- var error_bundle = try wip_errors.toOwnedBundle("");
- defer error_bundle.deinit(gpa);
- error_bundle.renderToStdErr(fmt.color.renderOptions());
- fmt.any_error = true;
- }
- }
-
- // As a heuristic, we make enough capacity for the same as the input source.
- fmt.out_buffer.shrinkRetainingCapacity(0);
- try fmt.out_buffer.ensureTotalCapacity(source_code.len);
-
- try tree.renderToArrayList(&fmt.out_buffer, .{});
- if (mem.eql(u8, fmt.out_buffer.items, source_code))
- return;
-
- if (check_mode) {
- const stdout = std.io.getStdOut().writer();
- try stdout.print("{s}\n", .{file_path});
- fmt.any_error = true;
- } else {
- var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
- defer af.deinit();
-
- try af.file.writeAll(fmt.out_buffer.items);
- try af.finish();
- const stdout = std.io.getStdOut().writer();
- try stdout.print("{s}\n", .{file_path});
- }
-}
-
-fn fatal(comptime format: []const u8, args: anytype) noreturn {
- std.log.err(format, args);
- process.exit(1);
-}
diff --git a/lib/std/zig/reduce.zig b/lib/std/zig/reduce.zig
deleted file mode 100644
index 1b40856ffe..0000000000
--- a/lib/std/zig/reduce.zig
+++ /dev/null
@@ -1,426 +0,0 @@
-const std = @import("std");
-const mem = std.mem;
-const Allocator = std.mem.Allocator;
-const assert = std.debug.assert;
-const Ast = std.zig.Ast;
-const Walk = @import("reduce/Walk.zig");
-const AstGen = std.zig.AstGen;
-const Zir = std.zig.Zir;
-
-const usage =
- \\zig reduce [options] ./checker root_source_file.zig [-- [argv]]
- \\
- \\root_source_file.zig is relative to --main-mod-path.
- \\
- \\checker:
- \\ An executable that communicates interestingness by returning these exit codes:
- \\ exit(0): interesting
- \\ exit(1): unknown (infinite loop or other mishap)
- \\ exit(other): not interesting
- \\
- \\options:
- \\ --seed [integer] Override the random seed. Defaults to 0
- \\ --skip-smoke-test Skip interestingness check smoke test
- \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name
- \\ deps: [dep],[dep],...
- \\ dep: [[import=]name]
- \\ --deps [dep],[dep],... Set dependency names for the root package
- \\ dep: [[import=]name]
- \\ --main-mod-path Set the directory of the root module
- \\
- \\argv:
- \\ Forwarded directly to the interestingness script.
- \\
-;
-
-const Interestingness = enum { interesting, unknown, boring };
-
-// Roadmap:
-// - add thread pool
-// - add support for parsing the module flags
-// - more fancy transformations
-// - @import inlining of modules
-// - removing statements or blocks of code
-// - replacing operands of `and` and `or` with `true` and `false`
-// - replacing if conditions with `true` and `false`
-// - reduce flags sent to the compiler
-// - integrate with the build system?
-
-pub fn main() !void {
- var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
- defer arena_instance.deinit();
- const arena = arena_instance.allocator();
-
- var general_purpose_allocator: std.heap.GeneralPurposeAllocator(.{}) = .{};
- const gpa = general_purpose_allocator.allocator();
-
- const args = try std.process.argsAlloc(arena);
-
- var opt_checker_path: ?[]const u8 = null;
- var opt_root_source_file_path: ?[]const u8 = null;
- var argv: []const []const u8 = &.{};
- var seed: u32 = 0;
- var skip_smoke_test = false;
-
- {
- var i: usize = 1;
- while (i < args.len) : (i += 1) {
- const arg = args[i];
- if (mem.startsWith(u8, arg, "-")) {
- if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
- const stdout = std.io.getStdOut().writer();
- try stdout.writeAll(usage);
- return std.process.cleanExit();
- } else if (mem.eql(u8, arg, "--")) {
- argv = args[i + 1 ..];
- break;
- } else if (mem.eql(u8, arg, "--skip-smoke-test")) {
- skip_smoke_test = true;
- } else if (mem.eql(u8, arg, "--main-mod-path")) {
- @panic("TODO: implement --main-mod-path");
- } else if (mem.eql(u8, arg, "--mod")) {
- @panic("TODO: implement --mod");
- } else if (mem.eql(u8, arg, "--deps")) {
- @panic("TODO: implement --deps");
- } else if (mem.eql(u8, arg, "--seed")) {
- i += 1;
- if (i >= args.len) fatal("expected 32-bit integer after {s}", .{arg});
- const next_arg = args[i];
- seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| {
- fatal("unable to parse seed '{s}' as 32-bit integer: {s}", .{
- next_arg, @errorName(err),
- });
- };
- } else {
- fatal("unrecognized parameter: '{s}'", .{arg});
- }
- } else if (opt_checker_path == null) {
- opt_checker_path = arg;
- } else if (opt_root_source_file_path == null) {
- opt_root_source_file_path = arg;
- } else {
- fatal("unexpected extra parameter: '{s}'", .{arg});
- }
- }
- }
-
- const checker_path = opt_checker_path orelse
- fatal("missing interestingness checker argument; see -h for usage", .{});
- const root_source_file_path = opt_root_source_file_path orelse
- fatal("missing root source file path argument; see -h for usage", .{});
-
- var interestingness_argv: std.ArrayListUnmanaged([]const u8) = .{};
- try interestingness_argv.ensureUnusedCapacity(arena, argv.len + 1);
- interestingness_argv.appendAssumeCapacity(checker_path);
- interestingness_argv.appendSliceAssumeCapacity(argv);
-
- var rendered = std.ArrayList(u8).init(gpa);
- defer rendered.deinit();
-
- var astgen_input = std.ArrayList(u8).init(gpa);
- defer astgen_input.deinit();
-
- var tree = try parse(gpa, root_source_file_path);
- defer {
- gpa.free(tree.source);
- tree.deinit(gpa);
- }
-
- if (!skip_smoke_test) {
- std.debug.print("smoke testing the interestingness check...\n", .{});
- switch (try runCheck(arena, interestingness_argv.items)) {
- .interesting => {},
- .boring, .unknown => |t| {
- fatal("interestingness check returned {s} for unmodified input\n", .{
- @tagName(t),
- });
- },
- }
- }
-
- var fixups: Ast.Fixups = .{};
- defer fixups.deinit(gpa);
-
- var more_fixups: Ast.Fixups = .{};
- defer more_fixups.deinit(gpa);
-
- var rng = std.Random.DefaultPrng.init(seed);
-
- // 1. Walk the AST of the source file looking for independent
- // reductions and collecting them all into an array list.
- // 2. Randomize the list of transformations. A future enhancement will add
- // priority weights to the sorting but for now they are completely
- // shuffled.
- // 3. Apply a subset consisting of 1/2 of the transformations and check for
- // interestingness.
- // 4. If not interesting, half the subset size again and check again.
- // 5. Repeat until the subset size is 1, then march the transformation
- // index forward by 1 with each non-interesting attempt.
- //
- // At any point if a subset of transformations succeeds in producing an interesting
- // result, restart the whole process, reparsing the AST and re-generating the list
- // of all possible transformations and shuffling it again.
-
- var transformations = std.ArrayList(Walk.Transformation).init(gpa);
- defer transformations.deinit();
- try Walk.findTransformations(arena, &tree, &transformations);
- sortTransformations(transformations.items, rng.random());
-
- fresh: while (transformations.items.len > 0) {
- std.debug.print("found {d} possible transformations\n", .{
- transformations.items.len,
- });
- var subset_size: usize = transformations.items.len;
- var start_index: usize = 0;
-
- while (start_index < transformations.items.len) {
- const prev_subset_size = subset_size;
- subset_size = @max(1, subset_size * 3 / 4);
- if (prev_subset_size > 1 and subset_size == 1)
- start_index = 0;
-
- const this_set = transformations.items[start_index..][0..subset_size];
- std.debug.print("trying {d} random transformations: ", .{subset_size});
- for (this_set[0..@min(this_set.len, 20)]) |t| {
- std.debug.print("{s} ", .{@tagName(t)});
- }
- std.debug.print("\n", .{});
- try transformationsToFixups(gpa, arena, root_source_file_path, this_set, &fixups);
-
- rendered.clearRetainingCapacity();
- try tree.renderToArrayList(&rendered, fixups);
-
- // The transformations we applied may have resulted in unused locals,
- // in which case we would like to add the respective discards.
- {
- try astgen_input.resize(rendered.items.len);
- @memcpy(astgen_input.items, rendered.items);
- try astgen_input.append(0);
- const source_with_null = astgen_input.items[0 .. astgen_input.items.len - 1 :0];
- var astgen_tree = try Ast.parse(gpa, source_with_null, .zig);
- defer astgen_tree.deinit(gpa);
- if (astgen_tree.errors.len != 0) {
- @panic("syntax errors occurred");
- }
- var zir = try AstGen.generate(gpa, astgen_tree);
- defer zir.deinit(gpa);
-
- if (zir.hasCompileErrors()) {
- more_fixups.clearRetainingCapacity();
- const payload_index = zir.extra[@intFromEnum(Zir.ExtraIndex.compile_errors)];
- assert(payload_index != 0);
- const header = zir.extraData(Zir.Inst.CompileErrors, payload_index);
- var extra_index = header.end;
- for (0..header.data.items_len) |_| {
- const item = zir.extraData(Zir.Inst.CompileErrors.Item, extra_index);
- extra_index = item.end;
- const msg = zir.nullTerminatedString(item.data.msg);
- if (mem.eql(u8, msg, "unused local constant") or
- mem.eql(u8, msg, "unused local variable") or
- mem.eql(u8, msg, "unused function parameter") or
- mem.eql(u8, msg, "unused capture"))
- {
- const ident_token = item.data.token;
- try more_fixups.unused_var_decls.put(gpa, ident_token, {});
- } else {
- std.debug.print("found other ZIR error: '{s}'\n", .{msg});
- }
- }
- if (more_fixups.count() != 0) {
- rendered.clearRetainingCapacity();
- try astgen_tree.renderToArrayList(&rendered, more_fixups);
- }
- }
- }
-
- try std.fs.cwd().writeFile(root_source_file_path, rendered.items);
- // std.debug.print("trying this code:\n{s}\n", .{rendered.items});
-
- const interestingness = try runCheck(arena, interestingness_argv.items);
- std.debug.print("{d} random transformations: {s}. {d}/{d}\n", .{
- subset_size, @tagName(interestingness), start_index, transformations.items.len,
- });
- switch (interestingness) {
- .interesting => {
- const new_tree = try parse(gpa, root_source_file_path);
- gpa.free(tree.source);
- tree.deinit(gpa);
- tree = new_tree;
-
- try Walk.findTransformations(arena, &tree, &transformations);
- sortTransformations(transformations.items, rng.random());
-
- continue :fresh;
- },
- .unknown, .boring => {
- // Continue to try the next set of transformations.
- // If we tested only one transformation, move on to the next one.
- if (subset_size == 1) {
- start_index += 1;
- } else {
- start_index += subset_size;
- if (start_index + subset_size > transformations.items.len) {
- start_index = 0;
- }
- }
- },
- }
- }
- std.debug.print("all {d} remaining transformations are uninteresting\n", .{
- transformations.items.len,
- });
-
- // Revert the source back to not be transformed.
- fixups.clearRetainingCapacity();
- rendered.clearRetainingCapacity();
- try tree.renderToArrayList(&rendered, fixups);
- try std.fs.cwd().writeFile(root_source_file_path, rendered.items);
-
- return std.process.cleanExit();
- }
- std.debug.print("no more transformations found\n", .{});
- return std.process.cleanExit();
-}
-
-fn sortTransformations(transformations: []Walk.Transformation, rng: std.Random) void {
- rng.shuffle(Walk.Transformation, transformations);
- // Stable sort based on priority to keep randomness as the secondary sort.
- // TODO: introduce transformation priorities
- // std.mem.sort(transformations);
-}
-
-fn termToInteresting(term: std.process.Child.Term) Interestingness {
- return switch (term) {
- .Exited => |code| switch (code) {
- 0 => .interesting,
- 1 => .unknown,
- else => .boring,
- },
- else => b: {
- std.debug.print("interestingness check aborted unexpectedly\n", .{});
- break :b .boring;
- },
- };
-}
-
-fn runCheck(arena: std.mem.Allocator, argv: []const []const u8) !Interestingness {
- const result = try std.process.Child.run(.{
- .allocator = arena,
- .argv = argv,
- });
- if (result.stderr.len != 0)
- std.debug.print("{s}", .{result.stderr});
- return termToInteresting(result.term);
-}
-
-fn transformationsToFixups(
- gpa: Allocator,
- arena: Allocator,
- root_source_file_path: []const u8,
- transforms: []const Walk.Transformation,
- fixups: *Ast.Fixups,
-) !void {
- fixups.clearRetainingCapacity();
-
- for (transforms) |t| switch (t) {
- .gut_function => |fn_decl_node| {
- try fixups.gut_functions.put(gpa, fn_decl_node, {});
- },
- .delete_node => |decl_node| {
- try fixups.omit_nodes.put(gpa, decl_node, {});
- },
- .delete_var_decl => |delete_var_decl| {
- try fixups.omit_nodes.put(gpa, delete_var_decl.var_decl_node, {});
- for (delete_var_decl.references.items) |ident_node| {
- try fixups.replace_nodes_with_string.put(gpa, ident_node, "undefined");
- }
- },
- .replace_with_undef => |node| {
- try fixups.replace_nodes_with_string.put(gpa, node, "undefined");
- },
- .replace_with_true => |node| {
- try fixups.replace_nodes_with_string.put(gpa, node, "true");
- },
- .replace_with_false => |node| {
- try fixups.replace_nodes_with_string.put(gpa, node, "false");
- },
- .replace_node => |r| {
- try fixups.replace_nodes_with_node.put(gpa, r.to_replace, r.replacement);
- },
- .inline_imported_file => |inline_imported_file| {
- const full_imported_path = try std.fs.path.join(gpa, &.{
- std.fs.path.dirname(root_source_file_path) orelse ".",
- inline_imported_file.imported_string,
- });
- defer gpa.free(full_imported_path);
- var other_file_ast = try parse(gpa, full_imported_path);
- defer {
- gpa.free(other_file_ast.source);
- other_file_ast.deinit(gpa);
- }
-
- var inlined_fixups: Ast.Fixups = .{};
- defer inlined_fixups.deinit(gpa);
- if (std.fs.path.dirname(inline_imported_file.imported_string)) |dirname| {
- inlined_fixups.rebase_imported_paths = dirname;
- }
- for (inline_imported_file.in_scope_names.keys()) |name| {
- // This name needs to be mangled in order to not cause an
- // ambiguous reference error.
- var i: u32 = 2;
- const mangled = while (true) : (i += 1) {
- const mangled = try std.fmt.allocPrint(gpa, "{s}{d}", .{ name, i });
- if (!inline_imported_file.in_scope_names.contains(mangled))
- break mangled;
- gpa.free(mangled);
- };
- try inlined_fixups.rename_identifiers.put(gpa, name, mangled);
- }
- defer {
- for (inlined_fixups.rename_identifiers.values()) |v| {
- gpa.free(v);
- }
- }
-
- var other_source = std.ArrayList(u8).init(gpa);
- defer other_source.deinit();
- try other_source.appendSlice("struct {\n");
- try other_file_ast.renderToArrayList(&other_source, inlined_fixups);
- try other_source.appendSlice("}");
-
- try fixups.replace_nodes_with_string.put(
- gpa,
- inline_imported_file.builtin_call_node,
- try arena.dupe(u8, other_source.items),
- );
- },
- };
-}
-
-fn parse(gpa: Allocator, file_path: []const u8) !Ast {
- const source_code = std.fs.cwd().readFileAllocOptions(
- gpa,
- file_path,
- std.math.maxInt(u32),
- null,
- 1,
- 0,
- ) catch |err| {
- fatal("unable to open '{s}': {s}", .{ file_path, @errorName(err) });
- };
- errdefer gpa.free(source_code);
-
- var tree = try Ast.parse(gpa, source_code, .zig);
- errdefer tree.deinit(gpa);
-
- if (tree.errors.len != 0) {
- @panic("syntax errors occurred");
- }
-
- return tree;
-}
-
-fn fatal(comptime format: []const u8, args: anytype) noreturn {
- std.log.err(format, args);
- std.process.exit(1);
-}
diff --git a/lib/std/zig/reduce/Walk.zig b/lib/std/zig/reduce/Walk.zig
deleted file mode 100644
index 572243d829..0000000000
--- a/lib/std/zig/reduce/Walk.zig
+++ /dev/null
@@ -1,1102 +0,0 @@
-const std = @import("std");
-const Ast = std.zig.Ast;
-const Walk = @This();
-const assert = std.debug.assert;
-const BuiltinFn = std.zig.BuiltinFn;
-
-ast: *const Ast,
-transformations: *std.ArrayList(Transformation),
-unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index),
-in_scope_names: std.StringArrayHashMapUnmanaged(u32),
-replace_names: std.StringArrayHashMapUnmanaged(u32),
-gpa: std.mem.Allocator,
-arena: std.mem.Allocator,
-
-pub const Transformation = union(enum) {
- /// Replace the fn decl AST Node with one whose body is only `@trap()` with
- /// discarded parameters.
- gut_function: Ast.Node.Index,
- /// Omit a global declaration.
- delete_node: Ast.Node.Index,
- /// Delete a local variable declaration and replace all of its references
- /// with `undefined`.
- delete_var_decl: struct {
- var_decl_node: Ast.Node.Index,
- /// Identifier nodes that reference the variable.
- references: std.ArrayListUnmanaged(Ast.Node.Index),
- },
- /// Replace an expression with `undefined`.
- replace_with_undef: Ast.Node.Index,
- /// Replace an expression with `true`.
- replace_with_true: Ast.Node.Index,
- /// Replace an expression with `false`.
- replace_with_false: Ast.Node.Index,
- /// Replace a node with another node.
- replace_node: struct {
- to_replace: Ast.Node.Index,
- replacement: Ast.Node.Index,
- },
- /// Replace an `@import` with the imported file contents wrapped in a struct.
- inline_imported_file: InlineImportedFile,
-
- pub const InlineImportedFile = struct {
- builtin_call_node: Ast.Node.Index,
- imported_string: []const u8,
- /// Identifier names that must be renamed in the inlined code or else
- /// will cause ambiguous reference errors.
- in_scope_names: std.StringArrayHashMapUnmanaged(void),
- };
-};
-
-pub const Error = error{OutOfMemory};
-
-/// The result will be priority shuffled.
-pub fn findTransformations(
- arena: std.mem.Allocator,
- ast: *const Ast,
- transformations: *std.ArrayList(Transformation),
-) !void {
- transformations.clearRetainingCapacity();
-
- var walk: Walk = .{
- .ast = ast,
- .transformations = transformations,
- .gpa = transformations.allocator,
- .arena = arena,
- .unreferenced_globals = .{},
- .in_scope_names = .{},
- .replace_names = .{},
- };
- defer {
- walk.unreferenced_globals.deinit(walk.gpa);
- walk.in_scope_names.deinit(walk.gpa);
- walk.replace_names.deinit(walk.gpa);
- }
-
- try walkMembers(&walk, walk.ast.rootDecls());
-
- const unreferenced_globals = walk.unreferenced_globals.values();
- try transformations.ensureUnusedCapacity(unreferenced_globals.len);
- for (unreferenced_globals) |node| {
- transformations.appendAssumeCapacity(.{ .delete_node = node });
- }
-}
-
-fn walkMembers(w: *Walk, members: []const Ast.Node.Index) Error!void {
- // First we scan for globals so that we can delete them while walking.
- try scanDecls(w, members, .add);
-
- for (members) |member| {
- try walkMember(w, member);
- }
-
- try scanDecls(w, members, .remove);
-}
-
-const ScanDeclsAction = enum { add, remove };
-
-fn scanDecls(w: *Walk, members: []const Ast.Node.Index, action: ScanDeclsAction) Error!void {
- const ast = w.ast;
- const gpa = w.gpa;
- const node_tags = ast.nodes.items(.tag);
- const main_tokens = ast.nodes.items(.main_token);
- const token_tags = ast.tokens.items(.tag);
-
- for (members) |member_node| {
- const name_token = switch (node_tags[member_node]) {
- .global_var_decl,
- .local_var_decl,
- .simple_var_decl,
- .aligned_var_decl,
- => main_tokens[member_node] + 1,
-
- .fn_proto_simple,
- .fn_proto_multi,
- .fn_proto_one,
- .fn_proto,
- .fn_decl,
- => main_tokens[member_node] + 1,
-
- else => continue,
- };
-
- assert(token_tags[name_token] == .identifier);
- const name_bytes = ast.tokenSlice(name_token);
-
- switch (action) {
- .add => {
- try w.unreferenced_globals.put(gpa, name_bytes, member_node);
-
- const gop = try w.in_scope_names.getOrPut(gpa, name_bytes);
- if (!gop.found_existing) gop.value_ptr.* = 0;
- gop.value_ptr.* += 1;
- },
- .remove => {
- const entry = w.in_scope_names.getEntry(name_bytes).?;
- if (entry.value_ptr.* <= 1) {
- assert(w.in_scope_names.swapRemove(name_bytes));
- } else {
- entry.value_ptr.* -= 1;
- }
- },
- }
- }
-}
-
-fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {
- const ast = w.ast;
- const datas = ast.nodes.items(.data);
- switch (ast.nodes.items(.tag)[decl]) {
- .fn_decl => {
- const fn_proto = datas[decl].lhs;
- try walkExpression(w, fn_proto);
- const body_node = datas[decl].rhs;
- if (!isFnBodyGutted(ast, body_node)) {
- w.replace_names.clearRetainingCapacity();
- try w.transformations.append(.{ .gut_function = decl });
- try walkExpression(w, body_node);
- }
- },
- .fn_proto_simple,
- .fn_proto_multi,
- .fn_proto_one,
- .fn_proto,
- => {
- try walkExpression(w, decl);
- },
-
- .@"usingnamespace" => {
- try w.transformations.append(.{ .delete_node = decl });
- const expr = datas[decl].lhs;
- try walkExpression(w, expr);
- },
-
- .global_var_decl,
- .local_var_decl,
- .simple_var_decl,
- .aligned_var_decl,
- => try walkGlobalVarDecl(w, decl, ast.fullVarDecl(decl).?),
-
- .test_decl => {
- try w.transformations.append(.{ .delete_node = decl });
- try walkExpression(w, datas[decl].rhs);
- },
-
- .container_field_init,
- .container_field_align,
- .container_field,
- => {
- try w.transformations.append(.{ .delete_node = decl });
- try walkContainerField(w, ast.fullContainerField(decl).?);
- },
-
- .@"comptime" => {
- try w.transformations.append(.{ .delete_node = decl });
- try walkExpression(w, decl);
- },
-
- .root => unreachable,
- else => unreachable,
- }
-}
-
-fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
- const ast = w.ast;
- const token_tags = ast.tokens.items(.tag);
- const main_tokens = ast.nodes.items(.main_token);
- const node_tags = ast.nodes.items(.tag);
- const datas = ast.nodes.items(.data);
- switch (node_tags[node]) {
- .identifier => {
- const name_ident = main_tokens[node];
- assert(token_tags[name_ident] == .identifier);
- const name_bytes = ast.tokenSlice(name_ident);
- _ = w.unreferenced_globals.swapRemove(name_bytes);
- if (w.replace_names.get(name_bytes)) |index| {
- try w.transformations.items[index].delete_var_decl.references.append(w.arena, node);
- }
- },
-
- .number_literal,
- .char_literal,
- .unreachable_literal,
- .anyframe_literal,
- .string_literal,
- => {},
-
- .multiline_string_literal => {},
-
- .error_value => {},
-
- .block_two,
- .block_two_semicolon,
- => {
- const statements = [2]Ast.Node.Index{ datas[node].lhs, datas[node].rhs };
- if (datas[node].lhs == 0) {
- return walkBlock(w, node, statements[0..0]);
- } else if (datas[node].rhs == 0) {
- return walkBlock(w, node, statements[0..1]);
- } else {
- return walkBlock(w, node, statements[0..2]);
- }
- },
- .block,
- .block_semicolon,
- => {
- const statements = ast.extra_data[datas[node].lhs..datas[node].rhs];
- return walkBlock(w, node, statements);
- },
-
- .@"errdefer" => {
- const expr = datas[node].rhs;
- return walkExpression(w, expr);
- },
-
- .@"defer" => {
- const expr = datas[node].rhs;
- return walkExpression(w, expr);
- },
- .@"comptime", .@"nosuspend" => {
- const block = datas[node].lhs;
- return walkExpression(w, block);
- },
-
- .@"suspend" => {
- const body = datas[node].lhs;
- return walkExpression(w, body);
- },
-
- .@"catch" => {
- try walkExpression(w, datas[node].lhs); // target
- try walkExpression(w, datas[node].rhs); // fallback
- },
-
- .field_access => {
- const field_access = datas[node];
- try walkExpression(w, field_access.lhs);
- },
-
- .error_union,
- .switch_range,
- => {
- const infix = datas[node];
- try walkExpression(w, infix.lhs);
- return walkExpression(w, infix.rhs);
- },
- .for_range => {
- const infix = datas[node];
- try walkExpression(w, infix.lhs);
- if (infix.rhs != 0) {
- return walkExpression(w, infix.rhs);
- }
- },
-
- .add,
- .add_wrap,
- .add_sat,
- .array_cat,
- .array_mult,
- .assign,
- .assign_bit_and,
- .assign_bit_or,
- .assign_shl,
- .assign_shl_sat,
- .assign_shr,
- .assign_bit_xor,
- .assign_div,
- .assign_sub,
- .assign_sub_wrap,
- .assign_sub_sat,
- .assign_mod,
- .assign_add,
- .assign_add_wrap,
- .assign_add_sat,
- .assign_mul,
- .assign_mul_wrap,
- .assign_mul_sat,
- .bang_equal,
- .bit_and,
- .bit_or,
- .shl,
- .shl_sat,
- .shr,
- .bit_xor,
- .bool_and,
- .bool_or,
- .div,
- .equal_equal,
- .greater_or_equal,
- .greater_than,
- .less_or_equal,
- .less_than,
- .merge_error_sets,
- .mod,
- .mul,
- .mul_wrap,
- .mul_sat,
- .sub,
- .sub_wrap,
- .sub_sat,
- .@"orelse",
- => {
- const infix = datas[node];
- try walkExpression(w, infix.lhs);
- try walkExpression(w, infix.rhs);
- },
-
- .assign_destructure => {
- const lhs_count = ast.extra_data[datas[node].lhs];
- assert(lhs_count > 1);
- const lhs_exprs = ast.extra_data[datas[node].lhs + 1 ..][0..lhs_count];
- const rhs = datas[node].rhs;
-
- for (lhs_exprs) |lhs_node| {
- switch (node_tags[lhs_node]) {
- .global_var_decl,
- .local_var_decl,
- .simple_var_decl,
- .aligned_var_decl,
- => try walkLocalVarDecl(w, ast.fullVarDecl(lhs_node).?),
-
- else => try walkExpression(w, lhs_node),
- }
- }
- return walkExpression(w, rhs);
- },
-
- .bit_not,
- .bool_not,
- .negation,
- .negation_wrap,
- .optional_type,
- .address_of,
- => {
- return walkExpression(w, datas[node].lhs);
- },
-
- .@"try",
- .@"resume",
- .@"await",
- => {
- return walkExpression(w, datas[node].lhs);
- },
-
- .array_type,
- .array_type_sentinel,
- => {},
-
- .ptr_type_aligned,
- .ptr_type_sentinel,
- .ptr_type,
- .ptr_type_bit_range,
- => {},
-
- .array_init_one,
- .array_init_one_comma,
- .array_init_dot_two,
- .array_init_dot_two_comma,
- .array_init_dot,
- .array_init_dot_comma,
- .array_init,
- .array_init_comma,
- => {
- var elements: [2]Ast.Node.Index = undefined;
- return walkArrayInit(w, ast.fullArrayInit(&elements, node).?);
- },
-
- .struct_init_one,
- .struct_init_one_comma,
- .struct_init_dot_two,
- .struct_init_dot_two_comma,
- .struct_init_dot,
- .struct_init_dot_comma,
- .struct_init,
- .struct_init_comma,
- => {
- var buf: [2]Ast.Node.Index = undefined;
- return walkStructInit(w, node, ast.fullStructInit(&buf, node).?);
- },
-
- .call_one,
- .call_one_comma,
- .async_call_one,
- .async_call_one_comma,
- .call,
- .call_comma,
- .async_call,
- .async_call_comma,
- => {
- var buf: [1]Ast.Node.Index = undefined;
- return walkCall(w, ast.fullCall(&buf, node).?);
- },
-
- .array_access => {
- const suffix = datas[node];
- try walkExpression(w, suffix.lhs);
- try walkExpression(w, suffix.rhs);
- },
-
- .slice_open, .slice, .slice_sentinel => return walkSlice(w, node, ast.fullSlice(node).?),
-
- .deref => {
- try walkExpression(w, datas[node].lhs);
- },
-
- .unwrap_optional => {
- try walkExpression(w, datas[node].lhs);
- },
-
- .@"break" => {
- const label_token = datas[node].lhs;
- const target = datas[node].rhs;
- if (label_token == 0 and target == 0) {
- // no expressions
- } else if (label_token == 0 and target != 0) {
- try walkExpression(w, target);
- } else if (label_token != 0 and target == 0) {
- try walkIdentifier(w, label_token);
- } else if (label_token != 0 and target != 0) {
- try walkExpression(w, target);
- }
- },
-
- .@"continue" => {
- const label = datas[node].lhs;
- if (label != 0) {
- return walkIdentifier(w, label); // label
- }
- },
-
- .@"return" => {
- if (datas[node].lhs != 0) {
- try walkExpression(w, datas[node].lhs);
- }
- },
-
- .grouped_expression => {
- try walkExpression(w, datas[node].lhs);
- },
-
- .container_decl,
- .container_decl_trailing,
- .container_decl_arg,
- .container_decl_arg_trailing,
- .container_decl_two,
- .container_decl_two_trailing,
- .tagged_union,
- .tagged_union_trailing,
- .tagged_union_enum_tag,
- .tagged_union_enum_tag_trailing,
- .tagged_union_two,
- .tagged_union_two_trailing,
- => {
- var buf: [2]Ast.Node.Index = undefined;
- return walkContainerDecl(w, node, ast.fullContainerDecl(&buf, node).?);
- },
-
- .error_set_decl => {
- const error_token = main_tokens[node];
- const lbrace = error_token + 1;
- const rbrace = datas[node].rhs;
-
- var i = lbrace + 1;
- while (i < rbrace) : (i += 1) {
- switch (token_tags[i]) {
- .doc_comment => unreachable, // TODO
- .identifier => try walkIdentifier(w, i),
- .comma => {},
- else => unreachable,
- }
- }
- },
-
- .builtin_call_two, .builtin_call_two_comma => {
- if (datas[node].lhs == 0) {
- return walkBuiltinCall(w, node, &.{});
- } else if (datas[node].rhs == 0) {
- return walkBuiltinCall(w, node, &.{datas[node].lhs});
- } else {
- return walkBuiltinCall(w, node, &.{ datas[node].lhs, datas[node].rhs });
- }
- },
- .builtin_call, .builtin_call_comma => {
- const params = ast.extra_data[datas[node].lhs..datas[node].rhs];
- return walkBuiltinCall(w, node, params);
- },
-
- .fn_proto_simple,
- .fn_proto_multi,
- .fn_proto_one,
- .fn_proto,
- => {
- var buf: [1]Ast.Node.Index = undefined;
- return walkFnProto(w, ast.fullFnProto(&buf, node).?);
- },
-
- .anyframe_type => {
- if (datas[node].rhs != 0) {
- return walkExpression(w, datas[node].rhs);
- }
- },
-
- .@"switch",
- .switch_comma,
- => {
- const condition = datas[node].lhs;
- const extra = ast.extraData(datas[node].rhs, Ast.Node.SubRange);
- const cases = ast.extra_data[extra.start..extra.end];
-
- try walkExpression(w, condition); // condition expression
- try walkExpressions(w, cases);
- },
-
- .switch_case_one,
- .switch_case_inline_one,
- .switch_case,
- .switch_case_inline,
- => return walkSwitchCase(w, ast.fullSwitchCase(node).?),
-
- .while_simple,
- .while_cont,
- .@"while",
- => return walkWhile(w, node, ast.fullWhile(node).?),
-
- .for_simple,
- .@"for",
- => return walkFor(w, ast.fullFor(node).?),
-
- .if_simple,
- .@"if",
- => return walkIf(w, node, ast.fullIf(node).?),
-
- .asm_simple,
- .@"asm",
- => return walkAsm(w, ast.fullAsm(node).?),
-
- .enum_literal => {
- return walkIdentifier(w, main_tokens[node]); // name
- },
-
- .fn_decl => unreachable,
- .container_field => unreachable,
- .container_field_init => unreachable,
- .container_field_align => unreachable,
- .root => unreachable,
- .global_var_decl => unreachable,
- .local_var_decl => unreachable,
- .simple_var_decl => unreachable,
- .aligned_var_decl => unreachable,
- .@"usingnamespace" => unreachable,
- .test_decl => unreachable,
- .asm_output => unreachable,
- .asm_input => unreachable,
- }
-}
-
-fn walkGlobalVarDecl(w: *Walk, decl_node: Ast.Node.Index, var_decl: Ast.full.VarDecl) Error!void {
- _ = decl_node;
-
- if (var_decl.ast.type_node != 0) {
- try walkExpression(w, var_decl.ast.type_node);
- }
-
- if (var_decl.ast.align_node != 0) {
- try walkExpression(w, var_decl.ast.align_node);
- }
-
- if (var_decl.ast.addrspace_node != 0) {
- try walkExpression(w, var_decl.ast.addrspace_node);
- }
-
- if (var_decl.ast.section_node != 0) {
- try walkExpression(w, var_decl.ast.section_node);
- }
-
- if (var_decl.ast.init_node != 0) {
- if (!isUndefinedIdent(w.ast, var_decl.ast.init_node)) {
- try w.transformations.append(.{ .replace_with_undef = var_decl.ast.init_node });
- }
- try walkExpression(w, var_decl.ast.init_node);
- }
-}
-
-fn walkLocalVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void {
- try walkIdentifierNew(w, var_decl.ast.mut_token + 1); // name
-
- if (var_decl.ast.type_node != 0) {
- try walkExpression(w, var_decl.ast.type_node);
- }
-
- if (var_decl.ast.align_node != 0) {
- try walkExpression(w, var_decl.ast.align_node);
- }
-
- if (var_decl.ast.addrspace_node != 0) {
- try walkExpression(w, var_decl.ast.addrspace_node);
- }
-
- if (var_decl.ast.section_node != 0) {
- try walkExpression(w, var_decl.ast.section_node);
- }
-
- if (var_decl.ast.init_node != 0) {
- if (!isUndefinedIdent(w.ast, var_decl.ast.init_node)) {
- try w.transformations.append(.{ .replace_with_undef = var_decl.ast.init_node });
- }
- try walkExpression(w, var_decl.ast.init_node);
- }
-}
-
-fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void {
- if (field.ast.type_expr != 0) {
- try walkExpression(w, field.ast.type_expr); // type
- }
- if (field.ast.align_expr != 0) {
- try walkExpression(w, field.ast.align_expr); // alignment
- }
- if (field.ast.value_expr != 0) {
- try walkExpression(w, field.ast.value_expr); // value
- }
-}
-
-fn walkBlock(
- w: *Walk,
- block_node: Ast.Node.Index,
- statements: []const Ast.Node.Index,
-) Error!void {
- _ = block_node;
- const ast = w.ast;
- const node_tags = ast.nodes.items(.tag);
-
- for (statements) |stmt| {
- switch (node_tags[stmt]) {
- .global_var_decl,
- .local_var_decl,
- .simple_var_decl,
- .aligned_var_decl,
- => {
- const var_decl = ast.fullVarDecl(stmt).?;
- if (var_decl.ast.init_node != 0 and
- isUndefinedIdent(w.ast, var_decl.ast.init_node))
- {
- try w.transformations.append(.{ .delete_var_decl = .{
- .var_decl_node = stmt,
- .references = .{},
- } });
- const name_tok = var_decl.ast.mut_token + 1;
- const name_bytes = ast.tokenSlice(name_tok);
- try w.replace_names.put(w.gpa, name_bytes, @intCast(w.transformations.items.len - 1));
- } else {
- try walkLocalVarDecl(w, var_decl);
- }
- },
-
- else => {
- switch (categorizeStmt(ast, stmt)) {
- // Don't try to remove `_ = foo;` discards; those are handled separately.
- .discard_identifier => {},
- // definitely try to remove `_ = undefined;` though.
- .discard_undefined, .trap_call, .other => {
- try w.transformations.append(.{ .delete_node = stmt });
- },
- }
- try walkExpression(w, stmt);
- },
- }
- }
-}
-
-fn walkArrayType(w: *Walk, array_type: Ast.full.ArrayType) Error!void {
- try walkExpression(w, array_type.ast.elem_count);
- if (array_type.ast.sentinel != 0) {
- try walkExpression(w, array_type.ast.sentinel);
- }
- return walkExpression(w, array_type.ast.elem_type);
-}
-
-fn walkArrayInit(w: *Walk, array_init: Ast.full.ArrayInit) Error!void {
- if (array_init.ast.type_expr != 0) {
- try walkExpression(w, array_init.ast.type_expr); // T
- }
- for (array_init.ast.elements) |elem_init| {
- try walkExpression(w, elem_init);
- }
-}
-
-fn walkStructInit(
- w: *Walk,
- struct_node: Ast.Node.Index,
- struct_init: Ast.full.StructInit,
-) Error!void {
- _ = struct_node;
- if (struct_init.ast.type_expr != 0) {
- try walkExpression(w, struct_init.ast.type_expr); // T
- }
- for (struct_init.ast.fields) |field_init| {
- try walkExpression(w, field_init);
- }
-}
-
-fn walkCall(w: *Walk, call: Ast.full.Call) Error!void {
- try walkExpression(w, call.ast.fn_expr);
- try walkParamList(w, call.ast.params);
-}
-
-fn walkSlice(
- w: *Walk,
- slice_node: Ast.Node.Index,
- slice: Ast.full.Slice,
-) Error!void {
- _ = slice_node;
- try walkExpression(w, slice.ast.sliced);
- try walkExpression(w, slice.ast.start);
- if (slice.ast.end != 0) {
- try walkExpression(w, slice.ast.end);
- }
- if (slice.ast.sentinel != 0) {
- try walkExpression(w, slice.ast.sentinel);
- }
-}
-
-fn walkIdentifier(w: *Walk, name_ident: Ast.TokenIndex) Error!void {
- const ast = w.ast;
- const token_tags = ast.tokens.items(.tag);
- assert(token_tags[name_ident] == .identifier);
- const name_bytes = ast.tokenSlice(name_ident);
- _ = w.unreferenced_globals.swapRemove(name_bytes);
-}
-
-fn walkIdentifierNew(w: *Walk, name_ident: Ast.TokenIndex) Error!void {
- _ = w;
- _ = name_ident;
-}
-
-fn walkContainerDecl(
- w: *Walk,
- container_decl_node: Ast.Node.Index,
- container_decl: Ast.full.ContainerDecl,
-) Error!void {
- _ = container_decl_node;
- if (container_decl.ast.arg != 0) {
- try walkExpression(w, container_decl.ast.arg);
- }
- try walkMembers(w, container_decl.ast.members);
-}
-
-fn walkBuiltinCall(
- w: *Walk,
- call_node: Ast.Node.Index,
- params: []const Ast.Node.Index,
-) Error!void {
- const ast = w.ast;
- const main_tokens = ast.nodes.items(.main_token);
- const builtin_token = main_tokens[call_node];
- const builtin_name = ast.tokenSlice(builtin_token);
- const info = BuiltinFn.list.get(builtin_name).?;
- switch (info.tag) {
- .import => {
- const operand_node = params[0];
- const str_lit_token = main_tokens[operand_node];
- const token_bytes = ast.tokenSlice(str_lit_token);
- if (std.mem.endsWith(u8, token_bytes, ".zig\"")) {
- const imported_string = std.zig.string_literal.parseAlloc(w.arena, token_bytes) catch
- unreachable;
- try w.transformations.append(.{ .inline_imported_file = .{
- .builtin_call_node = call_node,
- .imported_string = imported_string,
- .in_scope_names = try std.StringArrayHashMapUnmanaged(void).init(
- w.arena,
- w.in_scope_names.keys(),
- &.{},
- ),
- } });
- }
- },
- else => {},
- }
- for (params) |param_node| {
- try walkExpression(w, param_node);
- }
-}
-
-fn walkFnProto(w: *Walk, fn_proto: Ast.full.FnProto) Error!void {
- const ast = w.ast;
-
- {
- var it = fn_proto.iterate(ast);
- while (it.next()) |param| {
- if (param.type_expr != 0) {
- try walkExpression(w, param.type_expr);
- }
- }
- }
-
- if (fn_proto.ast.align_expr != 0) {
- try walkExpression(w, fn_proto.ast.align_expr);
- }
-
- if (fn_proto.ast.addrspace_expr != 0) {
- try walkExpression(w, fn_proto.ast.addrspace_expr);
- }
-
- if (fn_proto.ast.section_expr != 0) {
- try walkExpression(w, fn_proto.ast.section_expr);
- }
-
- if (fn_proto.ast.callconv_expr != 0) {
- try walkExpression(w, fn_proto.ast.callconv_expr);
- }
-
- try walkExpression(w, fn_proto.ast.return_type);
-}
-
-fn walkExpressions(w: *Walk, expressions: []const Ast.Node.Index) Error!void {
- for (expressions) |expression| {
- try walkExpression(w, expression);
- }
-}
-
-fn walkSwitchCase(w: *Walk, switch_case: Ast.full.SwitchCase) Error!void {
- for (switch_case.ast.values) |value_expr| {
- try walkExpression(w, value_expr);
- }
- try walkExpression(w, switch_case.ast.target_expr);
-}
-
-fn walkWhile(w: *Walk, node_index: Ast.Node.Index, while_node: Ast.full.While) Error!void {
- assert(while_node.ast.cond_expr != 0);
- assert(while_node.ast.then_expr != 0);
-
- // Perform these transformations in this priority order:
- // 1. If the `else` expression is missing or an empty block, replace the condition with `if (true)` if it is not already.
- // 2. If the `then` block is empty, replace the condition with `if (false)` if it is not already.
- // 3. If the condition is `if (true)`, replace the `if` expression with the contents of the `then` expression.
- // 4. If the condition is `if (false)`, replace the `if` expression with the contents of the `else` expression.
- if (!isTrueIdent(w.ast, while_node.ast.cond_expr) and
- (while_node.ast.else_expr == 0 or isEmptyBlock(w.ast, while_node.ast.else_expr)))
- {
- try w.transformations.ensureUnusedCapacity(1);
- w.transformations.appendAssumeCapacity(.{ .replace_with_true = while_node.ast.cond_expr });
- } else if (!isFalseIdent(w.ast, while_node.ast.cond_expr) and isEmptyBlock(w.ast, while_node.ast.then_expr)) {
- try w.transformations.ensureUnusedCapacity(1);
- w.transformations.appendAssumeCapacity(.{ .replace_with_false = while_node.ast.cond_expr });
- } else if (isTrueIdent(w.ast, while_node.ast.cond_expr)) {
- try w.transformations.ensureUnusedCapacity(1);
- w.transformations.appendAssumeCapacity(.{ .replace_node = .{
- .to_replace = node_index,
- .replacement = while_node.ast.then_expr,
- } });
- } else if (isFalseIdent(w.ast, while_node.ast.cond_expr)) {
- try w.transformations.ensureUnusedCapacity(1);
- w.transformations.appendAssumeCapacity(.{ .replace_node = .{
- .to_replace = node_index,
- .replacement = while_node.ast.else_expr,
- } });
- }
-
- try walkExpression(w, while_node.ast.cond_expr); // condition
-
- if (while_node.ast.cont_expr != 0) {
- try walkExpression(w, while_node.ast.cont_expr);
- }
-
- if (while_node.ast.then_expr != 0) {
- try walkExpression(w, while_node.ast.then_expr);
- }
- if (while_node.ast.else_expr != 0) {
- try walkExpression(w, while_node.ast.else_expr);
- }
-}
-
-fn walkFor(w: *Walk, for_node: Ast.full.For) Error!void {
- try walkParamList(w, for_node.ast.inputs);
- if (for_node.ast.then_expr != 0) {
- try walkExpression(w, for_node.ast.then_expr);
- }
- if (for_node.ast.else_expr != 0) {
- try walkExpression(w, for_node.ast.else_expr);
- }
-}
-
-fn walkIf(w: *Walk, node_index: Ast.Node.Index, if_node: Ast.full.If) Error!void {
- assert(if_node.ast.cond_expr != 0);
- assert(if_node.ast.then_expr != 0);
-
- // Perform these transformations in this priority order:
- // 1. If the `else` expression is missing or an empty block, replace the condition with `if (true)` if it is not already.
- // 2. If the `then` block is empty, replace the condition with `if (false)` if it is not already.
- // 3. If the condition is `if (true)`, replace the `if` expression with the contents of the `then` expression.
- // 4. If the condition is `if (false)`, replace the `if` expression with the contents of the `else` expression.
- if (!isTrueIdent(w.ast, if_node.ast.cond_expr) and
- (if_node.ast.else_expr == 0 or isEmptyBlock(w.ast, if_node.ast.else_expr)))
- {
- try w.transformations.ensureUnusedCapacity(1);
- w.transformations.appendAssumeCapacity(.{ .replace_with_true = if_node.ast.cond_expr });
- } else if (!isFalseIdent(w.ast, if_node.ast.cond_expr) and isEmptyBlock(w.ast, if_node.ast.then_expr)) {
- try w.transformations.ensureUnusedCapacity(1);
- w.transformations.appendAssumeCapacity(.{ .replace_with_false = if_node.ast.cond_expr });
- } else if (isTrueIdent(w.ast, if_node.ast.cond_expr)) {
- try w.transformations.ensureUnusedCapacity(1);
- w.transformations.appendAssumeCapacity(.{ .replace_node = .{
- .to_replace = node_index,
- .replacement = if_node.ast.then_expr,
- } });
- } else if (isFalseIdent(w.ast, if_node.ast.cond_expr)) {
- try w.transformations.ensureUnusedCapacity(1);
- w.transformations.appendAssumeCapacity(.{ .replace_node = .{
- .to_replace = node_index,
- .replacement = if_node.ast.else_expr,
- } });
- }
-
- try walkExpression(w, if_node.ast.cond_expr); // condition
-
- if (if_node.ast.then_expr != 0) {
- try walkExpression(w, if_node.ast.then_expr);
- }
- if (if_node.ast.else_expr != 0) {
- try walkExpression(w, if_node.ast.else_expr);
- }
-}
-
-fn walkAsm(w: *Walk, asm_node: Ast.full.Asm) Error!void {
- try walkExpression(w, asm_node.ast.template);
- for (asm_node.ast.items) |item| {
- try walkExpression(w, item);
- }
-}
-
-fn walkParamList(w: *Walk, params: []const Ast.Node.Index) Error!void {
- for (params) |param_node| {
- try walkExpression(w, param_node);
- }
-}
-
-/// Check if it is already gutted (i.e. its body replaced with `@trap()`).
-fn isFnBodyGutted(ast: *const Ast, body_node: Ast.Node.Index) bool {
- // skip over discards
- const node_tags = ast.nodes.items(.tag);
- const datas = ast.nodes.items(.data);
- var statements_buf: [2]Ast.Node.Index = undefined;
- const statements = switch (node_tags[body_node]) {
- .block_two,
- .block_two_semicolon,
- => blk: {
- statements_buf[0..2].* = .{ datas[body_node].lhs, datas[body_node].rhs };
- break :blk if (datas[body_node].lhs == 0)
- statements_buf[0..0]
- else if (datas[body_node].rhs == 0)
- statements_buf[0..1]
- else
- statements_buf[0..2];
- },
-
- .block,
- .block_semicolon,
- => ast.extra_data[datas[body_node].lhs..datas[body_node].rhs],
-
- else => return false,
- };
- var i: usize = 0;
- while (i < statements.len) : (i += 1) {
- switch (categorizeStmt(ast, statements[i])) {
- .discard_identifier => continue,
- .trap_call => return i + 1 == statements.len,
- else => return false,
- }
- }
- return false;
-}
-
-const StmtCategory = enum {
- discard_undefined,
- discard_identifier,
- trap_call,
- other,
-};
-
-fn categorizeStmt(ast: *const Ast, stmt: Ast.Node.Index) StmtCategory {
- const node_tags = ast.nodes.items(.tag);
- const datas = ast.nodes.items(.data);
- const main_tokens = ast.nodes.items(.main_token);
- switch (node_tags[stmt]) {
- .builtin_call_two, .builtin_call_two_comma => {
- if (datas[stmt].lhs == 0) {
- return categorizeBuiltinCall(ast, main_tokens[stmt], &.{});
- } else if (datas[stmt].rhs == 0) {
- return categorizeBuiltinCall(ast, main_tokens[stmt], &.{datas[stmt].lhs});
- } else {
- return categorizeBuiltinCall(ast, main_tokens[stmt], &.{ datas[stmt].lhs, datas[stmt].rhs });
- }
- },
- .builtin_call, .builtin_call_comma => {
- const params = ast.extra_data[datas[stmt].lhs..datas[stmt].rhs];
- return categorizeBuiltinCall(ast, main_tokens[stmt], params);
- },
- .assign => {
- const infix = datas[stmt];
- if (isDiscardIdent(ast, infix.lhs) and node_tags[infix.rhs] == .identifier) {
- const name_bytes = ast.tokenSlice(main_tokens[infix.rhs]);
- if (std.mem.eql(u8, name_bytes, "undefined")) {
- return .discard_undefined;
- } else {
- return .discard_identifier;
- }
- }
- return .other;
- },
- else => return .other,
- }
-}
-
-fn categorizeBuiltinCall(
- ast: *const Ast,
- builtin_token: Ast.TokenIndex,
- params: []const Ast.Node.Index,
-) StmtCategory {
- if (params.len != 0) return .other;
- const name_bytes = ast.tokenSlice(builtin_token);
- if (std.mem.eql(u8, name_bytes, "@trap"))
- return .trap_call;
- return .other;
-}
-
-fn isDiscardIdent(ast: *const Ast, node: Ast.Node.Index) bool {
- return isMatchingIdent(ast, node, "_");
-}
-
-fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool {
- return isMatchingIdent(ast, node, "undefined");
-}
-
-fn isTrueIdent(ast: *const Ast, node: Ast.Node.Index) bool {
- return isMatchingIdent(ast, node, "true");
-}
-
-fn isFalseIdent(ast: *const Ast, node: Ast.Node.Index) bool {
- return isMatchingIdent(ast, node, "false");
-}
-
-fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bool {
- const node_tags = ast.nodes.items(.tag);
- const main_tokens = ast.nodes.items(.main_token);
- switch (node_tags[node]) {
- .identifier => {
- const token_index = main_tokens[node];
- const name_bytes = ast.tokenSlice(token_index);
- return std.mem.eql(u8, name_bytes, string);
- },
- else => return false,
- }
-}
-
-fn isEmptyBlock(ast: *const Ast, node: Ast.Node.Index) bool {
- const node_tags = ast.nodes.items(.tag);
- const node_data = ast.nodes.items(.data);
- switch (node_tags[node]) {
- .block_two => {
- return node_data[node].lhs == 0 and node_data[node].rhs == 0;
- },
- else => return false,
- }
-}