authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-05 03:39:01-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-11-05 03:39:01-05:00
logdc63426b1eaef9c65152c8e052e5552e593e9382
tree4baa141bdd1da68a3f15164988317350f95b76c7
parentf24ceec35a6fd1e5e6a671461b78919b5f588a32
parenta9002156a09130038abcf418609fea725ce71bc2
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17866 from ziglang/reduce-inline-import

zig reduce: support inlining `@import` and more

4 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 }
576576
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 that591 /// 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,
2935
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 }
3644
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 }
4354
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); // rbrace197 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);
15181571
1519 // TODO remove before release of 0.12.01572 // 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 }
16111664
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;
16141687
...@@ -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];
19392011
1940 if (token_tags[lbrace - 1] == .colon and2012 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); // identifier2015 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,2029fn 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;
5const fatal = @import("./main.zig").fatal;5const fatal = @import("./main.zig").fatal;
6const Ast = std.zig.Ast;6const Ast = std.zig.Ast;
7const Walk = @import("reduce/Walk.zig");7const Walk = @import("reduce/Walk.zig");
8const AstGen = @import("AstGen.zig");
9const Zir = @import("Zir.zig");
810
9const usage =11const 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 flags41// - add support for parsing the module flags
40// - more fancy transformations42// - more fancy transformations
41// - @import inlining of modules43// - @import inlining of modules
42// - @import inlining of files
43// - deleting unused functions and other globals
44// - removing statements or blocks of code44// - 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();
111111
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 }
114120
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 {
126132
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);
130140
131 // 1. Walk the AST of the source file looking for independent141 // 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 {
145155
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());
150160
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;
157167
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;
160173
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);
163181
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});
167230
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;
177241
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());
183244
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
241307
242fn transformationsToFixups(308fn 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}
261382
262fn parse(gpa: Allocator, arena: Allocator, root_source_file_path: []const u8) !Ast {383fn 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);
271395
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");
2const Ast = std.zig.Ast;2const Ast = std.zig.Ast;
3const Walk = @This();3const Walk = @This();
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const BuiltinFn = @import("../BuiltinFn.zig");
56
6ast: *const Ast,7ast: *const Ast,
7transformations: *std.ArrayList(Transformation),8transformations: *std.ArrayList(Transformation),
8unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index),9unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index),
10in_scope_names: std.StringArrayHashMapUnmanaged(u32),
11replace_names: std.StringArrayHashMapUnmanaged(u32),
9gpa: std.mem.Allocator,12gpa: std.mem.Allocator,
13arena: std.mem.Allocator,
1014
11pub const Transformation = union(enum) {15pub const Transformation = union(enum) {
12 /// Replace the fn decl AST Node with one whose body is only `@trap()` with16 /// 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};
2041
21pub const Error = error{OutOfMemory};42pub const Error = error{OutOfMemory};
2243
23/// The result will be priority shuffled.44/// The result will be priority shuffled.
24pub fn findTransformations(ast: *const Ast, transformations: *std.ArrayList(Transformation)) !void {45pub fn findTransformations(
46 arena: std.mem.Allocator,
47 ast: *const Ast,
48 transformations: *std.ArrayList(Transformation),
49) !void {
25 transformations.clearRetainingCapacity();50 transformations.clearRetainingCapacity();
2651
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 }
3466
35 try walkMembers(&walk, walk.ast.rootDecls());67 try walkMembers(&walk, walk.ast.rootDecls());
3668
...@@ -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
4375
44fn walkMembers(w: *Walk, members: []const Ast.Node.Index) Error!void {76fn 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);
4779
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}
5286
53fn scanDecls(w: *Walk, members: []const Ast.Node.Index) Error!void {87const ScanDeclsAction = enum { add, remove };
88
89fn 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 {
74110
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}
82136
...@@ -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 },
125183
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 },
144210
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 {
437503
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 },
451517
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 }
539605
540 assert(var_decl.ast.init_node != 0);606 if (var_decl.ast.init_node != 0) {
541607 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}
544613
545fn walkLocalVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void {614fn 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 }
563632
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}
571640
572fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void {641fn 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); // alignment646 try walkExpression(w, field.ast.align_expr); // alignment
578 }647 }
579 try walkExpression(w, field.ast.value_expr); // value648 if (field.ast.value_expr != 0) {
649 try walkExpression(w, field.ast.value_expr); // value
650 }
580}651}
581652
582fn walkBlock(653fn 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 => {
598669 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(
680776
681fn walkBuiltinCall(777fn 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}
822943
823const StmtCategory = enum {944const 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}
868996
869fn isDiscardIdent(ast: *const Ast, node: Ast.Node.Index) bool {997fn 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}
8811000
882fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool {1001fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool {
1002 return isMatchingIdent(ast, node, "undefined");
1003}
1004
1005fn 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 }