diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index ce63c08533..348b75159f 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -26,6 +26,9 @@ pub const Fixups = struct { omit_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{}, /// These expressions will be replaced with the string value. replace_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, []const u8) = .{}, + /// Change all identifier names matching the key to be value instead. + rename_identifiers: std.StringArrayHashMapUnmanaged([]const u8) = .{}, + /// All `@import` builtin calls which refer to a file path will be prefixed /// with this path. rebase_imported_paths: ?[]const u8 = null, @@ -34,7 +37,9 @@ pub const Fixups = struct { return f.unused_var_decls.count() + f.gut_functions.count() + f.omit_nodes.count() + - f.replace_nodes.count(); + f.replace_nodes.count() + + f.rename_identifiers.count() + + @intFromBool(f.rebase_imported_paths != null); } pub fn clearRetainingCapacity(f: *Fixups) void { @@ -42,6 +47,9 @@ pub const Fixups = struct { f.gut_functions.clearRetainingCapacity(); f.omit_nodes.clearRetainingCapacity(); f.replace_nodes.clearRetainingCapacity(); + f.rename_identifiers.clearRetainingCapacity(); + + f.rebase_imported_paths = null; } pub fn deinit(f: *Fixups, gpa: Allocator) void { @@ -49,6 +57,7 @@ pub const Fixups = struct { f.gut_functions.deinit(gpa); f.omit_nodes.deinit(gpa); f.replace_nodes.deinit(gpa); + f.rename_identifiers.deinit(gpa); f.* = undefined; } }; @@ -2833,6 +2842,13 @@ fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote const token_tags = tree.tokens.items(.tag); assert(token_tags[token_index] == .identifier); const lexeme = tokenSliceForRender(tree, token_index); + + if (r.fixups.rename_identifiers.get(lexeme)) |mangled| { + try r.ais.writer().writeAll(mangled); + try renderSpace(r, token_index, lexeme.len, space); + return; + } + if (lexeme[0] != '@') { return renderToken(r, token_index, space); } diff --git a/src/reduce.zig b/src/reduce.zig index 82e4bc9031..d8af4dddc3 100644 --- a/src/reduce.zig +++ b/src/reduce.zig @@ -148,7 +148,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { var transformations = std.ArrayList(Walk.Transformation).init(gpa); defer transformations.deinit(); - try Walk.findTransformations(&tree, &transformations); + try Walk.findTransformations(arena, &tree, &transformations); sortTransformations(transformations.items, rng.random()); fresh: while (transformations.items.len > 0) { @@ -162,12 +162,19 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { subset_size = @max(1, subset_size / 2); 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); 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} remaining\n", .{ subset_size, @tagName(interestingness), transformations.items.len - start_index, @@ -179,7 +186,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { tree.deinit(gpa); tree = new_tree; - try Walk.findTransformations(&tree, &transformations); + try Walk.findTransformations(arena, &tree, &transformations); // Resetting based on the seed again means we will get the same // results if restarting the reduction process from this new point. rng = std.rand.DefaultPrng.init(seed); @@ -263,7 +270,6 @@ fn transformationsToFixups( try fixups.replace_nodes.put(gpa, node, "undefined"); }, .inline_imported_file => |inline_imported_file| { - defer gpa.free(inline_imported_file.imported_string); const full_imported_path = try std.fs.path.join(gpa, &.{ std.fs.path.dirname(root_source_file_path) orelse ".", inline_imported_file.imported_string, @@ -274,14 +280,34 @@ fn transformationsToFixups( gpa.free(other_file_ast.source); other_file_ast.deinit(gpa); } - var other_source = std.ArrayList(u8).init(gpa); - defer other_source.deinit(); + 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, .{ - .rebase_imported_paths = std.fs.path.dirname(inline_imported_file.imported_string), - }); + try other_file_ast.renderToArrayList(&other_source, inlined_fixups); try other_source.appendSlice("}"); try fixups.replace_nodes.put( diff --git a/src/reduce/Walk.zig b/src/reduce/Walk.zig index c68e58a4fa..d62cc4e905 100644 --- a/src/reduce/Walk.zig +++ b/src/reduce/Walk.zig @@ -7,7 +7,9 @@ const BuiltinFn = @import("../BuiltinFn.zig"); ast: *const Ast, transformations: *std.ArrayList(Transformation), unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index), +in_scope_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 @@ -18,25 +20,39 @@ pub const Transformation = union(enum) { /// Replace an expression with `undefined`. replace_with_undef: Ast.Node.Index, /// Replace an `@import` with the imported file contents wrapped in a struct. - inline_imported_file: 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(ast: *const Ast, transformations: *std.ArrayList(Transformation)) !void { +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 = .{}, }; - defer walk.unreferenced_globals.deinit(walk.gpa); + defer { + walk.unreferenced_globals.deinit(walk.gpa); + walk.in_scope_names.deinit(walk.gpa); + } try walkMembers(&walk, walk.ast.rootDecls()); @@ -49,14 +65,18 @@ pub fn findTransformations(ast: *const Ast, transformations: *std.ArrayList(Tran 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); + try scanDecls(w, members, .add); for (members) |member| { try walkMember(w, member); } + + try scanDecls(w, members, .remove); } -fn scanDecls(w: *Walk, members: []const Ast.Node.Index) Error!void { +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); @@ -80,9 +100,27 @@ fn scanDecls(w: *Walk, members: []const Ast.Node.Index) Error!void { else => continue, }; + assert(token_tags[name_token] == .identifier); const name_bytes = ast.tokenSlice(name_token); - try w.unreferenced_globals.put(gpa, name_bytes, member_node); + + 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; + } + }, + } } } @@ -567,12 +605,12 @@ fn walkLocalVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void { try walkExpression(w, var_decl.ast.section_node); } - assert(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 }); + 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); } - - return walkExpression(w, var_decl.ast.init_node); } fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void { @@ -582,7 +620,9 @@ fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void { if (field.ast.align_expr != 0) { try walkExpression(w, field.ast.align_expr); // alignment } - try walkExpression(w, field.ast.value_expr); // value + if (field.ast.value_expr != 0) { + try walkExpression(w, field.ast.value_expr); // value + } } fn walkBlock( @@ -690,7 +730,6 @@ fn walkBuiltinCall( params: []const Ast.Node.Index, ) Error!void { const ast = w.ast; - const gpa = w.gpa; const main_tokens = ast.nodes.items(.main_token); const builtin_token = main_tokens[call_node]; const builtin_name = ast.tokenSlice(builtin_token); @@ -700,12 +739,17 @@ fn walkBuiltinCall( const operand_node = params[0]; const str_lit_token = main_tokens[operand_node]; const token_bytes = ast.tokenSlice(str_lit_token); - const imported_string = std.zig.string_literal.parseAlloc(gpa, token_bytes) catch - unreachable; - if (std.mem.endsWith(u8, imported_string, ".zig")) { + 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(), + &.{}, + ), } }); } },