| author | |
| committer | |
| log | dc63426b1eaef9c65152c8e052e5552e593e9382 |
| tree | 4baa141bdd1da68a3f15164988317350f95b76c7 |
| parent | f24ceec35a6fd1e5e6a671461b78919b5f588a32 |
| parent | a9002156a09130038abcf418609fea725ce71bc2 |
| signature |
zig reduce: support inlining `@import` and more4 files changed, 433 insertions(+), 79 deletions(-)
lib/std/array_hash_map.zig+13| ... | @@ -574,6 +574,19 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -574,6 +574,19 @@ pub fn ArrayHashMapUnmanaged( |
| 574 | }; | 574 | }; |
| 575 | } | 575 | } |
| 576 | 576 | ||
| 577 | pub fn init(allocator: Allocator, key_list: []const K, value_list: []const V) !Self { | ||
| 578 | var self: Self = .{}; | ||
| 579 | try self.entries.resize(allocator, key_list.len); | ||
| 580 | errdefer self.entries.deinit(allocator); | ||
| 581 | @memcpy(self.keys(), key_list); | ||
| 582 | if (@sizeOf(V) != 0) { | ||
| 583 | assert(key_list.len == value_list.len); | ||
| 584 | @memcpy(self.values(), value_list); | ||
| 585 | } | ||
| 586 | try self.reIndex(allocator); | ||
| 587 | return self; | ||
| 588 | } | ||
| 589 | |||
| 577 | /// Frees the backing allocation and leaves the map in an undefined state. | 590 | /// Frees the backing allocation and leaves the map in an undefined state. |
| 578 | /// Note that this does not free keys or values. You must take care of that | 591 | /// Note that this does not free keys or values. You must take care of that |
| 579 | /// before calling this function, if it is needed. | 592 | /// before calling this function, if it is needed. |
lib/std/zig/render.zig+114-20| ... | @@ -24,14 +24,22 @@ pub const Fixups = struct { | ... | @@ -24,14 +24,22 @@ pub const Fixups = struct { |
| 24 | gut_functions: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{}, | 24 | gut_functions: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{}, |
| 25 | /// These global declarations will be omitted. | 25 | /// These global declarations will be omitted. |
| 26 | omit_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{}, | 26 | omit_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{}, |
| 27 | /// These expressions will be replaced with `undefined`. | 27 | /// These expressions will be replaced with the string value. |
| 28 | replace_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{}, | 28 | replace_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, []const u8) = .{}, |
| 29 | /// Change all identifier names matching the key to be value instead. | ||
| 30 | rename_identifiers: std.StringArrayHashMapUnmanaged([]const u8) = .{}, | ||
| 31 | |||
| 32 | /// All `@import` builtin calls which refer to a file path will be prefixed | ||
| 33 | /// with this path. | ||
| 34 | rebase_imported_paths: ?[]const u8 = null, | ||
| 29 | 35 | ||
| 30 | pub fn count(f: Fixups) usize { | 36 | pub fn count(f: Fixups) usize { |
| 31 | return f.unused_var_decls.count() + | 37 | return f.unused_var_decls.count() + |
| 32 | f.gut_functions.count() + | 38 | f.gut_functions.count() + |
| 33 | f.omit_nodes.count() + | 39 | f.omit_nodes.count() + |
| 34 | f.replace_nodes.count(); | 40 | f.replace_nodes.count() + |
| 41 | f.rename_identifiers.count() + | ||
| 42 | @intFromBool(f.rebase_imported_paths != null); | ||
| 35 | } | 43 | } |
| 36 | 44 | ||
| 37 | pub fn clearRetainingCapacity(f: *Fixups) void { | 45 | pub fn clearRetainingCapacity(f: *Fixups) void { |
| ... | @@ -39,6 +47,9 @@ pub const Fixups = struct { | ... | @@ -39,6 +47,9 @@ pub const Fixups = struct { |
| 39 | f.gut_functions.clearRetainingCapacity(); | 47 | f.gut_functions.clearRetainingCapacity(); |
| 40 | f.omit_nodes.clearRetainingCapacity(); | 48 | f.omit_nodes.clearRetainingCapacity(); |
| 41 | f.replace_nodes.clearRetainingCapacity(); | 49 | f.replace_nodes.clearRetainingCapacity(); |
| 50 | f.rename_identifiers.clearRetainingCapacity(); | ||
| 51 | |||
| 52 | f.rebase_imported_paths = null; | ||
| 42 | } | 53 | } |
| 43 | 54 | ||
| 44 | pub fn deinit(f: *Fixups, gpa: Allocator) void { | 55 | pub fn deinit(f: *Fixups, gpa: Allocator) void { |
| ... | @@ -46,6 +57,7 @@ pub const Fixups = struct { | ... | @@ -46,6 +57,7 @@ pub const Fixups = struct { |
| 46 | f.gut_functions.deinit(gpa); | 57 | f.gut_functions.deinit(gpa); |
| 47 | f.omit_nodes.deinit(gpa); | 58 | f.omit_nodes.deinit(gpa); |
| 48 | f.replace_nodes.deinit(gpa); | 59 | f.replace_nodes.deinit(gpa); |
| 60 | f.rename_identifiers.deinit(gpa); | ||
| 49 | f.* = undefined; | 61 | f.* = undefined; |
| 50 | } | 62 | } |
| 51 | }; | 63 | }; |
| ... | @@ -121,6 +133,7 @@ fn renderMember( | ... | @@ -121,6 +133,7 @@ fn renderMember( |
| 121 | ) Error!void { | 133 | ) Error!void { |
| 122 | const tree = r.tree; | 134 | const tree = r.tree; |
| 123 | const ais = r.ais; | 135 | const ais = r.ais; |
| 136 | const node_tags = tree.nodes.items(.tag); | ||
| 124 | const token_tags = tree.tokens.items(.tag); | 137 | const token_tags = tree.tokens.items(.tag); |
| 125 | const main_tokens = tree.nodes.items(.main_token); | 138 | const main_tokens = tree.nodes.items(.main_token); |
| 126 | const datas = tree.nodes.items(.data); | 139 | const datas = tree.nodes.items(.data); |
| ... | @@ -182,6 +195,45 @@ fn renderMember( | ... | @@ -182,6 +195,45 @@ fn renderMember( |
| 182 | ais.popIndent(); | 195 | ais.popIndent(); |
| 183 | try ais.insertNewline(); | 196 | try ais.insertNewline(); |
| 184 | try renderToken(r, tree.lastToken(body_node), space); // rbrace | 197 | try renderToken(r, tree.lastToken(body_node), space); // rbrace |
| 198 | } else if (r.fixups.unused_var_decls.count() != 0) { | ||
| 199 | ais.pushIndentNextLine(); | ||
| 200 | const lbrace = tree.nodes.items(.main_token)[body_node]; | ||
| 201 | try renderToken(r, lbrace, .newline); | ||
| 202 | |||
| 203 | var fn_proto_buf: [1]Ast.Node.Index = undefined; | ||
| 204 | const full_fn_proto = tree.fullFnProto(&fn_proto_buf, fn_proto).?; | ||
| 205 | var it = full_fn_proto.iterate(&tree); | ||
| 206 | while (it.next()) |param| { | ||
| 207 | const name_ident = param.name_token.?; | ||
| 208 | assert(token_tags[name_ident] == .identifier); | ||
| 209 | if (r.fixups.unused_var_decls.contains(name_ident)) { | ||
| 210 | const w = ais.writer(); | ||
| 211 | try w.writeAll("_ = "); | ||
| 212 | try w.writeAll(tokenSliceForRender(r.tree, name_ident)); | ||
| 213 | try w.writeAll(";\n"); | ||
| 214 | } | ||
| 215 | } | ||
| 216 | var statements_buf: [2]Ast.Node.Index = undefined; | ||
| 217 | const statements = switch (node_tags[body_node]) { | ||
| 218 | .block_two, | ||
| 219 | .block_two_semicolon, | ||
| 220 | => b: { | ||
| 221 | statements_buf = .{ datas[body_node].lhs, datas[body_node].rhs }; | ||
| 222 | if (datas[body_node].lhs == 0) { | ||
| 223 | break :b statements_buf[0..0]; | ||
| 224 | } else if (datas[body_node].rhs == 0) { | ||
| 225 | break :b statements_buf[0..1]; | ||
| 226 | } else { | ||
| 227 | break :b statements_buf[0..2]; | ||
| 228 | } | ||
| 229 | }, | ||
| 230 | .block, | ||
| 231 | .block_semicolon, | ||
| 232 | => tree.extra_data[datas[body_node].lhs..datas[body_node].rhs], | ||
| 233 | |||
| 234 | else => unreachable, | ||
| 235 | }; | ||
| 236 | return finishRenderBlock(r, body_node, statements, space); | ||
| 185 | } else { | 237 | } else { |
| 186 | return renderExpression(r, body_node, space); | 238 | return renderExpression(r, body_node, space); |
| 187 | } | 239 | } |
| ... | @@ -277,8 +329,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { | ... | @@ -277,8 +329,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { |
| 277 | const main_tokens = tree.nodes.items(.main_token); | 329 | const main_tokens = tree.nodes.items(.main_token); |
| 278 | const node_tags = tree.nodes.items(.tag); | 330 | const node_tags = tree.nodes.items(.tag); |
| 279 | const datas = tree.nodes.items(.data); | 331 | const datas = tree.nodes.items(.data); |
| 280 | if (r.fixups.replace_nodes.contains(node)) { | 332 | if (r.fixups.replace_nodes.get(node)) |replacement| { |
| 281 | try ais.writer().writeAll("undefined"); | 333 | try ais.writer().writeAll(replacement); |
| 282 | try renderOnlySpace(r, space); | 334 | try renderOnlySpace(r, space); |
| 283 | return; | 335 | return; |
| 284 | } | 336 | } |
| ... | @@ -1057,7 +1109,7 @@ fn renderVarDecl( | ... | @@ -1057,7 +1109,7 @@ fn renderVarDecl( |
| 1057 | space: Space, | 1109 | space: Space, |
| 1058 | ) Error!void { | 1110 | ) Error!void { |
| 1059 | try renderVarDeclWithoutFixups(r, var_decl, ignore_comptime_token, space); | 1111 | try renderVarDeclWithoutFixups(r, var_decl, ignore_comptime_token, space); |
| 1060 | if (r.fixups.unused_var_decls.contains(var_decl.ast.mut_token)) { | 1112 | if (r.fixups.unused_var_decls.contains(var_decl.ast.mut_token + 1)) { |
| 1061 | // Discard the variable like this: `_ = foo;` | 1113 | // Discard the variable like this: `_ = foo;` |
| 1062 | const w = r.ais.writer(); | 1114 | const w = r.ais.writer(); |
| 1063 | try w.writeAll("_ = "); | 1115 | try w.writeAll("_ = "); |
| ... | @@ -1515,6 +1567,7 @@ fn renderBuiltinCall( | ... | @@ -1515,6 +1567,7 @@ fn renderBuiltinCall( |
| 1515 | const tree = r.tree; | 1567 | const tree = r.tree; |
| 1516 | const ais = r.ais; | 1568 | const ais = r.ais; |
| 1517 | const token_tags = tree.tokens.items(.tag); | 1569 | const token_tags = tree.tokens.items(.tag); |
| 1570 | const main_tokens = tree.nodes.items(.main_token); | ||
| 1518 | 1571 | ||
| 1519 | // TODO remove before release of 0.12.0 | 1572 | // TODO remove before release of 0.12.0 |
| 1520 | const slice = tree.tokenSlice(builtin_token); | 1573 | const slice = tree.tokenSlice(builtin_token); |
| ... | @@ -1609,6 +1662,26 @@ fn renderBuiltinCall( | ... | @@ -1609,6 +1662,26 @@ fn renderBuiltinCall( |
| 1609 | return renderToken(r, builtin_token + 2, space); // ) | 1662 | return renderToken(r, builtin_token + 2, space); // ) |
| 1610 | } | 1663 | } |
| 1611 | 1664 | ||
| 1665 | if (r.fixups.rebase_imported_paths) |prefix| { | ||
| 1666 | if (mem.eql(u8, slice, "@import")) f: { | ||
| 1667 | const param = params[0]; | ||
| 1668 | const str_lit_token = main_tokens[param]; | ||
| 1669 | assert(token_tags[str_lit_token] == .string_literal); | ||
| 1670 | const token_bytes = tree.tokenSlice(str_lit_token); | ||
| 1671 | const imported_string = std.zig.string_literal.parseAlloc(r.gpa, token_bytes) catch |err| switch (err) { | ||
| 1672 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1673 | error.InvalidLiteral => break :f, | ||
| 1674 | }; | ||
| 1675 | defer r.gpa.free(imported_string); | ||
| 1676 | const new_string = try std.fs.path.resolvePosix(r.gpa, &.{ prefix, imported_string }); | ||
| 1677 | defer r.gpa.free(new_string); | ||
| 1678 | |||
| 1679 | try renderToken(r, builtin_token + 1, .none); // ( | ||
| 1680 | try ais.writer().print("\"{}\"", .{std.zig.fmtEscapes(new_string)}); | ||
| 1681 | return renderToken(r, str_lit_token + 1, space); // ) | ||
| 1682 | } | ||
| 1683 | } | ||
| 1684 | |||
| 1612 | const last_param = params[params.len - 1]; | 1685 | const last_param = params[params.len - 1]; |
| 1613 | const after_last_param_token = tree.lastToken(last_param) + 1; | 1686 | const after_last_param_token = tree.lastToken(last_param) + 1; |
| 1614 | 1687 | ||
| ... | @@ -1934,7 +2007,6 @@ fn renderBlock( | ... | @@ -1934,7 +2007,6 @@ fn renderBlock( |
| 1934 | const tree = r.tree; | 2007 | const tree = r.tree; |
| 1935 | const ais = r.ais; | 2008 | const ais = r.ais; |
| 1936 | const token_tags = tree.tokens.items(.tag); | 2009 | const token_tags = tree.tokens.items(.tag); |
| 1937 | const node_tags = tree.nodes.items(.tag); | ||
| 1938 | const lbrace = tree.nodes.items(.main_token)[block_node]; | 2010 | const lbrace = tree.nodes.items(.main_token)[block_node]; |
| 1939 | 2011 | ||
| 1940 | if (token_tags[lbrace - 1] == .colon and | 2012 | if (token_tags[lbrace - 1] == .colon and |
| ... | @@ -1943,22 +2015,37 @@ fn renderBlock( | ... | @@ -1943,22 +2015,37 @@ fn renderBlock( |
| 1943 | try renderIdentifier(r, lbrace - 2, .none, .eagerly_unquote); // identifier | 2015 | try renderIdentifier(r, lbrace - 2, .none, .eagerly_unquote); // identifier |
| 1944 | try renderToken(r, lbrace - 1, .space); // : | 2016 | try renderToken(r, lbrace - 1, .space); // : |
| 1945 | } | 2017 | } |
| 1946 | |||
| 1947 | ais.pushIndentNextLine(); | 2018 | ais.pushIndentNextLine(); |
| 1948 | if (statements.len == 0) { | 2019 | if (statements.len == 0) { |
| 1949 | try renderToken(r, lbrace, .none); | 2020 | try renderToken(r, lbrace, .none); |
| 1950 | } else { | 2021 | ais.popIndent(); |
| 1951 | try renderToken(r, lbrace, .newline); | 2022 | try renderToken(r, tree.lastToken(block_node), space); // rbrace |
| 1952 | for (statements, 0..) |stmt, i| { | 2023 | return; |
| 1953 | if (i != 0) try renderExtraNewline(r, stmt); | 2024 | } |
| 1954 | switch (node_tags[stmt]) { | 2025 | try renderToken(r, lbrace, .newline); |
| 1955 | .global_var_decl, | 2026 | return finishRenderBlock(r, block_node, statements, space); |
| 1956 | .local_var_decl, | 2027 | } |
| 1957 | .simple_var_decl, | 2028 | |
| 1958 | .aligned_var_decl, | 2029 | fn finishRenderBlock( |
| 1959 | => try renderVarDecl(r, tree.fullVarDecl(stmt).?, false, .semicolon), | 2030 | r: *Render, |
| 1960 | else => try renderExpression(r, stmt, .semicolon), | 2031 | block_node: Ast.Node.Index, |
| 1961 | } | 2032 | statements: []const Ast.Node.Index, |
| 2033 | space: Space, | ||
| 2034 | ) Error!void { | ||
| 2035 | const tree = r.tree; | ||
| 2036 | const node_tags = tree.nodes.items(.tag); | ||
| 2037 | const ais = r.ais; | ||
| 2038 | for (statements, 0..) |stmt, i| { | ||
| 2039 | if (i != 0) try renderExtraNewline(r, stmt); | ||
| 2040 | if (r.fixups.omit_nodes.contains(stmt)) continue; | ||
| 2041 | switch (node_tags[stmt]) { | ||
| 2042 | .global_var_decl, | ||
| 2043 | .local_var_decl, | ||
| 2044 | .simple_var_decl, | ||
| 2045 | .aligned_var_decl, | ||
| 2046 | => try renderVarDecl(r, tree.fullVarDecl(stmt).?, false, .semicolon), | ||
| 2047 | |||
| 2048 | else => try renderExpression(r, stmt, .semicolon), | ||
| 1962 | } | 2049 | } |
| 1963 | } | 2050 | } |
| 1964 | ais.popIndent(); | 2051 | ais.popIndent(); |
| ... | @@ -2809,6 +2896,13 @@ fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote | ... | @@ -2809,6 +2896,13 @@ fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote |
| 2809 | const token_tags = tree.tokens.items(.tag); | 2896 | const token_tags = tree.tokens.items(.tag); |
| 2810 | assert(token_tags[token_index] == .identifier); | 2897 | assert(token_tags[token_index] == .identifier); |
| 2811 | const lexeme = tokenSliceForRender(tree, token_index); | 2898 | const lexeme = tokenSliceForRender(tree, token_index); |
| 2899 | |||
| 2900 | if (r.fixups.rename_identifiers.get(lexeme)) |mangled| { | ||
| 2901 | try r.ais.writer().writeAll(mangled); | ||
| 2902 | try renderSpace(r, token_index, lexeme.len, space); | ||
| 2903 | return; | ||
| 2904 | } | ||
| 2905 | |||
| 2812 | if (lexeme[0] != '@') { | 2906 | if (lexeme[0] != '@') { |
| 2813 | return renderToken(r, token_index, space); | 2907 | return renderToken(r, token_index, space); |
| 2814 | } | 2908 | } |
src/reduce.zig+144-20| ... | @@ -5,6 +5,8 @@ const assert = std.debug.assert; | ... | @@ -5,6 +5,8 @@ const assert = std.debug.assert; |
| 5 | const fatal = @import("./main.zig").fatal; | 5 | const fatal = @import("./main.zig").fatal; |
| 6 | const Ast = std.zig.Ast; | 6 | const Ast = std.zig.Ast; |
| 7 | const Walk = @import("reduce/Walk.zig"); | 7 | const Walk = @import("reduce/Walk.zig"); |
| 8 | const AstGen = @import("AstGen.zig"); | ||
| 9 | const Zir = @import("Zir.zig"); | ||
| 8 | 10 | ||
| 9 | const usage = | 11 | const usage = |
| 10 | \\zig reduce [options] ./checker root_source_file.zig [-- [argv]] | 12 | \\zig reduce [options] ./checker root_source_file.zig [-- [argv]] |
| ... | @@ -39,8 +41,6 @@ const Interestingness = enum { interesting, unknown, boring }; | ... | @@ -39,8 +41,6 @@ const Interestingness = enum { interesting, unknown, boring }; |
| 39 | // - add support for parsing the module flags | 41 | // - add support for parsing the module flags |
| 40 | // - more fancy transformations | 42 | // - more fancy transformations |
| 41 | // - @import inlining of modules | 43 | // - @import inlining of modules |
| 42 | // - @import inlining of files | ||
| 43 | // - deleting unused functions and other globals | ||
| 44 | // - removing statements or blocks of code | 44 | // - removing statements or blocks of code |
| 45 | // - replacing operands of `and` and `or` with `true` and `false` | 45 | // - replacing operands of `and` and `or` with `true` and `false` |
| 46 | // - replacing if conditions with `true` and `false` | 46 | // - replacing if conditions with `true` and `false` |
| ... | @@ -109,8 +109,14 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | ... | @@ -109,8 +109,14 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 109 | var rendered = std.ArrayList(u8).init(gpa); | 109 | var rendered = std.ArrayList(u8).init(gpa); |
| 110 | defer rendered.deinit(); | 110 | defer rendered.deinit(); |
| 111 | 111 | ||
| 112 | var tree = try parse(gpa, arena, root_source_file_path); | 112 | var astgen_input = std.ArrayList(u8).init(gpa); |
| 113 | defer tree.deinit(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 | } | ||
| 114 | 120 | ||
| 115 | if (!skip_smoke_test) { | 121 | if (!skip_smoke_test) { |
| 116 | std.debug.print("smoke testing the interestingness check...\n", .{}); | 122 | std.debug.print("smoke testing the interestingness check...\n", .{}); |
| ... | @@ -126,6 +132,10 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | ... | @@ -126,6 +132,10 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 126 | 132 | ||
| 127 | var fixups: Ast.Fixups = .{}; | 133 | var fixups: Ast.Fixups = .{}; |
| 128 | defer fixups.deinit(gpa); | 134 | defer fixups.deinit(gpa); |
| 135 | |||
| 136 | var more_fixups: Ast.Fixups = .{}; | ||
| 137 | defer more_fixups.deinit(gpa); | ||
| 138 | |||
| 129 | var rng = std.rand.DefaultPrng.init(seed); | 139 | var rng = std.rand.DefaultPrng.init(seed); |
| 130 | 140 | ||
| 131 | // 1. Walk the AST of the source file looking for independent | 141 | // 1. Walk the AST of the source file looking for independent |
| ... | @@ -145,7 +155,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | ... | @@ -145,7 +155,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 145 | 155 | ||
| 146 | var transformations = std.ArrayList(Walk.Transformation).init(gpa); | 156 | var transformations = std.ArrayList(Walk.Transformation).init(gpa); |
| 147 | defer transformations.deinit(); | 157 | defer transformations.deinit(); |
| 148 | try Walk.findTransformations(&tree, &transformations); | 158 | try Walk.findTransformations(arena, &tree, &transformations); |
| 149 | sortTransformations(transformations.items, rng.random()); | 159 | sortTransformations(transformations.items, rng.random()); |
| 150 | 160 | ||
| 151 | fresh: while (transformations.items.len > 0) { | 161 | fresh: while (transformations.items.len > 0) { |
| ... | @@ -156,29 +166,80 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | ... | @@ -156,29 +166,80 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 156 | var start_index: usize = 0; | 166 | var start_index: usize = 0; |
| 157 | 167 | ||
| 158 | while (start_index < transformations.items.len) { | 168 | while (start_index < transformations.items.len) { |
| 159 | subset_size = @max(1, subset_size / 2); | 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; | ||
| 160 | 173 | ||
| 161 | const this_set = transformations.items[start_index..][0..subset_size]; | 174 | const this_set = transformations.items[start_index..][0..subset_size]; |
| 162 | try transformationsToFixups(gpa, this_set, &fixups); | 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); | ||
| 163 | 181 | ||
| 164 | rendered.clearRetainingCapacity(); | 182 | rendered.clearRetainingCapacity(); |
| 165 | try tree.renderToArrayList(&rendered, fixups); | 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 | |||
| 166 | try std.fs.cwd().writeFile(root_source_file_path, rendered.items); | 228 | try std.fs.cwd().writeFile(root_source_file_path, rendered.items); |
| 229 | //std.debug.print("trying this code:\n{s}\n", .{rendered.items}); | ||
| 167 | 230 | ||
| 168 | const interestingness = try runCheck(arena, interestingness_argv.items); | 231 | const interestingness = try runCheck(arena, interestingness_argv.items); |
| 169 | std.debug.print("{d} random transformations: {s}. {d} remaining\n", .{ | 232 | std.debug.print("{d} random transformations: {s}. {d}/{d}\n", .{ |
| 170 | subset_size, @tagName(interestingness), transformations.items.len - start_index, | 233 | subset_size, @tagName(interestingness), start_index, transformations.items.len, |
| 171 | }); | 234 | }); |
| 172 | switch (interestingness) { | 235 | switch (interestingness) { |
| 173 | .interesting => { | 236 | .interesting => { |
| 174 | const new_tree = try parse(gpa, arena, root_source_file_path); | 237 | const new_tree = try parse(gpa, root_source_file_path); |
| 238 | gpa.free(tree.source); | ||
| 175 | tree.deinit(gpa); | 239 | tree.deinit(gpa); |
| 176 | tree = new_tree; | 240 | tree = new_tree; |
| 177 | 241 | ||
| 178 | try Walk.findTransformations(&tree, &transformations); | 242 | try Walk.findTransformations(arena, &tree, &transformations); |
| 179 | // Resetting based on the seed again means we will get the same | ||
| 180 | // results if restarting the reduction process from this new point. | ||
| 181 | rng = std.rand.DefaultPrng.init(seed); | ||
| 182 | sortTransformations(transformations.items, rng.random()); | 243 | sortTransformations(transformations.items, rng.random()); |
| 183 | 244 | ||
| 184 | continue :fresh; | 245 | continue :fresh; |
| ... | @@ -188,6 +249,11 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | ... | @@ -188,6 +249,11 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 188 | // If we tested only one transformation, move on to the next one. | 249 | // If we tested only one transformation, move on to the next one. |
| 189 | if (subset_size == 1) { | 250 | if (subset_size == 1) { |
| 190 | start_index += 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 | } | ||
| 191 | } | 257 | } |
| 192 | }, | 258 | }, |
| 193 | } | 259 | } |
| ... | @@ -241,6 +307,8 @@ fn runCheck(arena: std.mem.Allocator, argv: []const []const u8) !Interestingness | ... | @@ -241,6 +307,8 @@ fn runCheck(arena: std.mem.Allocator, argv: []const []const u8) !Interestingness |
| 241 | 307 | ||
| 242 | fn transformationsToFixups( | 308 | fn transformationsToFixups( |
| 243 | gpa: Allocator, | 309 | gpa: Allocator, |
| 310 | arena: Allocator, | ||
| 311 | root_source_file_path: []const u8, | ||
| 244 | transforms: []const Walk.Transformation, | 312 | transforms: []const Walk.Transformation, |
| 245 | fixups: *Ast.Fixups, | 313 | fixups: *Ast.Fixups, |
| 246 | ) !void { | 314 | ) !void { |
| ... | @@ -253,21 +321,77 @@ fn transformationsToFixups( | ... | @@ -253,21 +321,77 @@ fn transformationsToFixups( |
| 253 | .delete_node => |decl_node| { | 321 | .delete_node => |decl_node| { |
| 254 | try fixups.omit_nodes.put(gpa, decl_node, {}); | 322 | try fixups.omit_nodes.put(gpa, decl_node, {}); |
| 255 | }, | 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.put(gpa, ident_node, "undefined"); | ||
| 328 | } | ||
| 329 | }, | ||
| 256 | .replace_with_undef => |node| { | 330 | .replace_with_undef => |node| { |
| 257 | try fixups.replace_nodes.put(gpa, node, {}); | 331 | try fixups.replace_nodes.put(gpa, node, "undefined"); |
| 332 | }, | ||
| 333 | .inline_imported_file => |inline_imported_file| { | ||
| 334 | const full_imported_path = try std.fs.path.join(gpa, &.{ | ||
| 335 | std.fs.path.dirname(root_source_file_path) orelse ".", | ||
| 336 | inline_imported_file.imported_string, | ||
| 337 | }); | ||
| 338 | defer gpa.free(full_imported_path); | ||
| 339 | var other_file_ast = try parse(gpa, full_imported_path); | ||
| 340 | defer { | ||
| 341 | gpa.free(other_file_ast.source); | ||
| 342 | other_file_ast.deinit(gpa); | ||
| 343 | } | ||
| 344 | |||
| 345 | var inlined_fixups: Ast.Fixups = .{}; | ||
| 346 | defer inlined_fixups.deinit(gpa); | ||
| 347 | if (std.fs.path.dirname(inline_imported_file.imported_string)) |dirname| { | ||
| 348 | inlined_fixups.rebase_imported_paths = dirname; | ||
| 349 | } | ||
| 350 | for (inline_imported_file.in_scope_names.keys()) |name| { | ||
| 351 | // This name needs to be mangled in order to not cause an | ||
| 352 | // ambiguous reference error. | ||
| 353 | var i: u32 = 2; | ||
| 354 | const mangled = while (true) : (i += 1) { | ||
| 355 | const mangled = try std.fmt.allocPrint(gpa, "{s}{d}", .{ name, i }); | ||
| 356 | if (!inline_imported_file.in_scope_names.contains(mangled)) | ||
| 357 | break mangled; | ||
| 358 | gpa.free(mangled); | ||
| 359 | }; | ||
| 360 | try inlined_fixups.rename_identifiers.put(gpa, name, mangled); | ||
| 361 | } | ||
| 362 | defer { | ||
| 363 | for (inlined_fixups.rename_identifiers.values()) |v| { | ||
| 364 | gpa.free(v); | ||
| 365 | } | ||
| 366 | } | ||
| 367 | |||
| 368 | var other_source = std.ArrayList(u8).init(gpa); | ||
| 369 | defer other_source.deinit(); | ||
| 370 | try other_source.appendSlice("struct {\n"); | ||
| 371 | try other_file_ast.renderToArrayList(&other_source, inlined_fixups); | ||
| 372 | try other_source.appendSlice("}"); | ||
| 373 | |||
| 374 | try fixups.replace_nodes.put( | ||
| 375 | gpa, | ||
| 376 | inline_imported_file.builtin_call_node, | ||
| 377 | try arena.dupe(u8, other_source.items), | ||
| 378 | ); | ||
| 258 | }, | 379 | }, |
| 259 | }; | 380 | }; |
| 260 | } | 381 | } |
| 261 | 382 | ||
| 262 | fn parse(gpa: Allocator, arena: Allocator, root_source_file_path: []const u8) !Ast { | 383 | fn parse(gpa: Allocator, file_path: []const u8) !Ast { |
| 263 | const source_code = try std.fs.cwd().readFileAllocOptions( | 384 | const source_code = std.fs.cwd().readFileAllocOptions( |
| 264 | arena, | 385 | gpa, |
| 265 | root_source_file_path, | 386 | file_path, |
| 266 | std.math.maxInt(u32), | 387 | std.math.maxInt(u32), |
| 267 | null, | 388 | null, |
| 268 | 1, | 389 | 1, |
| 269 | 0, | 390 | 0, |
| 270 | ); | 391 | ) catch |err| { |
| 392 | fatal("unable to open '{s}': {s}", .{ file_path, @errorName(err) }); | ||
| 393 | }; | ||
| 394 | errdefer gpa.free(source_code); | ||
| 271 | 395 | ||
| 272 | var tree = try Ast.parse(gpa, source_code, .zig); | 396 | var tree = try Ast.parse(gpa, source_code, .zig); |
| 273 | errdefer tree.deinit(gpa); | 397 | errdefer tree.deinit(gpa); |
src/reduce/Walk.zig+162-39| ... | @@ -2,11 +2,15 @@ const std = @import("std"); | ... | @@ -2,11 +2,15 @@ const std = @import("std"); |
| 2 | const Ast = std.zig.Ast; | 2 | const Ast = std.zig.Ast; |
| 3 | const Walk = @This(); | 3 | const Walk = @This(); |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const BuiltinFn = @import("../BuiltinFn.zig"); | ||
| 5 | 6 | ||
| 6 | ast: *const Ast, | 7 | ast: *const Ast, |
| 7 | transformations: *std.ArrayList(Transformation), | 8 | transformations: *std.ArrayList(Transformation), |
| 8 | unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index), | 9 | unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index), |
| 10 | in_scope_names: std.StringArrayHashMapUnmanaged(u32), | ||
| 11 | replace_names: std.StringArrayHashMapUnmanaged(u32), | ||
| 9 | gpa: std.mem.Allocator, | 12 | gpa: std.mem.Allocator, |
| 13 | arena: std.mem.Allocator, | ||
| 10 | 14 | ||
| 11 | pub const Transformation = union(enum) { | 15 | pub const Transformation = union(enum) { |
| 12 | /// Replace the fn decl AST Node with one whose body is only `@trap()` with | 16 | /// Replace the fn decl AST Node with one whose body is only `@trap()` with |
| ... | @@ -14,23 +18,51 @@ pub const Transformation = union(enum) { | ... | @@ -14,23 +18,51 @@ pub const Transformation = union(enum) { |
| 14 | gut_function: Ast.Node.Index, | 18 | gut_function: Ast.Node.Index, |
| 15 | /// Omit a global declaration. | 19 | /// Omit a global declaration. |
| 16 | delete_node: Ast.Node.Index, | 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 | }, | ||
| 17 | /// Replace an expression with `undefined`. | 28 | /// Replace an expression with `undefined`. |
| 18 | replace_with_undef: Ast.Node.Index, | 29 | replace_with_undef: Ast.Node.Index, |
| 30 | /// Replace an `@import` with the imported file contents wrapped in a struct. | ||
| 31 | inline_imported_file: InlineImportedFile, | ||
| 32 | |||
| 33 | pub const InlineImportedFile = struct { | ||
| 34 | builtin_call_node: Ast.Node.Index, | ||
| 35 | imported_string: []const u8, | ||
| 36 | /// Identifier names that must be renamed in the inlined code or else | ||
| 37 | /// will cause ambiguous reference errors. | ||
| 38 | in_scope_names: std.StringArrayHashMapUnmanaged(void), | ||
| 39 | }; | ||
| 19 | }; | 40 | }; |
| 20 | 41 | ||
| 21 | pub const Error = error{OutOfMemory}; | 42 | pub const Error = error{OutOfMemory}; |
| 22 | 43 | ||
| 23 | /// The result will be priority shuffled. | 44 | /// The result will be priority shuffled. |
| 24 | pub fn findTransformations(ast: *const Ast, transformations: *std.ArrayList(Transformation)) !void { | 45 | pub fn findTransformations( |
| 46 | arena: std.mem.Allocator, | ||
| 47 | ast: *const Ast, | ||
| 48 | transformations: *std.ArrayList(Transformation), | ||
| 49 | ) !void { | ||
| 25 | transformations.clearRetainingCapacity(); | 50 | transformations.clearRetainingCapacity(); |
| 26 | 51 | ||
| 27 | var walk: Walk = .{ | 52 | var walk: Walk = .{ |
| 28 | .ast = ast, | 53 | .ast = ast, |
| 29 | .transformations = transformations, | 54 | .transformations = transformations, |
| 30 | .gpa = transformations.allocator, | 55 | .gpa = transformations.allocator, |
| 56 | .arena = arena, | ||
| 31 | .unreferenced_globals = .{}, | 57 | .unreferenced_globals = .{}, |
| 58 | .in_scope_names = .{}, | ||
| 59 | .replace_names = .{}, | ||
| 32 | }; | 60 | }; |
| 33 | defer walk.unreferenced_globals.deinit(walk.gpa); | 61 | defer { |
| 62 | walk.unreferenced_globals.deinit(walk.gpa); | ||
| 63 | walk.in_scope_names.deinit(walk.gpa); | ||
| 64 | walk.replace_names.deinit(walk.gpa); | ||
| 65 | } | ||
| 34 | 66 | ||
| 35 | try walkMembers(&walk, walk.ast.rootDecls()); | 67 | try walkMembers(&walk, walk.ast.rootDecls()); |
| 36 | 68 | ||
| ... | @@ -43,14 +75,18 @@ pub fn findTransformations(ast: *const Ast, transformations: *std.ArrayList(Tran | ... | @@ -43,14 +75,18 @@ pub fn findTransformations(ast: *const Ast, transformations: *std.ArrayList(Tran |
| 43 | 75 | ||
| 44 | fn walkMembers(w: *Walk, members: []const Ast.Node.Index) Error!void { | 76 | fn walkMembers(w: *Walk, members: []const Ast.Node.Index) Error!void { |
| 45 | // First we scan for globals so that we can delete them while walking. | 77 | // First we scan for globals so that we can delete them while walking. |
| 46 | try scanDecls(w, members); | 78 | try scanDecls(w, members, .add); |
| 47 | 79 | ||
| 48 | for (members) |member| { | 80 | for (members) |member| { |
| 49 | try walkMember(w, member); | 81 | try walkMember(w, member); |
| 50 | } | 82 | } |
| 83 | |||
| 84 | try scanDecls(w, members, .remove); | ||
| 51 | } | 85 | } |
| 52 | 86 | ||
| 53 | fn scanDecls(w: *Walk, members: []const Ast.Node.Index) Error!void { | 87 | const ScanDeclsAction = enum { add, remove }; |
| 88 | |||
| 89 | fn scanDecls(w: *Walk, members: []const Ast.Node.Index, action: ScanDeclsAction) Error!void { | ||
| 54 | const ast = w.ast; | 90 | const ast = w.ast; |
| 55 | const gpa = w.gpa; | 91 | const gpa = w.gpa; |
| 56 | const node_tags = ast.nodes.items(.tag); | 92 | const node_tags = ast.nodes.items(.tag); |
| ... | @@ -74,9 +110,27 @@ fn scanDecls(w: *Walk, members: []const Ast.Node.Index) Error!void { | ... | @@ -74,9 +110,27 @@ fn scanDecls(w: *Walk, members: []const Ast.Node.Index) Error!void { |
| 74 | 110 | ||
| 75 | else => continue, | 111 | else => continue, |
| 76 | }; | 112 | }; |
| 113 | |||
| 77 | assert(token_tags[name_token] == .identifier); | 114 | assert(token_tags[name_token] == .identifier); |
| 78 | const name_bytes = ast.tokenSlice(name_token); | 115 | const name_bytes = ast.tokenSlice(name_token); |
| 79 | try w.unreferenced_globals.put(gpa, name_bytes, member_node); | 116 | |
| 117 | switch (action) { | ||
| 118 | .add => { | ||
| 119 | try w.unreferenced_globals.put(gpa, name_bytes, member_node); | ||
| 120 | |||
| 121 | const gop = try w.in_scope_names.getOrPut(gpa, name_bytes); | ||
| 122 | if (!gop.found_existing) gop.value_ptr.* = 0; | ||
| 123 | gop.value_ptr.* += 1; | ||
| 124 | }, | ||
| 125 | .remove => { | ||
| 126 | const entry = w.in_scope_names.getEntry(name_bytes).?; | ||
| 127 | if (entry.value_ptr.* <= 1) { | ||
| 128 | assert(w.in_scope_names.swapRemove(name_bytes)); | ||
| 129 | } else { | ||
| 130 | entry.value_ptr.* -= 1; | ||
| 131 | } | ||
| 132 | }, | ||
| 133 | } | ||
| 80 | } | 134 | } |
| 81 | } | 135 | } |
| 82 | 136 | ||
| ... | @@ -89,9 +143,10 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void { | ... | @@ -89,9 +143,10 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void { |
| 89 | try walkExpression(w, fn_proto); | 143 | try walkExpression(w, fn_proto); |
| 90 | const body_node = datas[decl].rhs; | 144 | const body_node = datas[decl].rhs; |
| 91 | if (!isFnBodyGutted(ast, body_node)) { | 145 | if (!isFnBodyGutted(ast, body_node)) { |
| 146 | w.replace_names.clearRetainingCapacity(); | ||
| 92 | try w.transformations.append(.{ .gut_function = decl }); | 147 | try w.transformations.append(.{ .gut_function = decl }); |
| 148 | try walkExpression(w, body_node); | ||
| 93 | } | 149 | } |
| 94 | try walkExpression(w, body_node); | ||
| 95 | }, | 150 | }, |
| 96 | .fn_proto_simple, | 151 | .fn_proto_simple, |
| 97 | .fn_proto_multi, | 152 | .fn_proto_multi, |
| ... | @@ -121,7 +176,10 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void { | ... | @@ -121,7 +176,10 @@ fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void { |
| 121 | .container_field_init, | 176 | .container_field_init, |
| 122 | .container_field_align, | 177 | .container_field_align, |
| 123 | .container_field, | 178 | .container_field, |
| 124 | => try walkContainerField(w, ast.fullContainerField(decl).?), | 179 | => { |
| 180 | try w.transformations.append(.{ .delete_node = decl }); | ||
| 181 | try walkContainerField(w, ast.fullContainerField(decl).?); | ||
| 182 | }, | ||
| 125 | 183 | ||
| 126 | .@"comptime" => { | 184 | .@"comptime" => { |
| 127 | try w.transformations.append(.{ .delete_node = decl }); | 185 | try w.transformations.append(.{ .delete_node = decl }); |
| ... | @@ -140,7 +198,15 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { | ... | @@ -140,7 +198,15 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { |
| 140 | const node_tags = ast.nodes.items(.tag); | 198 | const node_tags = ast.nodes.items(.tag); |
| 141 | const datas = ast.nodes.items(.data); | 199 | const datas = ast.nodes.items(.data); |
| 142 | switch (node_tags[node]) { | 200 | switch (node_tags[node]) { |
| 143 | .identifier => try walkIdentifier(w, main_tokens[node]), | 201 | .identifier => { |
| 202 | const name_ident = main_tokens[node]; | ||
| 203 | assert(token_tags[name_ident] == .identifier); | ||
| 204 | const name_bytes = ast.tokenSlice(name_ident); | ||
| 205 | _ = w.unreferenced_globals.swapRemove(name_bytes); | ||
| 206 | if (w.replace_names.get(name_bytes)) |index| { | ||
| 207 | try w.transformations.items[index].delete_var_decl.references.append(w.arena, node); | ||
| 208 | } | ||
| 209 | }, | ||
| 144 | 210 | ||
| 145 | .number_literal, | 211 | .number_literal, |
| 146 | .char_literal, | 212 | .char_literal, |
| ... | @@ -437,16 +503,16 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { | ... | @@ -437,16 +503,16 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { |
| 437 | 503 | ||
| 438 | .builtin_call_two, .builtin_call_two_comma => { | 504 | .builtin_call_two, .builtin_call_two_comma => { |
| 439 | if (datas[node].lhs == 0) { | 505 | if (datas[node].lhs == 0) { |
| 440 | return walkBuiltinCall(w, main_tokens[node], &.{}); | 506 | return walkBuiltinCall(w, node, &.{}); |
| 441 | } else if (datas[node].rhs == 0) { | 507 | } else if (datas[node].rhs == 0) { |
| 442 | return walkBuiltinCall(w, main_tokens[node], &.{datas[node].lhs}); | 508 | return walkBuiltinCall(w, node, &.{datas[node].lhs}); |
| 443 | } else { | 509 | } else { |
| 444 | return walkBuiltinCall(w, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs }); | 510 | return walkBuiltinCall(w, node, &.{ datas[node].lhs, datas[node].rhs }); |
| 445 | } | 511 | } |
| 446 | }, | 512 | }, |
| 447 | .builtin_call, .builtin_call_comma => { | 513 | .builtin_call, .builtin_call_comma => { |
| 448 | const params = ast.extra_data[datas[node].lhs..datas[node].rhs]; | 514 | const params = ast.extra_data[datas[node].lhs..datas[node].rhs]; |
| 449 | return walkBuiltinCall(w, main_tokens[node], params); | 515 | return walkBuiltinCall(w, node, params); |
| 450 | }, | 516 | }, |
| 451 | 517 | ||
| 452 | .fn_proto_simple, | 518 | .fn_proto_simple, |
| ... | @@ -537,9 +603,12 @@ fn walkGlobalVarDecl(w: *Walk, decl_node: Ast.Node.Index, var_decl: Ast.full.Var | ... | @@ -537,9 +603,12 @@ fn walkGlobalVarDecl(w: *Walk, decl_node: Ast.Node.Index, var_decl: Ast.full.Var |
| 537 | try walkExpression(w, var_decl.ast.section_node); | 603 | try walkExpression(w, var_decl.ast.section_node); |
| 538 | } | 604 | } |
| 539 | 605 | ||
| 540 | assert(var_decl.ast.init_node != 0); | 606 | if (var_decl.ast.init_node != 0) { |
| 541 | 607 | if (!isUndefinedIdent(w.ast, var_decl.ast.init_node)) { | |
| 542 | return walkExpression(w, var_decl.ast.init_node); | 608 | try w.transformations.append(.{ .replace_with_undef = var_decl.ast.init_node }); |
| 609 | } | ||
| 610 | try walkExpression(w, var_decl.ast.init_node); | ||
| 611 | } | ||
| 543 | } | 612 | } |
| 544 | 613 | ||
| 545 | fn walkLocalVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void { | 614 | fn walkLocalVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void { |
| ... | @@ -561,12 +630,12 @@ fn walkLocalVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void { | ... | @@ -561,12 +630,12 @@ fn walkLocalVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void { |
| 561 | try walkExpression(w, var_decl.ast.section_node); | 630 | try walkExpression(w, var_decl.ast.section_node); |
| 562 | } | 631 | } |
| 563 | 632 | ||
| 564 | assert(var_decl.ast.init_node != 0); | 633 | if (var_decl.ast.init_node != 0) { |
| 565 | if (!isUndefinedIdent(w.ast, var_decl.ast.init_node)) { | 634 | if (!isUndefinedIdent(w.ast, var_decl.ast.init_node)) { |
| 566 | try w.transformations.append(.{ .replace_with_undef = var_decl.ast.init_node }); | 635 | try w.transformations.append(.{ .replace_with_undef = var_decl.ast.init_node }); |
| 636 | } | ||
| 637 | try walkExpression(w, var_decl.ast.init_node); | ||
| 567 | } | 638 | } |
| 568 | |||
| 569 | return walkExpression(w, var_decl.ast.init_node); | ||
| 570 | } | 639 | } |
| 571 | 640 | ||
| 572 | fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void { | 641 | fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void { |
| ... | @@ -576,7 +645,9 @@ fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void { | ... | @@ -576,7 +645,9 @@ fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void { |
| 576 | if (field.ast.align_expr != 0) { | 645 | if (field.ast.align_expr != 0) { |
| 577 | try walkExpression(w, field.ast.align_expr); // alignment | 646 | try walkExpression(w, field.ast.align_expr); // alignment |
| 578 | } | 647 | } |
| 579 | try walkExpression(w, field.ast.value_expr); // value | 648 | if (field.ast.value_expr != 0) { |
| 649 | try walkExpression(w, field.ast.value_expr); // value | ||
| 650 | } | ||
| 580 | } | 651 | } |
| 581 | 652 | ||
| 582 | fn walkBlock( | 653 | fn walkBlock( |
| ... | @@ -594,9 +665,34 @@ fn walkBlock( | ... | @@ -594,9 +665,34 @@ fn walkBlock( |
| 594 | .local_var_decl, | 665 | .local_var_decl, |
| 595 | .simple_var_decl, | 666 | .simple_var_decl, |
| 596 | .aligned_var_decl, | 667 | .aligned_var_decl, |
| 597 | => try walkLocalVarDecl(w, ast.fullVarDecl(stmt).?), | 668 | => { |
| 598 | 669 | const var_decl = ast.fullVarDecl(stmt).?; | |
| 599 | else => try walkExpression(w, stmt), | 670 | if (var_decl.ast.init_node != 0 and |
| 671 | isUndefinedIdent(w.ast, var_decl.ast.init_node)) | ||
| 672 | { | ||
| 673 | try w.transformations.append(.{ .delete_var_decl = .{ | ||
| 674 | .var_decl_node = stmt, | ||
| 675 | .references = .{}, | ||
| 676 | } }); | ||
| 677 | const name_tok = var_decl.ast.mut_token + 1; | ||
| 678 | const name_bytes = ast.tokenSlice(name_tok); | ||
| 679 | try w.replace_names.put(w.gpa, name_bytes, @intCast(w.transformations.items.len - 1)); | ||
| 680 | } else { | ||
| 681 | try walkLocalVarDecl(w, var_decl); | ||
| 682 | } | ||
| 683 | }, | ||
| 684 | |||
| 685 | else => { | ||
| 686 | switch (categorizeStmt(ast, stmt)) { | ||
| 687 | // Don't try to remove `_ = foo;` discards; those are handled separately. | ||
| 688 | .discard_identifier => {}, | ||
| 689 | // definitely try to remove `_ = undefined;` though. | ||
| 690 | .discard_undefined, .trap_call, .other => { | ||
| 691 | try w.transformations.append(.{ .delete_node = stmt }); | ||
| 692 | }, | ||
| 693 | } | ||
| 694 | try walkExpression(w, stmt); | ||
| 695 | }, | ||
| 600 | } | 696 | } |
| 601 | } | 697 | } |
| 602 | } | 698 | } |
| ... | @@ -680,10 +776,35 @@ fn walkContainerDecl( | ... | @@ -680,10 +776,35 @@ fn walkContainerDecl( |
| 680 | 776 | ||
| 681 | fn walkBuiltinCall( | 777 | fn walkBuiltinCall( |
| 682 | w: *Walk, | 778 | w: *Walk, |
| 683 | builtin_token: Ast.TokenIndex, | 779 | call_node: Ast.Node.Index, |
| 684 | params: []const Ast.Node.Index, | 780 | params: []const Ast.Node.Index, |
| 685 | ) Error!void { | 781 | ) Error!void { |
| 686 | _ = builtin_token; | 782 | const ast = w.ast; |
| 783 | const main_tokens = ast.nodes.items(.main_token); | ||
| 784 | const builtin_token = main_tokens[call_node]; | ||
| 785 | const builtin_name = ast.tokenSlice(builtin_token); | ||
| 786 | const info = BuiltinFn.list.get(builtin_name).?; | ||
| 787 | switch (info.tag) { | ||
| 788 | .import => { | ||
| 789 | const operand_node = params[0]; | ||
| 790 | const str_lit_token = main_tokens[operand_node]; | ||
| 791 | const token_bytes = ast.tokenSlice(str_lit_token); | ||
| 792 | if (std.mem.endsWith(u8, token_bytes, ".zig\"")) { | ||
| 793 | const imported_string = std.zig.string_literal.parseAlloc(w.arena, token_bytes) catch | ||
| 794 | unreachable; | ||
| 795 | try w.transformations.append(.{ .inline_imported_file = .{ | ||
| 796 | .builtin_call_node = call_node, | ||
| 797 | .imported_string = imported_string, | ||
| 798 | .in_scope_names = try std.StringArrayHashMapUnmanaged(void).init( | ||
| 799 | w.arena, | ||
| 800 | w.in_scope_names.keys(), | ||
| 801 | &.{}, | ||
| 802 | ), | ||
| 803 | } }); | ||
| 804 | } | ||
| 805 | }, | ||
| 806 | else => {}, | ||
| 807 | } | ||
| 687 | for (params) |param_node| { | 808 | for (params) |param_node| { |
| 688 | try walkExpression(w, param_node); | 809 | try walkExpression(w, param_node); |
| 689 | } | 810 | } |
| ... | @@ -821,6 +942,7 @@ fn isFnBodyGutted(ast: *const Ast, body_node: Ast.Node.Index) bool { | ... | @@ -821,6 +942,7 @@ fn isFnBodyGutted(ast: *const Ast, body_node: Ast.Node.Index) bool { |
| 821 | } | 942 | } |
| 822 | 943 | ||
| 823 | const StmtCategory = enum { | 944 | const StmtCategory = enum { |
| 945 | discard_undefined, | ||
| 824 | discard_identifier, | 946 | discard_identifier, |
| 825 | trap_call, | 947 | trap_call, |
| 826 | other, | 948 | other, |
| ... | @@ -846,8 +968,14 @@ fn categorizeStmt(ast: *const Ast, stmt: Ast.Node.Index) StmtCategory { | ... | @@ -846,8 +968,14 @@ fn categorizeStmt(ast: *const Ast, stmt: Ast.Node.Index) StmtCategory { |
| 846 | }, | 968 | }, |
| 847 | .assign => { | 969 | .assign => { |
| 848 | const infix = datas[stmt]; | 970 | const infix = datas[stmt]; |
| 849 | if (isDiscardIdent(ast, infix.lhs) and node_tags[infix.rhs] == .identifier) | 971 | if (isDiscardIdent(ast, infix.lhs) and node_tags[infix.rhs] == .identifier) { |
| 850 | return .discard_identifier; | 972 | const name_bytes = ast.tokenSlice(main_tokens[infix.rhs]); |
| 973 | if (std.mem.eql(u8, name_bytes, "undefined")) { | ||
| 974 | return .discard_undefined; | ||
| 975 | } else { | ||
| 976 | return .discard_identifier; | ||
| 977 | } | ||
| 978 | } | ||
| 851 | return .other; | 979 | return .other; |
| 852 | }, | 980 | }, |
| 853 | else => return .other, | 981 | else => return .other, |
| ... | @@ -867,26 +995,21 @@ fn categorizeBuiltinCall( | ... | @@ -867,26 +995,21 @@ fn categorizeBuiltinCall( |
| 867 | } | 995 | } |
| 868 | 996 | ||
| 869 | fn isDiscardIdent(ast: *const Ast, node: Ast.Node.Index) bool { | 997 | fn isDiscardIdent(ast: *const Ast, node: Ast.Node.Index) bool { |
| 870 | const node_tags = ast.nodes.items(.tag); | 998 | return isMatchingIdent(ast, node, "_"); |
| 871 | const main_tokens = ast.nodes.items(.main_token); | ||
| 872 | switch (node_tags[node]) { | ||
| 873 | .identifier => { | ||
| 874 | const token_index = main_tokens[node]; | ||
| 875 | const name_bytes = ast.tokenSlice(token_index); | ||
| 876 | return std.mem.eql(u8, name_bytes, "_"); | ||
| 877 | }, | ||
| 878 | else => return false, | ||
| 879 | } | ||
| 880 | } | 999 | } |
| 881 | 1000 | ||
| 882 | fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool { | 1001 | fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool { |
| 1002 | return isMatchingIdent(ast, node, "undefined"); | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bool { | ||
| 883 | const node_tags = ast.nodes.items(.tag); | 1006 | const node_tags = ast.nodes.items(.tag); |
| 884 | const main_tokens = ast.nodes.items(.main_token); | 1007 | const main_tokens = ast.nodes.items(.main_token); |
| 885 | switch (node_tags[node]) { | 1008 | switch (node_tags[node]) { |
| 886 | .identifier => { | 1009 | .identifier => { |
| 887 | const token_index = main_tokens[node]; | 1010 | const token_index = main_tokens[node]; |
| 888 | const name_bytes = ast.tokenSlice(token_index); | 1011 | const name_bytes = ast.tokenSlice(token_index); |
| 889 | return std.mem.eql(u8, name_bytes, "undefined"); | 1012 | return std.mem.eql(u8, name_bytes, string); |
| 890 | }, | 1013 | }, |
| 891 | else => return false, | 1014 | else => return false, |
| 892 | } | 1015 | } |