Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

main

reduce.main
pub fn main(init: std.process.Init) !void

File

lib/compiler/reduce.zig:50

Code

pub fn main(init: std.process.Init) !void {
    const arena = init.arena.allocator();
    const gpa = init.gpa;
    const io = init.io;
    const args = try init.minimal.args.toSlice(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")) {
                    try Io.File.stdout().writeStreamingAll(io, usage);
                    return std.process.cleanExit(io);
                } 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: {t}", .{ next_arg, 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.ArrayList([]const u8) = .empty;
    try interestingness_argv.ensureUnusedCapacity(arena, argv.len + 1);
    interestingness_argv.appendAssumeCapacity(checker_path);
    interestingness_argv.appendSliceAssumeCapacity(argv);

    var rendered: std.Io.Writer.Allocating = .init(gpa);
    defer rendered.deinit();

    var astgen_input: std.Io.Writer.Allocating = .init(gpa);
    defer astgen_input.deinit();

    var tree = try parse(gpa, io, 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, io, interestingness_argv.items)) {
            .interesting => {},
            .boring, .unknown => |t| {
                fatal("interestingness check returned {t} for unmodified input\n", .{t});
            },
        }
    }

    var fixups: Ast.Render.Fixups = .{};
    defer fixups.deinit(gpa);

    var more_fixups: Ast.Render.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.array_list.Managed(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, io, root_source_file_path, this_set, &fixups);

            rendered.clearRetainingCapacity();
            try tree.render(gpa, &rendered.writer, 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.writer.writeAll(rendered.written());
                try astgen_input.writer.writeByte(0);
                const source_with_null = astgen_input.written()[0..(astgen_input.written().len - 1) :0];
                var astgen_tree = try Ast.parse(gpa, source_with_null, .{});
                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[@backingInt(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.unwrap().?;
                            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.render(gpa, &rendered.writer, more_fixups);
                    }
                }
            }

            try Io.Dir.cwd().writeFile(io, .{ .sub_path = root_source_file_path, .data = rendered.written() });
            // std.debug.print("trying this code:\n{s}\n", .{rendered.items});

            const interestingness = try runCheck(arena, io, interestingness_argv.items);
            std.debug.print("{d} random transformations: {t}. {d}/{d}\n", .{
                subset_size, interestingness, start_index, transformations.items.len,
            });
            switch (interestingness) {
                .interesting => {
                    const new_tree = try parse(gpa, io, 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.render(gpa, &rendered.writer, fixups);
        try Io.Dir.cwd().writeFile(io, .{ .sub_path = root_source_file_path, .data = rendered.written() });

        return std.process.cleanExit(io);
    }
    std.debug.print("no more transformations found\n", .{});
    return std.process.cleanExit(io);
}