authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-04 13:57:29-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-04 13:57:29-07:00
log8bd01d2d9bbda1a2e044223db4b2272e7c5020a0
tree9f7a7726478b6a1fc0a9b26385a08cf95b5d6e4c
parent98dc28bbe223cb7183aabe7ed7a847c67c1a4df9

zig reduce: add transformation for inlining file-based `@import`

One thing is missing for it to be useful, however, which is dealing with ambiguous reference errors introduced by the inlining process.

3 files changed, 108 insertions(+), 20 deletions(-)

lib/std/zig/render.zig+28-4
...@@ -24,8 +24,11 @@ pub const Fixups = struct {...@@ -24,8 +24,11 @@ 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 /// All `@import` builtin calls which refer to a file path will be prefixed
30 /// with this path.
31 rebase_imported_paths: ?[]const u8 = null,
2932
30 pub fn count(f: Fixups) usize {33 pub fn count(f: Fixups) usize {
31 return f.unused_var_decls.count() +34 return f.unused_var_decls.count() +
...@@ -277,8 +280,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -277,8 +280,8 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
277 const main_tokens = tree.nodes.items(.main_token);280 const main_tokens = tree.nodes.items(.main_token);
278 const node_tags = tree.nodes.items(.tag);281 const node_tags = tree.nodes.items(.tag);
279 const datas = tree.nodes.items(.data);282 const datas = tree.nodes.items(.data);
280 if (r.fixups.replace_nodes.contains(node)) {283 if (r.fixups.replace_nodes.get(node)) |replacement| {
281 try ais.writer().writeAll("undefined");284 try ais.writer().writeAll(replacement);
282 try renderOnlySpace(r, space);285 try renderOnlySpace(r, space);
283 return;286 return;
284 }287 }
...@@ -1515,6 +1518,7 @@ fn renderBuiltinCall(...@@ -1515,6 +1518,7 @@ fn renderBuiltinCall(
1515 const tree = r.tree;1518 const tree = r.tree;
1516 const ais = r.ais;1519 const ais = r.ais;
1517 const token_tags = tree.tokens.items(.tag);1520 const token_tags = tree.tokens.items(.tag);
1521 const main_tokens = tree.nodes.items(.main_token);
15181522
1519 // TODO remove before release of 0.12.01523 // TODO remove before release of 0.12.0
1520 const slice = tree.tokenSlice(builtin_token);1524 const slice = tree.tokenSlice(builtin_token);
...@@ -1609,6 +1613,26 @@ fn renderBuiltinCall(...@@ -1609,6 +1613,26 @@ fn renderBuiltinCall(
1609 return renderToken(r, builtin_token + 2, space); // )1613 return renderToken(r, builtin_token + 2, space); // )
1610 }1614 }
16111615
1616 if (r.fixups.rebase_imported_paths) |prefix| {
1617 if (mem.eql(u8, slice, "@import")) f: {
1618 const param = params[0];
1619 const str_lit_token = main_tokens[param];
1620 assert(token_tags[str_lit_token] == .string_literal);
1621 const token_bytes = tree.tokenSlice(str_lit_token);
1622 const imported_string = std.zig.string_literal.parseAlloc(r.gpa, token_bytes) catch |err| switch (err) {
1623 error.OutOfMemory => return error.OutOfMemory,
1624 error.InvalidLiteral => break :f,
1625 };
1626 defer r.gpa.free(imported_string);
1627 const new_string = try std.fs.path.resolvePosix(r.gpa, &.{ prefix, imported_string });
1628 defer r.gpa.free(new_string);
1629
1630 try renderToken(r, builtin_token + 1, .none); // (
1631 try ais.writer().print("\"{}\"", .{std.zig.fmtEscapes(new_string)});
1632 return renderToken(r, str_lit_token + 1, space); // )
1633 }
1634 }
1635
1612 const last_param = params[params.len - 1];1636 const last_param = params[params.len - 1];
1613 const after_last_param_token = tree.lastToken(last_param) + 1;1637 const after_last_param_token = tree.lastToken(last_param) + 1;
16141638
src/reduce.zig+47-10
...@@ -109,8 +109,11 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -109,8 +109,11 @@ 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 tree = try parse(gpa, root_source_file_path);
113 defer tree.deinit(gpa);113 defer {
114 gpa.free(tree.source);
115 tree.deinit(gpa);
116 }
114117
115 if (!skip_smoke_test) {118 if (!skip_smoke_test) {
116 std.debug.print("smoke testing the interestingness check...\n", .{});119 std.debug.print("smoke testing the interestingness check...\n", .{});
...@@ -159,7 +162,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -159,7 +162,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
159 subset_size = @max(1, subset_size / 2);162 subset_size = @max(1, subset_size / 2);
160163
161 const this_set = transformations.items[start_index..][0..subset_size];164 const this_set = transformations.items[start_index..][0..subset_size];
162 try transformationsToFixups(gpa, this_set, &fixups);165 try transformationsToFixups(gpa, arena, root_source_file_path, this_set, &fixups);
163166
164 rendered.clearRetainingCapacity();167 rendered.clearRetainingCapacity();
165 try tree.renderToArrayList(&rendered, fixups);168 try tree.renderToArrayList(&rendered, fixups);
...@@ -171,7 +174,8 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {...@@ -171,7 +174,8 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
171 });174 });
172 switch (interestingness) {175 switch (interestingness) {
173 .interesting => {176 .interesting => {
174 const new_tree = try parse(gpa, arena, root_source_file_path);177 const new_tree = try parse(gpa, root_source_file_path);
178 gpa.free(tree.source);
175 tree.deinit(gpa);179 tree.deinit(gpa);
176 tree = new_tree;180 tree = new_tree;
177181
...@@ -241,6 +245,8 @@ fn runCheck(arena: std.mem.Allocator, argv: []const []const u8) !Interestingness...@@ -241,6 +245,8 @@ fn runCheck(arena: std.mem.Allocator, argv: []const []const u8) !Interestingness
241245
242fn transformationsToFixups(246fn transformationsToFixups(
243 gpa: Allocator,247 gpa: Allocator,
248 arena: Allocator,
249 root_source_file_path: []const u8,
244 transforms: []const Walk.Transformation,250 transforms: []const Walk.Transformation,
245 fixups: *Ast.Fixups,251 fixups: *Ast.Fixups,
246) !void {252) !void {
...@@ -254,20 +260,51 @@ fn transformationsToFixups(...@@ -254,20 +260,51 @@ fn transformationsToFixups(
254 try fixups.omit_nodes.put(gpa, decl_node, {});260 try fixups.omit_nodes.put(gpa, decl_node, {});
255 },261 },
256 .replace_with_undef => |node| {262 .replace_with_undef => |node| {
257 try fixups.replace_nodes.put(gpa, node, {});263 try fixups.replace_nodes.put(gpa, node, "undefined");
264 },
265 .inline_imported_file => |inline_imported_file| {
266 defer gpa.free(inline_imported_file.imported_string);
267 const full_imported_path = try std.fs.path.join(gpa, &.{
268 std.fs.path.dirname(root_source_file_path) orelse ".",
269 inline_imported_file.imported_string,
270 });
271 defer gpa.free(full_imported_path);
272 var other_file_ast = try parse(gpa, full_imported_path);
273 defer {
274 gpa.free(other_file_ast.source);
275 other_file_ast.deinit(gpa);
276 }
277 var other_source = std.ArrayList(u8).init(gpa);
278 defer other_source.deinit();
279 var inlined_fixups: Ast.Fixups = .{};
280 defer inlined_fixups.deinit(gpa);
281 try other_source.appendSlice("struct {\n");
282 try other_file_ast.renderToArrayList(&other_source, .{
283 .rebase_imported_paths = std.fs.path.dirname(inline_imported_file.imported_string),
284 });
285 try other_source.appendSlice("}");
286
287 try fixups.replace_nodes.put(
288 gpa,
289 inline_imported_file.builtin_call_node,
290 try arena.dupe(u8, other_source.items),
291 );
258 },292 },
259 };293 };
260}294}
261295
262fn parse(gpa: Allocator, arena: Allocator, root_source_file_path: []const u8) !Ast {296fn parse(gpa: Allocator, file_path: []const u8) !Ast {
263 const source_code = try std.fs.cwd().readFileAllocOptions(297 const source_code = std.fs.cwd().readFileAllocOptions(
264 arena,298 gpa,
265 root_source_file_path,299 file_path,
266 std.math.maxInt(u32),300 std.math.maxInt(u32),
267 null,301 null,
268 1,302 1,
269 0,303 0,
270 );304 ) catch |err| {
305 fatal("unable to open '{s}': {s}", .{ file_path, @errorName(err) });
306 };
307 errdefer gpa.free(source_code);
271308
272 var tree = try Ast.parse(gpa, source_code, .zig);309 var tree = try Ast.parse(gpa, source_code, .zig);
273 errdefer tree.deinit(gpa);310 errdefer tree.deinit(gpa);
src/reduce/Walk.zig+33-6
...@@ -2,6 +2,7 @@ const std = @import("std");...@@ -2,6 +2,7 @@ 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),
...@@ -16,6 +17,11 @@ pub const Transformation = union(enum) {...@@ -16,6 +17,11 @@ pub const Transformation = union(enum) {
16 delete_node: Ast.Node.Index,17 delete_node: Ast.Node.Index,
17 /// Replace an expression with `undefined`.18 /// Replace an expression with `undefined`.
18 replace_with_undef: Ast.Node.Index,19 replace_with_undef: Ast.Node.Index,
20 /// Replace an `@import` with the imported file contents wrapped in a struct.
21 inline_imported_file: struct {
22 builtin_call_node: Ast.Node.Index,
23 imported_string: []const u8,
24 },
19};25};
2026
21pub const Error = error{OutOfMemory};27pub const Error = error{OutOfMemory};
...@@ -437,16 +443,16 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {...@@ -437,16 +443,16 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
437443
438 .builtin_call_two, .builtin_call_two_comma => {444 .builtin_call_two, .builtin_call_two_comma => {
439 if (datas[node].lhs == 0) {445 if (datas[node].lhs == 0) {
440 return walkBuiltinCall(w, main_tokens[node], &.{});446 return walkBuiltinCall(w, node, &.{});
441 } else if (datas[node].rhs == 0) {447 } else if (datas[node].rhs == 0) {
442 return walkBuiltinCall(w, main_tokens[node], &.{datas[node].lhs});448 return walkBuiltinCall(w, node, &.{datas[node].lhs});
443 } else {449 } else {
444 return walkBuiltinCall(w, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs });450 return walkBuiltinCall(w, node, &.{ datas[node].lhs, datas[node].rhs });
445 }451 }
446 },452 },
447 .builtin_call, .builtin_call_comma => {453 .builtin_call, .builtin_call_comma => {
448 const params = ast.extra_data[datas[node].lhs..datas[node].rhs];454 const params = ast.extra_data[datas[node].lhs..datas[node].rhs];
449 return walkBuiltinCall(w, main_tokens[node], params);455 return walkBuiltinCall(w, node, params);
450 },456 },
451457
452 .fn_proto_simple,458 .fn_proto_simple,
...@@ -680,10 +686,31 @@ fn walkContainerDecl(...@@ -680,10 +686,31 @@ fn walkContainerDecl(
680686
681fn walkBuiltinCall(687fn walkBuiltinCall(
682 w: *Walk,688 w: *Walk,
683 builtin_token: Ast.TokenIndex,689 call_node: Ast.Node.Index,
684 params: []const Ast.Node.Index,690 params: []const Ast.Node.Index,
685) Error!void {691) Error!void {
686 _ = builtin_token;692 const ast = w.ast;
693 const gpa = w.gpa;
694 const main_tokens = ast.nodes.items(.main_token);
695 const builtin_token = main_tokens[call_node];
696 const builtin_name = ast.tokenSlice(builtin_token);
697 const info = BuiltinFn.list.get(builtin_name).?;
698 switch (info.tag) {
699 .import => {
700 const operand_node = params[0];
701 const str_lit_token = main_tokens[operand_node];
702 const token_bytes = ast.tokenSlice(str_lit_token);
703 const imported_string = std.zig.string_literal.parseAlloc(gpa, token_bytes) catch
704 unreachable;
705 if (std.mem.endsWith(u8, imported_string, ".zig")) {
706 try w.transformations.append(.{ .inline_imported_file = .{
707 .builtin_call_node = call_node,
708 .imported_string = imported_string,
709 } });
710 }
711 },
712 else => {},
713 }
687 for (params) |param_node| {714 for (params) |param_node| {
688 try walkExpression(w, param_node);715 try walkExpression(w, param_node);
689 }716 }