authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-27 18:26:59-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-01 06:47:20-04:00
log2bae94280058f23ba44dc3857e2b551f5894e1cb
tree39483fa7a609f36b58dc19524e0b4467ab473974
parentb23a87953a7a4030af3d9acf8deacb85162dd275

add ZIR compare output test case to test suite


15 files changed, 407 insertions(+), 377 deletions(-)

build.zig+1-2
...@@ -44,7 +44,7 @@ pub fn build(b: *Builder) !void {...@@ -44,7 +44,7 @@ pub fn build(b: *Builder) !void {
44 try findAndReadConfigH(b);44 try findAndReadConfigH(b);
4545
46 var test_stage2 = b.addTest("src-self-hosted/test.zig");46 var test_stage2 = b.addTest("src-self-hosted/test.zig");
47 test_stage2.setBuildMode(builtin.Mode.Debug);47 test_stage2.setBuildMode(.Debug); // note this is only the mode of the test harness
48 test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig");48 test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig");
4949
50 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});50 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
...@@ -68,7 +68,6 @@ pub fn build(b: *Builder) !void {...@@ -68,7 +68,6 @@ pub fn build(b: *Builder) !void {
68 var ctx = parseConfigH(b, config_h_text);68 var ctx = parseConfigH(b, config_h_text);
69 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);69 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
7070
71 try configureStage2(b, test_stage2, ctx);
72 try configureStage2(b, exe, ctx);71 try configureStage2(b, exe, ctx);
7372
74 b.default_step.dependOn(&exe.step);73 b.default_step.dependOn(&exe.step);
lib/std/child_process.zig+11-1
...@@ -46,6 +46,12 @@ pub const ChildProcess = struct {...@@ -46,6 +46,12 @@ pub const ChildProcess = struct {
4646
47 /// Set to change the current working directory when spawning the child process.47 /// Set to change the current working directory when spawning the child process.
48 cwd: ?[]const u8,48 cwd: ?[]const u8,
49 /// Set to change the current working directory when spawning the child process.
50 /// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190
51 /// Once that is done, `cwd` will be deprecated in favor of this field.
52 /// The directory handle must be opened with the ability to be passed
53 /// to a child process (no `O_CLOEXEC` flag on POSIX).
54 cwd_dir: ?fs.Dir = null,
4955
50 err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t,56 err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t,
5157
...@@ -183,6 +189,7 @@ pub const ChildProcess = struct {...@@ -183,6 +189,7 @@ pub const ChildProcess = struct {
183 allocator: *mem.Allocator,189 allocator: *mem.Allocator,
184 argv: []const []const u8,190 argv: []const []const u8,
185 cwd: ?[]const u8 = null,191 cwd: ?[]const u8 = null,
192 cwd_dir: ?fs.Dir = null,
186 env_map: ?*const BufMap = null,193 env_map: ?*const BufMap = null,
187 max_output_bytes: usize = 50 * 1024,194 max_output_bytes: usize = 50 * 1024,
188 expand_arg0: Arg0Expand = .no_expand,195 expand_arg0: Arg0Expand = .no_expand,
...@@ -194,6 +201,7 @@ pub const ChildProcess = struct {...@@ -194,6 +201,7 @@ pub const ChildProcess = struct {
194 child.stdout_behavior = .Pipe;201 child.stdout_behavior = .Pipe;
195 child.stderr_behavior = .Pipe;202 child.stderr_behavior = .Pipe;
196 child.cwd = args.cwd;203 child.cwd = args.cwd;
204 child.cwd_dir = args.cwd_dir;
197 child.env_map = args.env_map;205 child.env_map = args.env_map;
198 child.expand_arg0 = args.expand_arg0;206 child.expand_arg0 = args.expand_arg0;
199207
...@@ -414,7 +422,9 @@ pub const ChildProcess = struct {...@@ -414,7 +422,9 @@ pub const ChildProcess = struct {
414 os.close(stderr_pipe[1]);422 os.close(stderr_pipe[1]);
415 }423 }
416424
417 if (self.cwd) |cwd| {425 if (self.cwd_dir) |cwd| {
426 os.fchdir(cwd.fd) catch |err| forkChildErrReport(err_pipe[1], err);
427 } else if (self.cwd) |cwd| {
418 os.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);428 os.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);
419 }429 }
420430
lib/std/fs.zig+22-4
...@@ -606,7 +606,8 @@ pub const Dir = struct {...@@ -606,7 +606,8 @@ pub const Dir = struct {
606 } else 0;606 } else 0;
607607
608 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;608 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
609 const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read)609 const O_CLOEXEC: u32 = if (flags.share_with_child_process) 0 else os.O_CLOEXEC;
610 const os_flags = lock_flag | O_LARGEFILE | O_CLOEXEC | if (flags.write and flags.read)
610 @as(u32, os.O_RDWR)611 @as(u32, os.O_RDWR)
611 else if (flags.write)612 else if (flags.write)
612 @as(u32, os.O_WRONLY)613 @as(u32, os.O_WRONLY)
...@@ -689,7 +690,8 @@ pub const Dir = struct {...@@ -689,7 +690,8 @@ pub const Dir = struct {
689 } else 0;690 } else 0;
690691
691 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;692 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
692 const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC |693 const O_CLOEXEC: u32 = if (flags.share_with_child_process) 0 else os.O_CLOEXEC;
694 const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | O_CLOEXEC |
693 (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |695 (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |
694 (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |696 (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |
695 (if (flags.exclusive) @as(u32, os.O_EXCL) else 0);697 (if (flags.exclusive) @as(u32, os.O_EXCL) else 0);
...@@ -787,6 +789,15 @@ pub const Dir = struct {...@@ -787,6 +789,15 @@ pub const Dir = struct {
787 }789 }
788 }790 }
789791
792 /// This function performs `makePath`, followed by `openDir`.
793 /// If supported by the OS, this operation is atomic. It is not atomic on
794 /// all operating systems.
795 pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !Dir {
796 // TODO improve this implementation on Windows; we can avoid 1 call to NtClose
797 try self.makePath(sub_path);
798 return self.openDir(sub_path, open_dir_options);
799 }
800
790 /// Changes the current working directory to the open directory handle.801 /// Changes the current working directory to the open directory handle.
791 /// This modifies global state and can have surprising effects in multi-802 /// This modifies global state and can have surprising effects in multi-
792 /// threaded applications. Most applications and especially libraries should803 /// threaded applications. Most applications and especially libraries should
...@@ -807,6 +818,11 @@ pub const Dir = struct {...@@ -807,6 +818,11 @@ pub const Dir = struct {
807 /// `true` means the opened directory can be scanned for the files and sub-directories818 /// `true` means the opened directory can be scanned for the files and sub-directories
808 /// of the result. It means the `iterate` function can be called.819 /// of the result. It means the `iterate` function can be called.
809 iterate: bool = false,820 iterate: bool = false,
821
822 /// `true` means the opened directory can be passed to a child process.
823 /// `false` means the directory handle is considered to be closed when a child
824 /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX.
825 share_with_child_process: bool = false,
810 };826 };
811827
812 /// Opens a directory at the given path. The directory is a system resource that remains828 /// Opens a directory at the given path. The directory is a system resource that remains
...@@ -832,9 +848,11 @@ pub const Dir = struct {...@@ -832,9 +848,11 @@ pub const Dir = struct {
832 return self.openDirW(&sub_path_w, args);848 return self.openDirW(&sub_path_w, args);
833 } else if (!args.iterate) {849 } else if (!args.iterate) {
834 const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0;850 const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0;
835 return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH);851 const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC;
852 return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC | O_PATH);
836 } else {853 } else {
837 return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC);854 const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC;
855 return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC);
838 }856 }
839 }857 }
840858
lib/std/fs/file.zig+10
...@@ -69,6 +69,11 @@ pub const File = struct {...@@ -69,6 +69,11 @@ pub const File = struct {
69 /// It allows the use of `noasync` when calling functions related to opening69 /// It allows the use of `noasync` when calling functions related to opening
70 /// the file, reading, and writing.70 /// the file, reading, and writing.
71 always_blocking: bool = false,71 always_blocking: bool = false,
72
73 /// `true` means the opened directory can be passed to a child process.
74 /// `false` means the directory handle is considered to be closed when a child
75 /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX.
76 share_with_child_process: bool = false,
72 };77 };
7378
74 /// TODO https://github.com/ziglang/zig/issues/380279 /// TODO https://github.com/ziglang/zig/issues/3802
...@@ -107,6 +112,11 @@ pub const File = struct {...@@ -107,6 +112,11 @@ pub const File = struct {
107 /// For POSIX systems this is the file system mode the file will112 /// For POSIX systems this is the file system mode the file will
108 /// be created with.113 /// be created with.
109 mode: Mode = default_mode,114 mode: Mode = default_mode,
115
116 /// `true` means the opened directory can be passed to a child process.
117 /// `false` means the directory handle is considered to be closed when a child
118 /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX.
119 share_with_child_process: bool = false,
110 };120 };
111121
112 /// Upon success, the stream is in an uninitialized state. To continue using it,122 /// Upon success, the stream is in an uninitialized state. To continue using it,
lib/std/testing.zig+38
...@@ -193,6 +193,44 @@ pub fn expect(ok: bool) void {...@@ -193,6 +193,44 @@ pub fn expect(ok: bool) void {
193 if (!ok) @panic("test failure");193 if (!ok) @panic("test failure");
194}194}
195195
196pub const TmpDir = struct {
197 dir: std.fs.Dir,
198 parent_dir: std.fs.Dir,
199 sub_path: [sub_path_len]u8,
200
201 const random_bytes_count = 12;
202 const sub_path_len = std.base64.Base64Encoder.calcSize(random_bytes_count);
203
204 pub fn cleanup(self: *TmpDir) void {
205 self.dir.close();
206 self.parent_dir.deleteTree(&self.sub_path) catch {};
207 self.parent_dir.close();
208 self.* = undefined;
209 }
210};
211
212pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
213 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
214 std.crypto.randomBytes(&random_bytes) catch
215 @panic("unable to make tmp dir for testing: unable to get random bytes");
216 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
217 std.fs.base64_encoder.encode(&sub_path, &random_bytes);
218
219 var cache_dir = std.fs.cwd().makeOpenPath("zig-cache", .{}) catch
220 @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");
221 defer cache_dir.close();
222 var parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch
223 @panic("unable to make tmp dir for testing: unable to make and open zig-cache/tmp dir");
224 var dir = parent_dir.makeOpenPath(&sub_path, opts) catch
225 @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
226
227 return .{
228 .dir = dir,
229 .parent_dir = parent_dir,
230 .sub_path = sub_path,
231 };
232}
233
196test "expectEqual nested array" {234test "expectEqual nested array" {
197 const a = [2][2]f32{235 const a = [2][2]f32{
198 [_]f32{ 1.0, 0.0 },236 [_]f32{ 1.0, 0.0 },
lib/std/zig.zig+17
...@@ -9,6 +9,23 @@ pub const ast = @import("zig/ast.zig");...@@ -9,6 +9,23 @@ pub const ast = @import("zig/ast.zig");
9pub const system = @import("zig/system.zig");9pub const system = @import("zig/system.zig");
10pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;10pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
1111
12pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
13 var line: usize = 0;
14 var column: usize = 0;
15 for (source[0..byte_offset]) |byte| {
16 switch (byte) {
17 '\n' => {
18 line += 1;
19 column = 0;
20 },
21 else => {
22 column += 1;
23 },
24 }
25 }
26 return .{ .line = line, .column = column };
27}
28
12test "" {29test "" {
13 @import("std").meta.refAllDecls(@This());30 @import("std").meta.refAllDecls(@This());
14}31}
lib/std/zig/system.zig+6-1
...@@ -415,7 +415,12 @@ pub const NativeTargetInfo = struct {...@@ -415,7 +415,12 @@ pub const NativeTargetInfo = struct {
415 // over our own shared objects and find a dynamic linker.415 // over our own shared objects and find a dynamic linker.
416 self_exe: {416 self_exe: {
417 const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);417 const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);
418 defer allocator.free(lib_paths);418 defer {
419 for (lib_paths) |lib_path| {
420 allocator.free(lib_path);
421 }
422 allocator.free(lib_paths);
423 }
419424
420 var found_ld_info: LdInfo = undefined;425 var found_ld_info: LdInfo = undefined;
421 var found_ld_path: [:0]const u8 = undefined;426 var found_ld_path: [:0]const u8 = undefined;
src-self-hosted/ir.zig+11-23
...@@ -4,10 +4,11 @@ const Allocator = std.mem.Allocator;...@@ -4,10 +4,11 @@ const Allocator = std.mem.Allocator;
4const Value = @import("value.zig").Value;4const Value = @import("value.zig").Value;
5const Type = @import("type.zig").Type;5const Type = @import("type.zig").Type;
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const text = @import("ir/text.zig");
8const BigInt = std.math.big.Int;7const BigInt = std.math.big.Int;
9const Target = std.Target;8const Target = std.Target;
109
10pub const text = @import("ir/text.zig");
11
11/// These are in-memory, analyzed instructions. See `text.Inst` for the representation12/// These are in-memory, analyzed instructions. See `text.Inst` for the representation
12/// of instructions that correspond to the ZIR text format.13/// of instructions that correspond to the ZIR text format.
13/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,14/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
...@@ -124,6 +125,10 @@ pub const Module = struct {...@@ -124,6 +125,10 @@ pub const Module = struct {
124 pub fn deinit(self: *Module, allocator: *Allocator) void {125 pub fn deinit(self: *Module, allocator: *Allocator) void {
125 allocator.free(self.exports);126 allocator.free(self.exports);
126 allocator.free(self.errors);127 allocator.free(self.errors);
128 for (self.fns) |f| {
129 allocator.free(f.body);
130 }
131 allocator.free(self.fns);
127 self.arena.deinit();132 self.arena.deinit();
128 self.* = undefined;133 self.* = undefined;
129 }134 }
...@@ -795,7 +800,7 @@ pub fn main() anyerror!void {...@@ -795,7 +800,7 @@ pub fn main() anyerror!void {
795800
796 if (zir_module.errors.len != 0) {801 if (zir_module.errors.len != 0) {
797 for (zir_module.errors) |err_msg| {802 for (zir_module.errors) |err_msg| {
798 const loc = findLineColumn(source, err_msg.byte_offset);803 const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
799 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });804 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
800 }805 }
801 if (debug_error_trace) return error.ParseFailure;806 if (debug_error_trace) return error.ParseFailure;
...@@ -809,10 +814,10 @@ pub fn main() anyerror!void {...@@ -809,10 +814,10 @@ pub fn main() anyerror!void {
809814
810 if (analyzed_module.errors.len != 0) {815 if (analyzed_module.errors.len != 0) {
811 for (analyzed_module.errors) |err_msg| {816 for (analyzed_module.errors) |err_msg| {
812 const loc = findLineColumn(source, err_msg.byte_offset);817 const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
813 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });818 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
814 }819 }
815 if (debug_error_trace) return error.ParseFailure;820 if (debug_error_trace) return error.AnalysisFail;
816 std.process.exit(1);821 std.process.exit(1);
817 }822 }
818823
...@@ -831,30 +836,13 @@ pub fn main() anyerror!void {...@@ -831,30 +836,13 @@ pub fn main() anyerror!void {
831 defer result.deinit(allocator);836 defer result.deinit(allocator);
832 if (result.errors.len != 0) {837 if (result.errors.len != 0) {
833 for (result.errors) |err_msg| {838 for (result.errors) |err_msg| {
834 const loc = findLineColumn(source, err_msg.byte_offset);839 const loc = std.zig.findLineColumn(source, err_msg.byte_offset);
835 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });840 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
836 }841 }
837 if (debug_error_trace) return error.ParseFailure;842 if (debug_error_trace) return error.LinkFailure;
838 std.process.exit(1);843 std.process.exit(1);
839 }844 }
840}845}
841846
842fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
843 var line: usize = 0;
844 var column: usize = 0;
845 for (source[0..byte_offset]) |byte| {
846 switch (byte) {
847 '\n' => {
848 line += 1;
849 column = 0;
850 },
851 else => {
852 column += 1;
853 },
854 }
855 }
856 return .{ .line = line, .column = column };
857}
858
859// Performance optimization ideas:847// Performance optimization ideas:
860// * when analyzing use a field in the Inst instead of HashMap to track corresponding instructions848// * when analyzing use a field in the Inst instead of HashMap to track corresponding instructions
src-self-hosted/ir/text.zig+25-23
...@@ -532,9 +532,10 @@ const Parser = struct {...@@ -532,9 +532,10 @@ const Parser = struct {
532 else => |byte| return self.failByte(byte),532 else => |byte| return self.failByte(byte),
533 };533 };
534534
535 return Inst.Fn.Body{535 // Move the instructions to the arena
536 .instructions = body_context.instructions.toOwnedSlice(),536 const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len);
537 };537 mem.copy(*Inst, instrs, body_context.instructions.items);
538 return Inst.Fn.Body{ .instructions = instrs };
538 }539 }
539540
540 fn parseStringLiteral(self: *Parser) ![]u8 {541 fn parseStringLiteral(self: *Parser) ![]u8 {
...@@ -588,26 +589,27 @@ const Parser = struct {...@@ -588,26 +589,27 @@ const Parser = struct {
588589
589 fn parseRoot(self: *Parser) !void {590 fn parseRoot(self: *Parser) !void {
590 // The IR format is designed so that it can be tokenized and parsed at the same time.591 // The IR format is designed so that it can be tokenized and parsed at the same time.
591 while (true) : (self.i += 1) switch (self.source[self.i]) {592 while (true) {
592 ';' => _ = try skipToAndOver(self, '\n'),593 switch (self.source[self.i]) {
593 '@' => {594 ';' => _ = try skipToAndOver(self, '\n'),
594 self.i += 1;595 '@' => {
595 const ident = try skipToAndOver(self, ' ');596 self.i += 1;
596 skipSpace(self);597 const ident = try skipToAndOver(self, ' ');
597 try requireEatBytes(self, "=");598 skipSpace(self);
598 skipSpace(self);599 try requireEatBytes(self, "=");
599 const inst = try parseInstruction(self, null);600 skipSpace(self);
600 const ident_index = self.decls.items.len;601 const inst = try parseInstruction(self, null);
601 if (try self.global_name_map.put(ident, ident_index)) |_| {602 const ident_index = self.decls.items.len;
602 return self.fail("redefinition of identifier '{}'", .{ident});603 if (try self.global_name_map.put(ident, ident_index)) |_| {
603 }604 return self.fail("redefinition of identifier '{}'", .{ident});
604 try self.decls.append(inst);605 }
605 continue;606 try self.decls.append(inst);
606 },607 },
607 ' ', '\n' => continue,608 ' ', '\n' => self.i += 1,
608 0 => break,609 0 => break,
609 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),610 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
610 };611 }
612 }
611 }613 }
612614
613 fn eatByte(self: *Parser, byte: u8) bool {615 fn eatByte(self: *Parser, byte: u8) bool {
src-self-hosted/test.zig+131-200
...@@ -1,237 +1,168 @@...@@ -1,237 +1,168 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;2const link = @import("link.zig");
3const Target = std.Target;3const ir = @import("ir.zig");
4const Compilation = @import("compilation.zig").Compilation;4const Allocator = std.mem.Allocator;
5const introspect = @import("introspect.zig");
6const testing = std.testing;
7const errmsg = @import("errmsg.zig");
8const ZigCompiler = @import("compilation.zig").ZigCompiler;
95
10var ctx: TestContext = undefined;6var global_ctx: TestContext = undefined;
117
12test "stage2" {8test "self-hosted" {
13 // TODO provide a way to run tests in evented I/O mode9 try global_ctx.init();
14 if (!std.io.is_async) return error.SkipZigTest;10 defer global_ctx.deinit();
1511
16 // TODO https://github.com/ziglang/zig/issues/136412 try @import("stage2_tests").addCases(&global_ctx);
17 // TODO https://github.com/ziglang/zig/issues/3117
18 if (true) return error.SkipZigTest;
1913
20 try ctx.init();14 try global_ctx.run();
21 defer ctx.deinit();
22
23 try @import("stage2_tests").addCases(&ctx);
24
25 try ctx.run();
26}15}
2716
28const file1 = "1.zig";
29// TODO https://github.com/ziglang/zig/issues/3783
30const allocator = std.heap.page_allocator;
31
32pub const TestContext = struct {17pub const TestContext = struct {
33 zig_compiler: ZigCompiler,18 zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase),
34 zig_lib_dir: []u8,19
35 file_index: std.atomic.Int(usize),20 pub const ZIRCompareOutputCase = struct {
36 group: std.event.Group(anyerror!void),21 name: []const u8,
37 any_err: anyerror!void,22 src: [:0]const u8,
3823 expected_stdout: []const u8,
39 const tmp_dir_name = "stage2_test_tmp";24 };
25
26 pub fn addZIRCompareOutput(
27 ctx: *TestContext,
28 name: []const u8,
29 src: [:0]const u8,
30 expected_stdout: []const u8,
31 ) void {
32 ctx.zir_cmp_output_cases.append(.{
33 .name = name,
34 .src = src,
35 .expected_stdout = expected_stdout,
36 }) catch unreachable;
37 }
4038
41 fn init(self: *TestContext) !void {39 fn init(self: *TestContext) !void {
42 self.* = TestContext{40 self.* = .{
43 .any_err = {},41 .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator),
44 .zig_compiler = undefined,
45 .zig_lib_dir = undefined,
46 .group = undefined,
47 .file_index = std.atomic.Int(usize).init(0),
48 };42 };
49
50 self.zig_compiler = try ZigCompiler.init(allocator);
51 errdefer self.zig_compiler.deinit();
52
53 self.group = std.event.Group(anyerror!void).init(allocator);
54 errdefer self.group.wait() catch {};
55
56 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
57 errdefer allocator.free(self.zig_lib_dir);
58
59 try std.fs.cwd().makePath(tmp_dir_name);
60 errdefer std.fs.cwd().deleteTree(tmp_dir_name) catch {};
61 }43 }
6244
63 fn deinit(self: *TestContext) void {45 fn deinit(self: *TestContext) void {
64 std.fs.cwd().deleteTree(tmp_dir_name) catch {};46 self.zir_cmp_output_cases.deinit();
65 allocator.free(self.zig_lib_dir);47 self.* = undefined;
66 self.zig_compiler.deinit();
67 }48 }
6849
69 fn run(self: *TestContext) !void {50 fn run(self: *TestContext) !void {
70 std.event.Loop.startCpuBoundOperation();51 var progress = std.Progress{};
71 self.any_err = self.group.wait();52 const root_node = try progress.start("zir", self.zir_cmp_output_cases.items.len);
72 return self.any_err;53 defer root_node.end();
54
55 const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{});
56
57 for (self.zir_cmp_output_cases.items) |case| {
58 std.testing.base_allocator_instance.reset();
59 try self.runOneZIRCmpOutputCase(std.testing.allocator, root_node, case, native_info.target);
60 try std.testing.allocator_instance.validate();
61 }
73 }62 }
7463
75 fn testCompileError(64 fn runOneZIRCmpOutputCase(
76 self: *TestContext,65 self: *TestContext,
77 source: []const u8,66 allocator: *Allocator,
78 path: []const u8,67 root_node: *std.Progress.Node,
79 line: usize,68 case: ZIRCompareOutputCase,
80 column: usize,69 target: std.Target,
81 msg: []const u8,
82 ) !void {70 ) !void {
83 var file_index_buf: [20]u8 = undefined;71 var tmp = std.testing.tmpDir(.{ .share_with_child_process = true });
84 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()});72 defer tmp.cleanup();
85 const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
8673
87 if (std.fs.path.dirname(file1_path)) |dirname| {74 var prg_node = root_node.start(case.name, 4);
88 try std.fs.cwd().makePath(dirname);75 prg_node.activate();
89 }76 defer prg_node.end();
9077
91 try std.fs.cwd().writeFile(file1_path, source);78 var zir_module = x: {
79 var parse_node = prg_node.start("parse", null);
80 parse_node.activate();
81 defer parse_node.end();
9282
93 var comp = try Compilation.create(83 break :x try ir.text.parse(allocator, case.src);
94 &self.zig_compiler,84 };
95 "test",85 defer zir_module.deinit(allocator);
96 file1_path,86 if (zir_module.errors.len != 0) {
97 .Native,87 debugPrintErrors(case.src, zir_module.errors);
98 .Obj,88 return error.ParseFailure;
99 .Debug,89 }
100 true, // is_static
101 self.zig_lib_dir,
102 );
103 errdefer comp.destroy();
104
105 comp.start();
10690
107 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);91 var analyzed_module = x: {
108 }92 var analyze_node = prg_node.start("analyze", null);
93 analyze_node.activate();
94 defer analyze_node.end();
10995
110 fn testCompareOutputLibC(96 break :x try ir.analyze(allocator, zir_module, target);
111 self: *TestContext,97 };
112 source: []const u8,98 defer analyzed_module.deinit(allocator);
113 expected_output: []const u8,99 if (analyzed_module.errors.len != 0) {
114 ) !void {100 debugPrintErrors(case.src, analyzed_module.errors);
115 var file_index_buf: [20]u8 = undefined;101 return error.ParseFailure;
116 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()});102 }
117 const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
118103
119 const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() });104 var link_result = x: {
120 if (std.fs.path.dirname(file1_path)) |dirname| {105 var link_node = prg_node.start("link", null);
121 try std.fs.cwd().makePath(dirname);106 link_node.activate();
107 defer link_node.end();
108
109 break :x try link.updateExecutableFilePath(
110 allocator,
111 analyzed_module,
112 tmp.dir,
113 "a.out",
114 );
115 };
116 defer link_result.deinit(allocator);
117 if (link_result.errors.len != 0) {
118 debugPrintErrors(case.src, link_result.errors);
119 return error.LinkFailure;
122 }120 }
123121
124 try std.fs.cwd().writeFile(file1_path, source);122 var exec_result = x: {
125123 var exec_node = prg_node.start("execute", null);
126 var comp = try Compilation.create(124 exec_node.activate();
127 &self.zig_compiler,125 defer exec_node.end();
128 "test",
129 file1_path,
130 .Native,
131 .Exe,
132 .Debug,
133 false,
134 self.zig_lib_dir,
135 );
136 errdefer comp.destroy();
137
138 _ = try comp.addLinkLib("c", true);
139 comp.link_out_file = output_file;
140 comp.start();
141
142 try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
143 }
144126
145 async fn getModuleEventSuccess(127 break :x try std.ChildProcess.exec(.{
146 comp: *Compilation,128 .allocator = allocator,
147 exe_file: []const u8,129 .argv = &[_][]const u8{"./a.out"},
148 expected_output: []const u8,130 .cwd_dir = tmp.dir,
149 ) anyerror!void {131 });
150 defer comp.destroy();132 };
151 const build_event = comp.events.get();133 defer allocator.free(exec_result.stdout);
152134 defer allocator.free(exec_result.stderr);
153 switch (build_event) {135 switch (exec_result.term) {
154 .Ok => {136 .Exited => |code| {
155 const argv = [_][]const u8{exe_file};137 if (code != 0) {
156 // TODO use event loop138 std.debug.warn("elf file exited with code {}\n", .{code});
157 const child = try std.ChildProcess.exec(.{139 return error.BinaryBadExitCode;
158 .allocator = allocator,
159 .argv = argv,
160 .max_output_bytes = 1024 * 1024,
161 });
162 switch (child.term) {
163 .Exited => |code| {
164 if (code != 0) {
165 return error.BadReturnCode;
166 }
167 },
168 else => {
169 return error.Crashed;
170 },
171 }
172 if (!mem.eql(u8, child.stdout, expected_output)) {
173 return error.OutputMismatch;
174 }
175 },
176 .Error => @panic("Cannot return error: https://github.com/ziglang/zig/issues/3190"), // |err| return err,
177 .Fail => |msgs| {
178 const stderr = std.io.getStdErr();
179 try stderr.write("build incorrectly failed:\n");
180 for (msgs) |msg| {
181 defer msg.destroy();
182 try msg.printToFile(stderr, .Auto);
183 }140 }
184 },141 },
142 else => return error.BinaryCrashed,
185 }143 }
144 std.testing.expectEqualSlices(u8, case.expected_stdout, exec_result.stdout);
186 }145 }
146};
187147
188 async fn getModuleEvent(148fn debugPrintErrors(src: []const u8, errors: var) void {
189 comp: *Compilation,149 std.debug.warn("\n", .{});
190 source: []const u8,150 var nl = true;
191 path: []const u8,151 var line: usize = 1;
192 line: usize,152 for (src) |byte| {
193 column: usize,153 if (nl) {
194 text: []const u8,154 std.debug.warn("{: >3}| ", .{line});
195 ) anyerror!void {155 nl = false;
196 defer comp.destroy();
197 const build_event = comp.events.get();
198
199 switch (build_event) {
200 .Ok => {
201 @panic("build incorrectly succeeded");
202 },
203 .Error => |err| {
204 @panic("build incorrectly failed");
205 },
206 .Fail => |msgs| {
207 testing.expect(msgs.len != 0);
208 for (msgs) |msg| {
209 if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) {
210 const span = msg.getSpan();
211 const first_token = msg.getTree().tokens.at(span.first);
212 const last_token = msg.getTree().tokens.at(span.first);
213 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
214 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
215 return;
216 }
217 }
218 }
219 std.debug.warn("\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n", .{
220 source,
221 path,
222 line,
223 column,
224 text,
225 });
226 std.debug.warn("\n====found:========\n", .{});
227 const stderr = std.io.getStdErr();
228 for (msgs) |msg| {
229 defer msg.destroy();
230 try msg.printToFile(stderr, errmsg.Color.Auto);
231 }
232 std.debug.warn("============\n", .{});
233 return error.TestFailed;
234 },
235 }156 }
157 if (byte == '\n') {
158 nl = true;
159 line += 1;
160 }
161 std.debug.warn("{c}", .{byte});
236 }162 }
237};163 std.debug.warn("\n", .{});
164 for (errors) |err_msg| {
165 const loc = std.zig.findLineColumn(src, err_msg.byte_offset);
166 std.debug.warn("{}:{}: error: {}\n", .{ loc.line + 1, loc.column + 1, err_msg.msg });
167 }
168}
test/stage2/compare_output.zig+22-19
...@@ -2,24 +2,27 @@ const std = @import("std");...@@ -2,24 +2,27 @@ const std = @import("std");
2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
33
4pub fn addCases(ctx: *TestContext) !void {4pub fn addCases(ctx: *TestContext) !void {
5 // hello world5 // TODO: re-enable these tests.
6 try ctx.testCompareOutputLibC(6 // https://github.com/ziglang/zig/issues/1364
7 \\extern fn puts([*]const u8) void;
8 \\pub export fn main() c_int {
9 \\ puts("Hello, world!");
10 \\ return 0;
11 \\}
12 , "Hello, world!" ++ std.cstr.line_sep);
137
14 // function calling another function8 //// hello world
15 try ctx.testCompareOutputLibC(9 //try ctx.testCompareOutputLibC(
16 \\extern fn puts(s: [*]const u8) void;10 // \\extern fn puts([*]const u8) void;
17 \\pub export fn main() c_int {11 // \\pub export fn main() c_int {
18 \\ return foo("OK");12 // \\ puts("Hello, world!");
19 \\}13 // \\ return 0;
20 \\fn foo(s: [*]const u8) c_int {14 // \\}
21 \\ puts(s);15 //, "Hello, world!" ++ std.cstr.line_sep);
22 \\ return 0;16
23 \\}17 //// function calling another function
24 , "OK" ++ std.cstr.line_sep);18 //try ctx.testCompareOutputLibC(
19 // \\extern fn puts(s: [*]const u8) void;
20 // \\pub export fn main() c_int {
21 // \\ return foo("OK");
22 // \\}
23 // \\fn foo(s: [*]const u8) c_int {
24 // \\ puts(s);
25 // \\ return 0;
26 // \\}
27 //, "OK" ++ std.cstr.line_sep);
25}28}
test/stage2/compile_errors.zig+53-50
...@@ -1,54 +1,57 @@...@@ -1,54 +1,57 @@
1const TestContext = @import("../../src-self-hosted/test.zig").TestContext;1const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
22
3pub fn addCases(ctx: *TestContext) !void {3pub fn addCases(ctx: *TestContext) !void {
4 try ctx.testCompileError(4 // TODO: re-enable these tests.
5 \\export fn entry() void {}5 // https://github.com/ziglang/zig/issues/1364
6 \\export fn entry() void {}6
7 , "1.zig", 2, 8, "exported symbol collision: 'entry'");7 //try ctx.testCompileError(
88 // \\export fn entry() void {}
9 try ctx.testCompileError(9 // \\export fn entry() void {}
10 \\fn() void {}10 //, "1.zig", 2, 8, "exported symbol collision: 'entry'");
11 , "1.zig", 1, 1, "missing function name");11
1212 //try ctx.testCompileError(
13 try ctx.testCompileError(13 // \\fn() void {}
14 \\comptime {14 //, "1.zig", 1, 1, "missing function name");
15 \\ return;15
16 \\}16 //try ctx.testCompileError(
17 , "1.zig", 2, 5, "return expression outside function definition");17 // \\comptime {
1818 // \\ return;
19 try ctx.testCompileError(19 // \\}
20 \\export fn entry() void {20 //, "1.zig", 2, 5, "return expression outside function definition");
21 \\ defer return;21
22 \\}22 //try ctx.testCompileError(
23 , "1.zig", 2, 11, "cannot return from defer expression");23 // \\export fn entry() void {
2424 // \\ defer return;
25 try ctx.testCompileError(25 // \\}
26 \\export fn entry() c_int {26 //, "1.zig", 2, 11, "cannot return from defer expression");
27 \\ return 36893488147419103232;27
28 \\}28 //try ctx.testCompileError(
29 , "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");29 // \\export fn entry() c_int {
3030 // \\ return 36893488147419103232;
31 try ctx.testCompileError(31 // \\}
32 \\comptime {32 //, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
33 \\ var a: *align(4) align(4) i32 = 0;33
34 \\}34 //try ctx.testCompileError(
35 , "1.zig", 2, 22, "Extra align qualifier");35 // \\comptime {
3636 // \\ var a: *align(4) align(4) i32 = 0;
37 try ctx.testCompileError(37 // \\}
38 \\comptime {38 //, "1.zig", 2, 22, "Extra align qualifier");
39 \\ var b: *const const i32 = 0;39
40 \\}40 //try ctx.testCompileError(
41 , "1.zig", 2, 19, "Extra align qualifier");41 // \\comptime {
4242 // \\ var b: *const const i32 = 0;
43 try ctx.testCompileError(43 // \\}
44 \\comptime {44 //, "1.zig", 2, 19, "Extra align qualifier");
45 \\ var c: *volatile volatile i32 = 0;45
46 \\}46 //try ctx.testCompileError(
47 , "1.zig", 2, 22, "Extra align qualifier");47 // \\comptime {
4848 // \\ var c: *volatile volatile i32 = 0;
49 try ctx.testCompileError(49 // \\}
50 \\comptime {50 //, "1.zig", 2, 22, "Extra align qualifier");
51 \\ var d: *allowzero allowzero i32 = 0;51
52 \\}52 //try ctx.testCompileError(
53 , "1.zig", 2, 23, "Extra align qualifier");53 // \\comptime {
54 // \\ var d: *allowzero allowzero i32 = 0;
55 // \\}
56 //, "1.zig", 2, 23, "Extra align qualifier");
54}57}
test/stage2/ir.zig deleted-54
...@@ -1,54 +0,0 @@
1test "hello world IR" {
2 exeCmp(
3 \\@0 = str("Hello, world!\n")
4 \\@1 = primitive(void)
5 \\@2 = primitive(usize)
6 \\@3 = fntype([], @1, cc=Naked)
7 \\@4 = int(0)
8 \\@5 = int(1)
9 \\@6 = int(231)
10 \\@7 = str("len")
11 \\
12 \\@8 = fn(@3, {
13 \\ %0 = as(@2, @5) ; SYS_write
14 \\ %1 = as(@2, @5) ; STDOUT_FILENO
15 \\ %2 = ptrtoint(@0) ; msg ptr
16 \\ %3 = fieldptr(@0, @7) ; msg len ptr
17 \\ %4 = deref(%3) ; msg len
18 \\ %sysoutreg = str("={rax}")
19 \\ %rax = str("{rax}")
20 \\ %rdi = str("{rdi}")
21 \\ %rsi = str("{rsi}")
22 \\ %rdx = str("{rdx}")
23 \\ %rcx = str("rcx")
24 \\ %r11 = str("r11")
25 \\ %memory = str("memory")
26 \\ %syscall = str("syscall")
27 \\ %5 = asm(%syscall, @2,
28 \\ volatile=1,
29 \\ output=%sysoutreg,
30 \\ inputs=[%rax, %rdi, %rsi, %rdx],
31 \\ clobbers=[%rcx, %r11, %memory],
32 \\ args=[%0, %1, %2, %4])
33 \\
34 \\ %6 = as(@2, @6) ;SYS_exit_group
35 \\ %7 = as(@2, @4) ;exit code
36 \\ %8 = asm(%syscall, @2,
37 \\ volatile=1,
38 \\ output=%sysoutreg,
39 \\ inputs=[%rax, %rdi],
40 \\ clobbers=[%rcx, %r11, %memory],
41 \\ args=[%6, %7])
42 \\
43 \\ %9 = unreachable()
44 \\})
45 \\
46 \\@9 = str("_start")
47 \\@10 = export(@9, @8)
48 ,
49 \\Hello, world!
50 \\
51 );
52}
53
54fn exeCmp(src: []const u8, expected_stdout: []const u8) void {}
test/stage2/test.zig+1
...@@ -3,4 +3,5 @@ const TestContext = @import("../../src-self-hosted/test.zig").TestContext;...@@ -3,4 +3,5 @@ const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
3pub fn addCases(ctx: *TestContext) !void {3pub fn addCases(ctx: *TestContext) !void {
4 try @import("compile_errors.zig").addCases(ctx);4 try @import("compile_errors.zig").addCases(ctx);
5 try @import("compare_output.zig").addCases(ctx);5 try @import("compare_output.zig").addCases(ctx);
6 @import("zir.zig").addCases(ctx);
6}7}
test/stage2/zir.zig created+59
...@@ -0,0 +1,59 @@
1const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
2
3pub fn addCases(ctx: *TestContext) void {
4 if (@import("std").Target.current.os.tag == .windows) {
5 // TODO implement self-hosted PE (.exe file) linking
6 return;
7 }
8
9 ctx.addZIRCompareOutput("hello world ZIR",
10 \\@0 = str("Hello, world!\n")
11 \\@1 = primitive(noreturn)
12 \\@2 = primitive(usize)
13 \\@3 = fntype([], @1, cc=Naked)
14 \\@4 = int(0)
15 \\@5 = int(1)
16 \\@6 = int(231)
17 \\@7 = str("len")
18 \\
19 \\@8 = fn(@3, {
20 \\ %0 = as(@2, @5) ; SYS_write
21 \\ %1 = as(@2, @5) ; STDOUT_FILENO
22 \\ %2 = ptrtoint(@0) ; msg ptr
23 \\ %3 = fieldptr(@0, @7) ; msg len ptr
24 \\ %4 = deref(%3) ; msg len
25 \\ %sysoutreg = str("={rax}")
26 \\ %rax = str("{rax}")
27 \\ %rdi = str("{rdi}")
28 \\ %rsi = str("{rsi}")
29 \\ %rdx = str("{rdx}")
30 \\ %rcx = str("rcx")
31 \\ %r11 = str("r11")
32 \\ %memory = str("memory")
33 \\ %syscall = str("syscall")
34 \\ %5 = asm(%syscall, @2,
35 \\ volatile=1,
36 \\ output=%sysoutreg,
37 \\ inputs=[%rax, %rdi, %rsi, %rdx],
38 \\ clobbers=[%rcx, %r11, %memory],
39 \\ args=[%0, %1, %2, %4])
40 \\
41 \\ %6 = as(@2, @6) ;SYS_exit_group
42 \\ %7 = as(@2, @4) ;exit code
43 \\ %8 = asm(%syscall, @2,
44 \\ volatile=1,
45 \\ output=%sysoutreg,
46 \\ inputs=[%rax, %rdi],
47 \\ clobbers=[%rcx, %r11, %memory],
48 \\ args=[%6, %7])
49 \\
50 \\ %9 = unreachable()
51 \\})
52 \\
53 \\@9 = str("_start")
54 \\@10 = export(@9, @8)
55 ,
56 \\Hello, world!
57 \\
58 );
59}