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 {
4444 try findAndReadConfigH(b);
4545
4646 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
4848 test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig");
4949
5050 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
......@@ -68,7 +68,6 @@ pub fn build(b: *Builder) !void {
6868 var ctx = parseConfigH(b, config_h_text);
6969 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
7070
71 try configureStage2(b, test_stage2, ctx);
7271 try configureStage2(b, exe, ctx);
7372
7473 b.default_step.dependOn(&exe.step);
lib/std/child_process.zig+11-1
......@@ -46,6 +46,12 @@ pub const ChildProcess = struct {
4646
4747 /// Set to change the current working directory when spawning the child process.
4848 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
5056 err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t,
5157
......@@ -183,6 +189,7 @@ pub const ChildProcess = struct {
183189 allocator: *mem.Allocator,
184190 argv: []const []const u8,
185191 cwd: ?[]const u8 = null,
192 cwd_dir: ?fs.Dir = null,
186193 env_map: ?*const BufMap = null,
187194 max_output_bytes: usize = 50 * 1024,
188195 expand_arg0: Arg0Expand = .no_expand,
......@@ -194,6 +201,7 @@ pub const ChildProcess = struct {
194201 child.stdout_behavior = .Pipe;
195202 child.stderr_behavior = .Pipe;
196203 child.cwd = args.cwd;
204 child.cwd_dir = args.cwd_dir;
197205 child.env_map = args.env_map;
198206 child.expand_arg0 = args.expand_arg0;
199207
......@@ -414,7 +422,9 @@ pub const ChildProcess = struct {
414422 os.close(stderr_pipe[1]);
415423 }
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| {
418428 os.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err);
419429 }
420430
lib/std/fs.zig+22-4
......@@ -606,7 +606,8 @@ pub const Dir = struct {
606606 } else 0;
607607
608608 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)
610611 @as(u32, os.O_RDWR)
611612 else if (flags.write)
612613 @as(u32, os.O_WRONLY)
......@@ -689,7 +690,8 @@ pub const Dir = struct {
689690 } else 0;
690691
691692 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 |
693695 (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |
694696 (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |
695697 (if (flags.exclusive) @as(u32, os.O_EXCL) else 0);
......@@ -787,6 +789,15 @@ pub const Dir = struct {
787789 }
788790 }
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
790801 /// Changes the current working directory to the open directory handle.
791802 /// This modifies global state and can have surprising effects in multi-
792803 /// threaded applications. Most applications and especially libraries should
......@@ -807,6 +818,11 @@ pub const Dir = struct {
807818 /// `true` means the opened directory can be scanned for the files and sub-directories
808819 /// of the result. It means the `iterate` function can be called.
809820 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,
810826 };
811827
812828 /// Opens a directory at the given path. The directory is a system resource that remains
......@@ -832,9 +848,11 @@ pub const Dir = struct {
832848 return self.openDirW(&sub_path_w, args);
833849 } else if (!args.iterate) {
834850 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);
836853 } 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);
838856 }
839857 }
840858
lib/std/fs/file.zig+10
......@@ -69,6 +69,11 @@ pub const File = struct {
6969 /// It allows the use of `noasync` when calling functions related to opening
7070 /// the file, reading, and writing.
7171 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,
7277 };
7378
7479 /// TODO https://github.com/ziglang/zig/issues/3802
......@@ -107,6 +112,11 @@ pub const File = struct {
107112 /// For POSIX systems this is the file system mode the file will
108113 /// be created with.
109114 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,
110120 };
111121
112122 /// 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 {
193193 if (!ok) @panic("test failure");
194194}
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
196234test "expectEqual nested array" {
197235 const a = [2][2]f32{
198236 [_]f32{ 1.0, 0.0 },
lib/std/zig.zig+17
......@@ -9,6 +9,23 @@ pub const ast = @import("zig/ast.zig");
99pub const system = @import("zig/system.zig");
1010pub 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
1229test "" {
1330 @import("std").meta.refAllDecls(@This());
1431}
lib/std/zig/system.zig+6-1
......@@ -415,7 +415,12 @@ pub const NativeTargetInfo = struct {
415415 // over our own shared objects and find a dynamic linker.
416416 self_exe: {
417417 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
420425 var found_ld_info: LdInfo = undefined;
421426 var found_ld_path: [:0]const u8 = undefined;
src-self-hosted/ir.zig+11-23
......@@ -4,10 +4,11 @@ const Allocator = std.mem.Allocator;
44const Value = @import("value.zig").Value;
55const Type = @import("type.zig").Type;
66const assert = std.debug.assert;
7const text = @import("ir/text.zig");
87const BigInt = std.math.big.Int;
98const Target = std.Target;
109
10pub const text = @import("ir/text.zig");
11
1112/// These are in-memory, analyzed instructions. See `text.Inst` for the representation
1213/// of instructions that correspond to the ZIR text format.
1314/// This struct owns the `Value` and `Type` memory. When the struct is deallocated,
......@@ -124,6 +125,10 @@ pub const Module = struct {
124125 pub fn deinit(self: *Module, allocator: *Allocator) void {
125126 allocator.free(self.exports);
126127 allocator.free(self.errors);
128 for (self.fns) |f| {
129 allocator.free(f.body);
130 }
131 allocator.free(self.fns);
127132 self.arena.deinit();
128133 self.* = undefined;
129134 }
......@@ -795,7 +800,7 @@ pub fn main() anyerror!void {
795800
796801 if (zir_module.errors.len != 0) {
797802 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);
799804 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
800805 }
801806 if (debug_error_trace) return error.ParseFailure;
......@@ -809,10 +814,10 @@ pub fn main() anyerror!void {
809814
810815 if (analyzed_module.errors.len != 0) {
811816 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);
813818 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
814819 }
815 if (debug_error_trace) return error.ParseFailure;
820 if (debug_error_trace) return error.AnalysisFail;
816821 std.process.exit(1);
817822 }
818823
......@@ -831,30 +836,13 @@ pub fn main() anyerror!void {
831836 defer result.deinit(allocator);
832837 if (result.errors.len != 0) {
833838 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);
835840 std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
836841 }
837 if (debug_error_trace) return error.ParseFailure;
842 if (debug_error_trace) return error.LinkFailure;
838843 std.process.exit(1);
839844 }
840845}
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
859847// Performance optimization ideas:
860848// * 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 {
532532 else => |byte| return self.failByte(byte),
533533 };
534534
535 return Inst.Fn.Body{
536 .instructions = body_context.instructions.toOwnedSlice(),
537 };
535 // Move the instructions to the arena
536 const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len);
537 mem.copy(*Inst, instrs, body_context.instructions.items);
538 return Inst.Fn.Body{ .instructions = instrs };
538539 }
539540
540541 fn parseStringLiteral(self: *Parser) ![]u8 {
......@@ -588,26 +589,27 @@ const Parser = struct {
588589
589590 fn parseRoot(self: *Parser) !void {
590591 // 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 ';' => _ = try skipToAndOver(self, '\n'),
593 '@' => {
594 self.i += 1;
595 const ident = try skipToAndOver(self, ' ');
596 skipSpace(self);
597 try requireEatBytes(self, "=");
598 skipSpace(self);
599 const inst = try parseInstruction(self, null);
600 const ident_index = self.decls.items.len;
601 if (try self.global_name_map.put(ident, ident_index)) |_| {
602 return self.fail("redefinition of identifier '{}'", .{ident});
603 }
604 try self.decls.append(inst);
605 continue;
606 },
607 ' ', '\n' => continue,
608 0 => break,
609 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
610 };
592 while (true) {
593 switch (self.source[self.i]) {
594 ';' => _ = try skipToAndOver(self, '\n'),
595 '@' => {
596 self.i += 1;
597 const ident = try skipToAndOver(self, ' ');
598 skipSpace(self);
599 try requireEatBytes(self, "=");
600 skipSpace(self);
601 const inst = try parseInstruction(self, null);
602 const ident_index = self.decls.items.len;
603 if (try self.global_name_map.put(ident, ident_index)) |_| {
604 return self.fail("redefinition of identifier '{}'", .{ident});
605 }
606 try self.decls.append(inst);
607 },
608 ' ', '\n' => self.i += 1,
609 0 => break,
610 else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}),
611 }
612 }
611613 }
612614
613615 fn eatByte(self: *Parser, byte: u8) bool {
src-self-hosted/test.zig+131-200
......@@ -1,237 +1,168 @@
11const std = @import("std");
2const mem = std.mem;
3const Target = std.Target;
4const Compilation = @import("compilation.zig").Compilation;
5const introspect = @import("introspect.zig");
6const testing = std.testing;
7const errmsg = @import("errmsg.zig");
8const ZigCompiler = @import("compilation.zig").ZigCompiler;
2const link = @import("link.zig");
3const ir = @import("ir.zig");
4const Allocator = std.mem.Allocator;
95
10var ctx: TestContext = undefined;
6var global_ctx: TestContext = undefined;
117
12test "stage2" {
13 // TODO provide a way to run tests in evented I/O mode
14 if (!std.io.is_async) return error.SkipZigTest;
8test "self-hosted" {
9 try global_ctx.init();
10 defer global_ctx.deinit();
1511
16 // TODO https://github.com/ziglang/zig/issues/1364
17 // TODO https://github.com/ziglang/zig/issues/3117
18 if (true) return error.SkipZigTest;
12 try @import("stage2_tests").addCases(&global_ctx);
1913
20 try ctx.init();
21 defer ctx.deinit();
22
23 try @import("stage2_tests").addCases(&ctx);
24
25 try ctx.run();
14 try global_ctx.run();
2615}
2716
28const file1 = "1.zig";
29// TODO https://github.com/ziglang/zig/issues/3783
30const allocator = std.heap.page_allocator;
31
3217pub const TestContext = struct {
33 zig_compiler: ZigCompiler,
34 zig_lib_dir: []u8,
35 file_index: std.atomic.Int(usize),
36 group: std.event.Group(anyerror!void),
37 any_err: anyerror!void,
38
39 const tmp_dir_name = "stage2_test_tmp";
18 zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase),
19
20 pub const ZIRCompareOutputCase = struct {
21 name: []const u8,
22 src: [:0]const u8,
23 expected_stdout: []const u8,
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
4139 fn init(self: *TestContext) !void {
42 self.* = TestContext{
43 .any_err = {},
44 .zig_compiler = undefined,
45 .zig_lib_dir = undefined,
46 .group = undefined,
47 .file_index = std.atomic.Int(usize).init(0),
40 self.* = .{
41 .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator),
4842 };
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 {};
6143 }
6244
6345 fn deinit(self: *TestContext) void {
64 std.fs.cwd().deleteTree(tmp_dir_name) catch {};
65 allocator.free(self.zig_lib_dir);
66 self.zig_compiler.deinit();
46 self.zir_cmp_output_cases.deinit();
47 self.* = undefined;
6748 }
6849
6950 fn run(self: *TestContext) !void {
70 std.event.Loop.startCpuBoundOperation();
71 self.any_err = self.group.wait();
72 return self.any_err;
51 var progress = std.Progress{};
52 const root_node = try progress.start("zir", self.zir_cmp_output_cases.items.len);
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 }
7362 }
7463
75 fn testCompileError(
64 fn runOneZIRCmpOutputCase(
7665 self: *TestContext,
77 source: []const u8,
78 path: []const u8,
79 line: usize,
80 column: usize,
81 msg: []const u8,
66 allocator: *Allocator,
67 root_node: *std.Progress.Node,
68 case: ZIRCompareOutputCase,
69 target: std.Target,
8270 ) !void {
83 var file_index_buf: [20]u8 = undefined;
84 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()});
85 const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
71 var tmp = std.testing.tmpDir(.{ .share_with_child_process = true });
72 defer tmp.cleanup();
8673
87 if (std.fs.path.dirname(file1_path)) |dirname| {
88 try std.fs.cwd().makePath(dirname);
89 }
74 var prg_node = root_node.start(case.name, 4);
75 prg_node.activate();
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(
94 &self.zig_compiler,
95 "test",
96 file1_path,
97 .Native,
98 .Obj,
99 .Debug,
100 true, // is_static
101 self.zig_lib_dir,
102 );
103 errdefer comp.destroy();
104
105 comp.start();
83 break :x try ir.text.parse(allocator, case.src);
84 };
85 defer zir_module.deinit(allocator);
86 if (zir_module.errors.len != 0) {
87 debugPrintErrors(case.src, zir_module.errors);
88 return error.ParseFailure;
89 }
10690
107 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
108 }
91 var analyzed_module = x: {
92 var analyze_node = prg_node.start("analyze", null);
93 analyze_node.activate();
94 defer analyze_node.end();
10995
110 fn testCompareOutputLibC(
111 self: *TestContext,
112 source: []const u8,
113 expected_output: []const u8,
114 ) !void {
115 var file_index_buf: [20]u8 = undefined;
116 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()});
117 const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
96 break :x try ir.analyze(allocator, zir_module, target);
97 };
98 defer analyzed_module.deinit(allocator);
99 if (analyzed_module.errors.len != 0) {
100 debugPrintErrors(case.src, analyzed_module.errors);
101 return error.ParseFailure;
102 }
118103
119 const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() });
120 if (std.fs.path.dirname(file1_path)) |dirname| {
121 try std.fs.cwd().makePath(dirname);
104 var link_result = x: {
105 var link_node = prg_node.start("link", null);
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;
122120 }
123121
124 try std.fs.cwd().writeFile(file1_path, source);
125
126 var comp = try Compilation.create(
127 &self.zig_compiler,
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 }
122 var exec_result = x: {
123 var exec_node = prg_node.start("execute", null);
124 exec_node.activate();
125 defer exec_node.end();
144126
145 async fn getModuleEventSuccess(
146 comp: *Compilation,
147 exe_file: []const u8,
148 expected_output: []const u8,
149 ) anyerror!void {
150 defer comp.destroy();
151 const build_event = comp.events.get();
152
153 switch (build_event) {
154 .Ok => {
155 const argv = [_][]const u8{exe_file};
156 // TODO use event loop
157 const child = try std.ChildProcess.exec(.{
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);
127 break :x try std.ChildProcess.exec(.{
128 .allocator = allocator,
129 .argv = &[_][]const u8{"./a.out"},
130 .cwd_dir = tmp.dir,
131 });
132 };
133 defer allocator.free(exec_result.stdout);
134 defer allocator.free(exec_result.stderr);
135 switch (exec_result.term) {
136 .Exited => |code| {
137 if (code != 0) {
138 std.debug.warn("elf file exited with code {}\n", .{code});
139 return error.BinaryBadExitCode;
183140 }
184141 },
142 else => return error.BinaryCrashed,
185143 }
144 std.testing.expectEqualSlices(u8, case.expected_stdout, exec_result.stdout);
186145 }
146};
187147
188 async fn getModuleEvent(
189 comp: *Compilation,
190 source: []const u8,
191 path: []const u8,
192 line: usize,
193 column: usize,
194 text: []const u8,
195 ) anyerror!void {
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 },
148fn debugPrintErrors(src: []const u8, errors: var) void {
149 std.debug.warn("\n", .{});
150 var nl = true;
151 var line: usize = 1;
152 for (src) |byte| {
153 if (nl) {
154 std.debug.warn("{: >3}| ", .{line});
155 nl = false;
235156 }
157 if (byte == '\n') {
158 nl = true;
159 line += 1;
160 }
161 std.debug.warn("{c}", .{byte});
236162 }
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");
22const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
33
44pub fn addCases(ctx: *TestContext) !void {
5 // hello world
6 try ctx.testCompareOutputLibC(
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);
5 // TODO: re-enable these tests.
6 // https://github.com/ziglang/zig/issues/1364
137
14 // function calling another function
15 try ctx.testCompareOutputLibC(
16 \\extern fn puts(s: [*]const u8) void;
17 \\pub export fn main() c_int {
18 \\ return foo("OK");
19 \\}
20 \\fn foo(s: [*]const u8) c_int {
21 \\ puts(s);
22 \\ return 0;
23 \\}
24 , "OK" ++ std.cstr.line_sep);
8 //// hello world
9 //try ctx.testCompareOutputLibC(
10 // \\extern fn puts([*]const u8) void;
11 // \\pub export fn main() c_int {
12 // \\ puts("Hello, world!");
13 // \\ return 0;
14 // \\}
15 //, "Hello, world!" ++ std.cstr.line_sep);
16
17 //// function calling another function
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);
2528}
test/stage2/compile_errors.zig+53-50
......@@ -1,54 +1,57 @@
11const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
22
33pub fn addCases(ctx: *TestContext) !void {
4 try ctx.testCompileError(
5 \\export fn entry() void {}
6 \\export fn entry() void {}
7 , "1.zig", 2, 8, "exported symbol collision: 'entry'");
8
9 try ctx.testCompileError(
10 \\fn() void {}
11 , "1.zig", 1, 1, "missing function name");
12
13 try ctx.testCompileError(
14 \\comptime {
15 \\ return;
16 \\}
17 , "1.zig", 2, 5, "return expression outside function definition");
18
19 try ctx.testCompileError(
20 \\export fn entry() void {
21 \\ defer return;
22 \\}
23 , "1.zig", 2, 11, "cannot return from defer expression");
24
25 try ctx.testCompileError(
26 \\export fn entry() c_int {
27 \\ return 36893488147419103232;
28 \\}
29 , "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
30
31 try ctx.testCompileError(
32 \\comptime {
33 \\ var a: *align(4) align(4) i32 = 0;
34 \\}
35 , "1.zig", 2, 22, "Extra align qualifier");
36
37 try ctx.testCompileError(
38 \\comptime {
39 \\ var b: *const const i32 = 0;
40 \\}
41 , "1.zig", 2, 19, "Extra align qualifier");
42
43 try ctx.testCompileError(
44 \\comptime {
45 \\ var c: *volatile volatile i32 = 0;
46 \\}
47 , "1.zig", 2, 22, "Extra align qualifier");
48
49 try ctx.testCompileError(
50 \\comptime {
51 \\ var d: *allowzero allowzero i32 = 0;
52 \\}
53 , "1.zig", 2, 23, "Extra align qualifier");
4 // TODO: re-enable these tests.
5 // https://github.com/ziglang/zig/issues/1364
6
7 //try ctx.testCompileError(
8 // \\export fn entry() void {}
9 // \\export fn entry() void {}
10 //, "1.zig", 2, 8, "exported symbol collision: 'entry'");
11
12 //try ctx.testCompileError(
13 // \\fn() void {}
14 //, "1.zig", 1, 1, "missing function name");
15
16 //try ctx.testCompileError(
17 // \\comptime {
18 // \\ return;
19 // \\}
20 //, "1.zig", 2, 5, "return expression outside function definition");
21
22 //try ctx.testCompileError(
23 // \\export fn entry() void {
24 // \\ defer return;
25 // \\}
26 //, "1.zig", 2, 11, "cannot return from defer expression");
27
28 //try ctx.testCompileError(
29 // \\export fn entry() c_int {
30 // \\ return 36893488147419103232;
31 // \\}
32 //, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
33
34 //try ctx.testCompileError(
35 // \\comptime {
36 // \\ var a: *align(4) align(4) i32 = 0;
37 // \\}
38 //, "1.zig", 2, 22, "Extra align qualifier");
39
40 //try ctx.testCompileError(
41 // \\comptime {
42 // \\ var b: *const const i32 = 0;
43 // \\}
44 //, "1.zig", 2, 19, "Extra align qualifier");
45
46 //try ctx.testCompileError(
47 // \\comptime {
48 // \\ var c: *volatile volatile i32 = 0;
49 // \\}
50 //, "1.zig", 2, 22, "Extra align qualifier");
51
52 //try ctx.testCompileError(
53 // \\comptime {
54 // \\ var d: *allowzero allowzero i32 = 0;
55 // \\}
56 //, "1.zig", 2, 23, "Extra align qualifier");
5457}
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;
33pub fn addCases(ctx: *TestContext) !void {
44 try @import("compile_errors.zig").addCases(ctx);
55 try @import("compare_output.zig").addCases(ctx);
6 @import("zir.zig").addCases(ctx);
67}
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}