authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-14 18:27:51-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-14 18:27:51-04:00
log4d920cee6e8be2f2ae2cfd9067358c65b977568a
tree2c04de6151b7448dec9958d0a91234ea0ba9a15d
parentda3acacc14331a6be33445c3bfd204e2cccabddd
parent28c3d4809bc6d497ac81892bc7eb03b95d8c2b32

Merge remote-tracking branch 'origin/master' into llvm7


53 files changed, 4238 insertions(+), 1695 deletions(-)

CMakeLists.txt+4-2
...@@ -431,8 +431,8 @@ set(ZIG_CPP_SOURCES...@@ -431,8 +431,8 @@ set(ZIG_CPP_SOURCES
431set(ZIG_STD_FILES431set(ZIG_STD_FILES
432 "array_list.zig"432 "array_list.zig"
433 "atomic/index.zig"433 "atomic/index.zig"
434 "atomic/queue_mpmc.zig"434 "atomic/int.zig"
435 "atomic/queue_mpsc.zig"435 "atomic/queue.zig"
436 "atomic/stack.zig"436 "atomic/stack.zig"
437 "base64.zig"437 "base64.zig"
438 "buf_map.zig"438 "buf_map.zig"
...@@ -459,6 +459,8 @@ set(ZIG_STD_FILES...@@ -459,6 +459,8 @@ set(ZIG_STD_FILES
459 "empty.zig"459 "empty.zig"
460 "event.zig"460 "event.zig"
461 "event/channel.zig"461 "event/channel.zig"
462 "event/future.zig"
463 "event/group.zig"
462 "event/lock.zig"464 "event/lock.zig"
463 "event/locked.zig"465 "event/locked.zig"
464 "event/loop.zig"466 "event/loop.zig"
build.zig+105-72
...@@ -35,73 +35,31 @@ pub fn build(b: *Builder) !void {...@@ -35,73 +35,31 @@ pub fn build(b: *Builder) !void {
35 "BUILD_INFO",35 "BUILD_INFO",
36 });36 });
37 var index: usize = 0;37 var index: usize = 0;
38 const cmake_binary_dir = nextValue(&index, build_info);38 var ctx = Context{
39 const cxx_compiler = nextValue(&index, build_info);39 .cmake_binary_dir = nextValue(&index, build_info),
40 const llvm_config_exe = nextValue(&index, build_info);40 .cxx_compiler = nextValue(&index, build_info),
41 const lld_include_dir = nextValue(&index, build_info);41 .llvm_config_exe = nextValue(&index, build_info),
42 const lld_libraries = nextValue(&index, build_info);42 .lld_include_dir = nextValue(&index, build_info),
43 const std_files = nextValue(&index, build_info);43 .lld_libraries = nextValue(&index, build_info),
44 const c_header_files = nextValue(&index, build_info);44 .std_files = nextValue(&index, build_info),
45 const dia_guids_lib = nextValue(&index, build_info);45 .c_header_files = nextValue(&index, build_info),
46 .dia_guids_lib = nextValue(&index, build_info),
47 .llvm = undefined,
48 };
49 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
4650
47 const llvm = findLLVM(b, llvm_config_exe) catch unreachable;51 var test_stage2 = b.addTest("src-self-hosted/test.zig");
52 test_stage2.setBuildMode(builtin.Mode.Debug);
4853
49 var exe = b.addExecutable("zig", "src-self-hosted/main.zig");54 var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
50 exe.setBuildMode(mode);55 exe.setBuildMode(mode);
5156
52 // This is for finding /lib/libz.a on alpine linux.57 try configureStage2(b, test_stage2, ctx);
53 // TODO turn this into -Dextra-lib-path=/lib option58 try configureStage2(b, exe, ctx);
54 exe.addLibPath("/lib");
55
56 exe.addIncludeDir("src");
57 exe.addIncludeDir(cmake_binary_dir);
58 addCppLib(b, exe, cmake_binary_dir, "zig_cpp");
59 if (lld_include_dir.len != 0) {
60 exe.addIncludeDir(lld_include_dir);
61 var it = mem.split(lld_libraries, ";");
62 while (it.next()) |lib| {
63 exe.addObjectFile(lib);
64 }
65 } else {
66 addCppLib(b, exe, cmake_binary_dir, "embedded_lld_wasm");
67 addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf");
68 addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff");
69 addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib");
70 }
71 dependOnLib(exe, llvm);
72
73 if (exe.target.getOs() == builtin.Os.linux) {
74 const libstdcxx_path_padded = try b.exec([][]const u8{
75 cxx_compiler,
76 "-print-file-name=libstdc++.a",
77 });
78 const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?;
79 if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) {
80 warn(
81 \\Unable to determine path to libstdc++.a
82 \\On Fedora, install libstdc++-static and try again.
83 \\
84 );
85 return error.RequiredLibraryNotFound;
86 }
87 exe.addObjectFile(libstdcxx_path);
88
89 exe.linkSystemLibrary("pthread");
90 } else if (exe.target.isDarwin()) {
91 exe.linkSystemLibrary("c++");
92 }
93
94 if (dia_guids_lib.len != 0) {
95 exe.addObjectFile(dia_guids_lib);
96 }
97
98 if (exe.target.getOs() != builtin.Os.windows) {
99 exe.linkSystemLibrary("xml2");
100 }
101 exe.linkSystemLibrary("c");
10259
103 b.default_step.dependOn(&exe.step);60 b.default_step.dependOn(&exe.step);
10461
62 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
105 const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false;63 const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false;
106 if (!skip_self_hosted) {64 if (!skip_self_hosted) {
107 test_step.dependOn(&exe.step);65 test_step.dependOn(&exe.step);
...@@ -110,30 +68,40 @@ pub fn build(b: *Builder) !void {...@@ -110,30 +68,40 @@ pub fn build(b: *Builder) !void {
110 exe.setVerboseLink(verbose_link_exe);68 exe.setVerboseLink(verbose_link_exe);
11169
112 b.installArtifact(exe);70 b.installArtifact(exe);
113 installStdLib(b, std_files);71 installStdLib(b, ctx.std_files);
114 installCHeaders(b, c_header_files);72 installCHeaders(b, ctx.c_header_files);
11573
116 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");74 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
117 const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") orelse false;
11875
119 test_step.dependOn(docs_step);76 const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
77 test_stage2_step.dependOn(&test_stage2.step);
78 test_step.dependOn(test_stage2_step);
12079
121 test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", with_lldb));80 const all_modes = []builtin.Mode{
81 builtin.Mode.Debug,
82 builtin.Mode.ReleaseSafe,
83 builtin.Mode.ReleaseFast,
84 builtin.Mode.ReleaseSmall,
85 };
86 const modes = if (skip_release) []builtin.Mode{builtin.Mode.Debug} else all_modes;
12287
123 test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", with_lldb));88 test_step.dependOn(tests.addPkgTests(b, test_filter, "test/behavior.zig", "behavior", "Run the behavior tests", modes));
12489
125 test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", with_lldb));90 test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", modes));
12691
127 test_step.dependOn(tests.addCompareOutputTests(b, test_filter));92 test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes));
93
94 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
128 test_step.dependOn(tests.addBuildExampleTests(b, test_filter));95 test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
129 test_step.dependOn(tests.addCompileErrorTests(b, test_filter));96 test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
130 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));97 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
131 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter));98 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
132 test_step.dependOn(tests.addTranslateCTests(b, test_filter));99 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
133 test_step.dependOn(tests.addGenHTests(b, test_filter));100 test_step.dependOn(tests.addGenHTests(b, test_filter));
101 test_step.dependOn(docs_step);
134}102}
135103
136fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) void {104fn dependOnLib(lib_exe_obj: var, dep: *const LibraryDep) void {
137 for (dep.libdirs.toSliceConst()) |lib_dir| {105 for (dep.libdirs.toSliceConst()) |lib_dir| {
138 lib_exe_obj.addLibPath(lib_dir);106 lib_exe_obj.addLibPath(lib_dir);
139 }107 }
...@@ -148,7 +116,7 @@ fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) vo...@@ -148,7 +116,7 @@ fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) vo
148 }116 }
149}117}
150118
151fn addCppLib(b: *Builder, lib_exe_obj: *std.build.LibExeObjStep, cmake_binary_dir: []const u8, lib_name: []const u8) void {119fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void {
152 const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib";120 const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib";
153 lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable);121 lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable);
154}122}
...@@ -254,3 +222,68 @@ fn nextValue(index: *usize, build_info: []const u8) []const u8 {...@@ -254,3 +222,68 @@ fn nextValue(index: *usize, build_info: []const u8) []const u8 {
254 }222 }
255 }223 }
256}224}
225
226fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
227 // This is for finding /lib/libz.a on alpine linux.
228 // TODO turn this into -Dextra-lib-path=/lib option
229 exe.addLibPath("/lib");
230
231 exe.addIncludeDir("src");
232 exe.addIncludeDir(ctx.cmake_binary_dir);
233 addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp");
234 if (ctx.lld_include_dir.len != 0) {
235 exe.addIncludeDir(ctx.lld_include_dir);
236 var it = mem.split(ctx.lld_libraries, ";");
237 while (it.next()) |lib| {
238 exe.addObjectFile(lib);
239 }
240 } else {
241 addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_wasm");
242 addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_elf");
243 addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_coff");
244 addCppLib(b, exe, ctx.cmake_binary_dir, "embedded_lld_lib");
245 }
246 dependOnLib(exe, ctx.llvm);
247
248 if (exe.target.getOs() == builtin.Os.linux) {
249 const libstdcxx_path_padded = try b.exec([][]const u8{
250 ctx.cxx_compiler,
251 "-print-file-name=libstdc++.a",
252 });
253 const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?;
254 if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) {
255 warn(
256 \\Unable to determine path to libstdc++.a
257 \\On Fedora, install libstdc++-static and try again.
258 \\
259 );
260 return error.RequiredLibraryNotFound;
261 }
262 exe.addObjectFile(libstdcxx_path);
263
264 exe.linkSystemLibrary("pthread");
265 } else if (exe.target.isDarwin()) {
266 exe.linkSystemLibrary("c++");
267 }
268
269 if (ctx.dia_guids_lib.len != 0) {
270 exe.addObjectFile(ctx.dia_guids_lib);
271 }
272
273 if (exe.target.getOs() != builtin.Os.windows) {
274 exe.linkSystemLibrary("xml2");
275 }
276 exe.linkSystemLibrary("c");
277}
278
279const Context = struct {
280 cmake_binary_dir: []const u8,
281 cxx_compiler: []const u8,
282 llvm_config_exe: []const u8,
283 lld_include_dir: []const u8,
284 lld_libraries: []const u8,
285 std_files: []const u8,
286 c_header_files: []const u8,
287 dia_guids_lib: []const u8,
288 llvm: LibraryDep,
289};
doc/langref.html.in+11-5
...@@ -2239,7 +2239,7 @@ test "switch inside function" {...@@ -2239,7 +2239,7 @@ test "switch inside function" {
2239 // On an OS other than fuchsia, block is not even analyzed,2239 // On an OS other than fuchsia, block is not even analyzed,
2240 // so this compile error is not triggered.2240 // so this compile error is not triggered.
2241 // On fuchsia this compile error would be triggered.2241 // On fuchsia this compile error would be triggered.
2242 @compileError("windows not supported");2242 @compileError("fuchsia not supported");
2243 },2243 },
2244 else => {},2244 else => {},
2245 }2245 }
...@@ -2303,13 +2303,13 @@ test "while continue" {...@@ -2303,13 +2303,13 @@ test "while continue" {
2303 {#code_begin|test|while#}2303 {#code_begin|test|while#}
2304const assert = @import("std").debug.assert;2304const assert = @import("std").debug.assert;
23052305
2306test "while loop continuation expression" {2306test "while loop continue expression" {
2307 var i: usize = 0;2307 var i: usize = 0;
2308 while (i < 10) : (i += 1) {}2308 while (i < 10) : (i += 1) {}
2309 assert(i == 10);2309 assert(i == 10);
2310}2310}
23112311
2312test "while loop continuation expression, more complicated" {2312test "while loop continue expression, more complicated" {
2313 var i1: usize = 1;2313 var i1: usize = 1;
2314 var j1: usize = 1;2314 var j1: usize = 1;
2315 while (i1 * j1 < 2000) : ({ i1 *= 2; j1 *= 3; }) {2315 while (i1 * j1 < 2000) : ({ i1 *= 2; j1 *= 3; }) {
...@@ -7118,10 +7118,16 @@ Environments:...@@ -7118,10 +7118,16 @@ Environments:
7118 opencl</code></pre>7118 opencl</code></pre>
7119 <p>7119 <p>
7120 The Zig Standard Library (<code>@import("std")</code>) has architecture, environment, and operating sytsem7120 The Zig Standard Library (<code>@import("std")</code>) has architecture, environment, and operating sytsem
7121 abstractions, and thus takes additional work to support more platforms. It currently supports7121 abstractions, and thus takes additional work to support more platforms.
7122 Linux x86_64. Not all standard library code requires operating system abstractions, however,7122 Not all standard library code requires operating system abstractions, however,
7123 so things such as generic data structures work an all above platforms.7123 so things such as generic data structures work an all above platforms.
7124 </p>7124 </p>
7125 <p>The current list of targets supported by the Zig Standard Library is:</p>
7126 <ul>
7127 <li>Linux x86_64</li>
7128 <li>Windows x86_64</li>
7129 <li>MacOS x86_64</li>
7130 </ul>
7125 {#header_close#}7131 {#header_close#}
7126 {#header_open|Style Guide#}7132 {#header_open|Style Guide#}
7127 <p>7133 <p>
src-self-hosted/codegen.zig created+59
...@@ -0,0 +1,59 @@
1const std = @import("std");
2const Compilation = @import("compilation.zig").Compilation;
3// we go through llvm instead of c for 2 reasons:
4// 1. to avoid accidentally calling the non-thread-safe functions
5// 2. patch up some of the types to remove nullability
6const llvm = @import("llvm.zig");
7const ir = @import("ir.zig");
8const Value = @import("value.zig").Value;
9const Type = @import("type.zig").Type;
10const event = std.event;
11
12pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void {
13 fn_val.base.ref();
14 defer fn_val.base.deref(comp);
15 defer code.destroy(comp.a());
16
17 const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();
18 defer llvm_handle.release(comp.event_loop_local);
19
20 const context = llvm_handle.node.data;
21
22 const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory;
23 defer llvm.DisposeModule(module);
24
25 const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory;
26 defer llvm.DisposeBuilder(builder);
27
28 var ofile = ObjectFile{
29 .comp = comp,
30 .module = module,
31 .builder = builder,
32 .context = context,
33 .lock = event.Lock.init(comp.loop),
34 };
35
36 try renderToLlvmModule(&ofile, fn_val, code);
37
38 if (comp.verbose_llvm_ir) {
39 llvm.DumpModule(ofile.module);
40 }
41}
42
43pub const ObjectFile = struct {
44 comp: *Compilation,
45 module: llvm.ModuleRef,
46 builder: llvm.BuilderRef,
47 context: llvm.ContextRef,
48 lock: event.Lock,
49
50 fn a(self: *ObjectFile) *std.mem.Allocator {
51 return self.comp.a();
52 }
53};
54
55pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void {
56 // TODO audit more of codegen.cpp:fn_llvm_value and port more logic
57 const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile);
58 const llvm_fn = llvm.AddFunction(ofile.module, fn_val.symbol_name.ptr(), llvm_fn_type);
59}
src-self-hosted/compilation.zig created+747
...@@ -0,0 +1,747 @@
1const std = @import("std");
2const os = std.os;
3const io = std.io;
4const mem = std.mem;
5const Allocator = mem.Allocator;
6const Buffer = std.Buffer;
7const llvm = @import("llvm.zig");
8const c = @import("c.zig");
9const builtin = @import("builtin");
10const Target = @import("target.zig").Target;
11const warn = std.debug.warn;
12const Token = std.zig.Token;
13const ArrayList = std.ArrayList;
14const errmsg = @import("errmsg.zig");
15const ast = std.zig.ast;
16const event = std.event;
17const assert = std.debug.assert;
18const AtomicRmwOp = builtin.AtomicRmwOp;
19const AtomicOrder = builtin.AtomicOrder;
20const Scope = @import("scope.zig").Scope;
21const Decl = @import("decl.zig").Decl;
22const ir = @import("ir.zig");
23const Visib = @import("visib.zig").Visib;
24const ParsedFile = @import("parsed_file.zig").ParsedFile;
25const Value = @import("value.zig").Value;
26const Type = Value.Type;
27const Span = errmsg.Span;
28const codegen = @import("codegen.zig");
29
30/// Data that is local to the event loop.
31pub const EventLoopLocal = struct {
32 loop: *event.Loop,
33 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
34
35 fn init(loop: *event.Loop) EventLoopLocal {
36 return EventLoopLocal{
37 .loop = loop,
38 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
39 };
40 }
41
42 fn deinit(self: *EventLoopLocal) void {
43 while (self.llvm_handle_pool.pop()) |node| {
44 c.LLVMContextDispose(node.data);
45 self.loop.allocator.destroy(node);
46 }
47 }
48
49 /// Gets an exclusive handle on any LlvmContext.
50 /// Caller must release the handle when done.
51 pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle {
52 if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
53
54 const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;
55 errdefer c.LLVMContextDispose(context_ref);
56
57 const node = try self.loop.allocator.create(std.atomic.Stack(llvm.ContextRef).Node{
58 .next = undefined,
59 .data = context_ref,
60 });
61 errdefer self.loop.allocator.destroy(node);
62
63 return LlvmHandle{ .node = node };
64 }
65};
66
67pub const LlvmHandle = struct {
68 node: *std.atomic.Stack(llvm.ContextRef).Node,
69
70 pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void {
71 event_loop_local.llvm_handle_pool.push(self.node);
72 }
73};
74
75pub const Compilation = struct {
76 event_loop_local: *EventLoopLocal,
77 loop: *event.Loop,
78 name: Buffer,
79 root_src_path: ?[]const u8,
80 target: Target,
81 build_mode: builtin.Mode,
82 zig_lib_dir: []const u8,
83
84 version_major: u32,
85 version_minor: u32,
86 version_patch: u32,
87
88 linker_script: ?[]const u8,
89 cache_dir: []const u8,
90 libc_lib_dir: ?[]const u8,
91 libc_static_lib_dir: ?[]const u8,
92 libc_include_dir: ?[]const u8,
93 msvc_lib_dir: ?[]const u8,
94 kernel32_lib_dir: ?[]const u8,
95 dynamic_linker: ?[]const u8,
96 out_h_path: ?[]const u8,
97
98 is_test: bool,
99 each_lib_rpath: bool,
100 strip: bool,
101 is_static: bool,
102 linker_rdynamic: bool,
103
104 clang_argv: []const []const u8,
105 llvm_argv: []const []const u8,
106 lib_dirs: []const []const u8,
107 rpath_list: []const []const u8,
108 assembly_files: []const []const u8,
109 link_objects: []const []const u8,
110
111 windows_subsystem_windows: bool,
112 windows_subsystem_console: bool,
113
114 link_libs_list: ArrayList(*LinkLib),
115 libc_link_lib: ?*LinkLib,
116
117 err_color: errmsg.Color,
118
119 verbose_tokenize: bool,
120 verbose_ast_tree: bool,
121 verbose_ast_fmt: bool,
122 verbose_cimport: bool,
123 verbose_ir: bool,
124 verbose_llvm_ir: bool,
125 verbose_link: bool,
126
127 darwin_frameworks: []const []const u8,
128 darwin_version_min: DarwinVersionMin,
129
130 test_filters: []const []const u8,
131 test_name_prefix: ?[]const u8,
132
133 emit_file_type: Emit,
134
135 kind: Kind,
136
137 link_out_file: ?[]const u8,
138 events: *event.Channel(Event),
139
140 exported_symbol_names: event.Locked(Decl.Table),
141
142 /// Before code generation starts, must wait on this group to make sure
143 /// the build is complete.
144 build_group: event.Group(BuildError!void),
145
146 compile_errors: event.Locked(CompileErrList),
147
148 meta_type: *Type.MetaType,
149 void_type: *Type.Void,
150 bool_type: *Type.Bool,
151 noreturn_type: *Type.NoReturn,
152
153 void_value: *Value.Void,
154 true_value: *Value.Bool,
155 false_value: *Value.Bool,
156 noreturn_value: *Value.NoReturn,
157
158 const CompileErrList = std.ArrayList(*errmsg.Msg);
159
160 // TODO handle some of these earlier and report them in a way other than error codes
161 pub const BuildError = error{
162 OutOfMemory,
163 EndOfStream,
164 BadFd,
165 Io,
166 IsDir,
167 Unexpected,
168 SystemResources,
169 SharingViolation,
170 PathAlreadyExists,
171 FileNotFound,
172 AccessDenied,
173 PipeBusy,
174 FileTooBig,
175 SymLinkLoop,
176 ProcessFdQuotaExceeded,
177 NameTooLong,
178 SystemFdQuotaExceeded,
179 NoDevice,
180 PathNotFound,
181 NoSpaceLeft,
182 NotDir,
183 FileSystem,
184 OperationAborted,
185 IoPending,
186 BrokenPipe,
187 WouldBlock,
188 FileClosed,
189 DestinationAddressRequired,
190 DiskQuota,
191 InputOutput,
192 NoStdHandles,
193 Overflow,
194 NotSupported,
195 BufferTooSmall,
196 Unimplemented, // TODO remove this one
197 SemanticAnalysisFailed, // TODO remove this one
198 };
199
200 pub const Event = union(enum) {
201 Ok,
202 Error: BuildError,
203 Fail: []*errmsg.Msg,
204 };
205
206 pub const DarwinVersionMin = union(enum) {
207 None,
208 MacOS: []const u8,
209 Ios: []const u8,
210 };
211
212 pub const Kind = enum {
213 Exe,
214 Lib,
215 Obj,
216 };
217
218 pub const LinkLib = struct {
219 name: []const u8,
220 path: ?[]const u8,
221
222 /// the list of symbols we depend on from this lib
223 symbols: ArrayList([]u8),
224 provided_explicitly: bool,
225 };
226
227 pub const Emit = enum {
228 Binary,
229 Assembly,
230 LlvmIr,
231 };
232
233 pub fn create(
234 event_loop_local: *EventLoopLocal,
235 name: []const u8,
236 root_src_path: ?[]const u8,
237 target: *const Target,
238 kind: Kind,
239 build_mode: builtin.Mode,
240 zig_lib_dir: []const u8,
241 cache_dir: []const u8,
242 ) !*Compilation {
243 const loop = event_loop_local.loop;
244
245 var name_buffer = try Buffer.init(loop.allocator, name);
246 errdefer name_buffer.deinit();
247
248 const events = try event.Channel(Event).create(loop, 0);
249 errdefer events.destroy();
250
251 const comp = try loop.allocator.create(Compilation{
252 .loop = loop,
253 .event_loop_local = event_loop_local,
254 .events = events,
255 .name = name_buffer,
256 .root_src_path = root_src_path,
257 .target = target.*,
258 .kind = kind,
259 .build_mode = build_mode,
260 .zig_lib_dir = zig_lib_dir,
261 .cache_dir = cache_dir,
262
263 .version_major = 0,
264 .version_minor = 0,
265 .version_patch = 0,
266
267 .verbose_tokenize = false,
268 .verbose_ast_tree = false,
269 .verbose_ast_fmt = false,
270 .verbose_cimport = false,
271 .verbose_ir = false,
272 .verbose_llvm_ir = false,
273 .verbose_link = false,
274
275 .linker_script = null,
276 .libc_lib_dir = null,
277 .libc_static_lib_dir = null,
278 .libc_include_dir = null,
279 .msvc_lib_dir = null,
280 .kernel32_lib_dir = null,
281 .dynamic_linker = null,
282 .out_h_path = null,
283 .is_test = false,
284 .each_lib_rpath = false,
285 .strip = false,
286 .is_static = false,
287 .linker_rdynamic = false,
288 .clang_argv = [][]const u8{},
289 .llvm_argv = [][]const u8{},
290 .lib_dirs = [][]const u8{},
291 .rpath_list = [][]const u8{},
292 .assembly_files = [][]const u8{},
293 .link_objects = [][]const u8{},
294 .windows_subsystem_windows = false,
295 .windows_subsystem_console = false,
296 .link_libs_list = ArrayList(*LinkLib).init(loop.allocator),
297 .libc_link_lib = null,
298 .err_color = errmsg.Color.Auto,
299 .darwin_frameworks = [][]const u8{},
300 .darwin_version_min = DarwinVersionMin.None,
301 .test_filters = [][]const u8{},
302 .test_name_prefix = null,
303 .emit_file_type = Emit.Binary,
304 .link_out_file = null,
305 .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
306 .build_group = event.Group(BuildError!void).init(loop),
307 .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
308
309 .meta_type = undefined,
310 .void_type = undefined,
311 .void_value = undefined,
312 .bool_type = undefined,
313 .true_value = undefined,
314 .false_value = undefined,
315 .noreturn_type = undefined,
316 .noreturn_value = undefined,
317 });
318 try comp.initTypes();
319 return comp;
320 }
321
322 fn initTypes(comp: *Compilation) !void {
323 comp.meta_type = try comp.a().create(Type.MetaType{
324 .base = Type{
325 .base = Value{
326 .id = Value.Id.Type,
327 .typeof = undefined,
328 .ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice
329 },
330 .id = builtin.TypeId.Type,
331 },
332 .value = undefined,
333 });
334 comp.meta_type.value = &comp.meta_type.base;
335 comp.meta_type.base.base.typeof = &comp.meta_type.base;
336 errdefer comp.a().destroy(comp.meta_type);
337
338 comp.void_type = try comp.a().create(Type.Void{
339 .base = Type{
340 .base = Value{
341 .id = Value.Id.Type,
342 .typeof = &Type.MetaType.get(comp).base,
343 .ref_count = std.atomic.Int(usize).init(1),
344 },
345 .id = builtin.TypeId.Void,
346 },
347 });
348 errdefer comp.a().destroy(comp.void_type);
349
350 comp.noreturn_type = try comp.a().create(Type.NoReturn{
351 .base = Type{
352 .base = Value{
353 .id = Value.Id.Type,
354 .typeof = &Type.MetaType.get(comp).base,
355 .ref_count = std.atomic.Int(usize).init(1),
356 },
357 .id = builtin.TypeId.NoReturn,
358 },
359 });
360 errdefer comp.a().destroy(comp.noreturn_type);
361
362 comp.bool_type = try comp.a().create(Type.Bool{
363 .base = Type{
364 .base = Value{
365 .id = Value.Id.Type,
366 .typeof = &Type.MetaType.get(comp).base,
367 .ref_count = std.atomic.Int(usize).init(1),
368 },
369 .id = builtin.TypeId.Bool,
370 },
371 });
372 errdefer comp.a().destroy(comp.bool_type);
373
374 comp.void_value = try comp.a().create(Value.Void{
375 .base = Value{
376 .id = Value.Id.Void,
377 .typeof = &Type.Void.get(comp).base,
378 .ref_count = std.atomic.Int(usize).init(1),
379 },
380 });
381 errdefer comp.a().destroy(comp.void_value);
382
383 comp.true_value = try comp.a().create(Value.Bool{
384 .base = Value{
385 .id = Value.Id.Bool,
386 .typeof = &Type.Bool.get(comp).base,
387 .ref_count = std.atomic.Int(usize).init(1),
388 },
389 .x = true,
390 });
391 errdefer comp.a().destroy(comp.true_value);
392
393 comp.false_value = try comp.a().create(Value.Bool{
394 .base = Value{
395 .id = Value.Id.Bool,
396 .typeof = &Type.Bool.get(comp).base,
397 .ref_count = std.atomic.Int(usize).init(1),
398 },
399 .x = false,
400 });
401 errdefer comp.a().destroy(comp.false_value);
402
403 comp.noreturn_value = try comp.a().create(Value.NoReturn{
404 .base = Value{
405 .id = Value.Id.NoReturn,
406 .typeof = &Type.NoReturn.get(comp).base,
407 .ref_count = std.atomic.Int(usize).init(1),
408 },
409 });
410 errdefer comp.a().destroy(comp.noreturn_value);
411 }
412
413 pub fn destroy(self: *Compilation) void {
414 self.noreturn_value.base.deref(self);
415 self.void_value.base.deref(self);
416 self.false_value.base.deref(self);
417 self.true_value.base.deref(self);
418 self.noreturn_type.base.base.deref(self);
419 self.void_type.base.base.deref(self);
420 self.meta_type.base.base.deref(self);
421
422 self.events.destroy();
423 self.name.deinit();
424
425 self.a().destroy(self);
426 }
427
428 pub fn build(self: *Compilation) !void {
429 if (self.llvm_argv.len != 0) {
430 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{
431 [][]const u8{"zig (LLVM option parsing)"},
432 self.llvm_argv,
433 });
434 defer c_compatible_args.deinit();
435 // TODO this sets global state
436 c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
437 }
438
439 _ = try async<self.a()> self.buildAsync();
440 }
441
442 async fn buildAsync(self: *Compilation) void {
443 while (true) {
444 // TODO directly awaiting async should guarantee memory allocation elision
445 // TODO also async before suspending should guarantee memory allocation elision
446 const build_result = await (async self.addRootSrc() catch unreachable);
447
448 // this makes a handy error return trace and stack trace in debug mode
449 if (std.debug.runtime_safety) {
450 build_result catch unreachable;
451 }
452
453 const compile_errors = blk: {
454 const held = await (async self.compile_errors.acquire() catch unreachable);
455 defer held.release();
456 break :blk held.value.toOwnedSlice();
457 };
458
459 if (build_result) |_| {
460 if (compile_errors.len == 0) {
461 await (async self.events.put(Event.Ok) catch unreachable);
462 } else {
463 await (async self.events.put(Event{ .Fail = compile_errors }) catch unreachable);
464 }
465 } else |err| {
466 // if there's an error then the compile errors have dangling references
467 self.a().free(compile_errors);
468
469 await (async self.events.put(Event{ .Error = err }) catch unreachable);
470 }
471
472 // for now we stop after 1
473 return;
474 }
475 }
476
477 async fn addRootSrc(self: *Compilation) !void {
478 const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
479 // TODO async/await os.path.real
480 const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| {
481 try printError("unable to get real path '{}': {}", root_src_path, err);
482 return err;
483 };
484 errdefer self.a().free(root_src_real_path);
485
486 // TODO async/await readFileAlloc()
487 const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| {
488 try printError("unable to open '{}': {}", root_src_real_path, err);
489 return err;
490 };
491 errdefer self.a().free(source_code);
492
493 const parsed_file = try self.a().create(ParsedFile{
494 .tree = undefined,
495 .realpath = root_src_real_path,
496 });
497 errdefer self.a().destroy(parsed_file);
498
499 parsed_file.tree = try std.zig.parse(self.a(), source_code);
500 errdefer parsed_file.tree.deinit();
501
502 const tree = &parsed_file.tree;
503
504 // create empty struct for it
505 const decls = try Scope.Decls.create(self, null);
506 defer decls.base.deref(self);
507
508 var decl_group = event.Group(BuildError!void).init(self.loop);
509 errdefer decl_group.cancelAll();
510
511 var it = tree.root_node.decls.iterator(0);
512 while (it.next()) |decl_ptr| {
513 const decl = decl_ptr.*;
514 switch (decl.id) {
515 ast.Node.Id.Comptime => @panic("TODO"),
516 ast.Node.Id.VarDecl => @panic("TODO"),
517 ast.Node.Id.FnProto => {
518 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
519
520 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
521 try self.addCompileError(parsed_file, Span{
522 .first = fn_proto.fn_token,
523 .last = fn_proto.fn_token + 1,
524 }, "missing function name");
525 continue;
526 };
527
528 const fn_decl = try self.a().create(Decl.Fn{
529 .base = Decl{
530 .id = Decl.Id.Fn,
531 .name = name,
532 .visib = parseVisibToken(tree, fn_proto.visib_token),
533 .resolution = event.Future(BuildError!void).init(self.loop),
534 .resolution_in_progress = 0,
535 .parsed_file = parsed_file,
536 .parent_scope = &decls.base,
537 },
538 .value = Decl.Fn.Val{ .Unresolved = {} },
539 .fn_proto = fn_proto,
540 });
541 errdefer self.a().destroy(fn_decl);
542
543 try decl_group.call(addTopLevelDecl, self, &fn_decl.base);
544 },
545 ast.Node.Id.TestDecl => @panic("TODO"),
546 else => unreachable,
547 }
548 }
549 try await (async decl_group.wait() catch unreachable);
550 try await (async self.build_group.wait() catch unreachable);
551 }
552
553 async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void {
554 const is_export = decl.isExported(&decl.parsed_file.tree);
555
556 if (is_export) {
557 try self.build_group.call(verifyUniqueSymbol, self, decl);
558 try self.build_group.call(resolveDecl, self, decl);
559 }
560 }
561
562 fn addCompileError(self: *Compilation, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void {
563 const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args);
564 errdefer self.loop.allocator.free(text);
565
566 try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text);
567 }
568
569 async fn addCompileErrorAsync(
570 self: *Compilation,
571 parsed_file: *ParsedFile,
572 span: Span,
573 text: []u8,
574 ) !void {
575 const msg = try self.loop.allocator.create(errmsg.Msg{
576 .path = parsed_file.realpath,
577 .text = text,
578 .span = span,
579 .tree = &parsed_file.tree,
580 });
581 errdefer self.loop.allocator.destroy(msg);
582
583 const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
584 defer compile_errors.release();
585
586 try compile_errors.value.append(msg);
587 }
588
589 async fn verifyUniqueSymbol(self: *Compilation, decl: *Decl) !void {
590 const exported_symbol_names = await (async self.exported_symbol_names.acquire() catch unreachable);
591 defer exported_symbol_names.release();
592
593 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
594 try self.addCompileError(
595 decl.parsed_file,
596 decl.getSpan(),
597 "exported symbol collision: '{}'",
598 decl.name,
599 );
600 // TODO add error note showing location of other symbol
601 }
602 }
603
604 pub fn link(self: *Compilation, out_file: ?[]const u8) !void {
605 warn("TODO link");
606 return error.Todo;
607 }
608
609 pub fn addLinkLib(self: *Compilation, name: []const u8, provided_explicitly: bool) !*LinkLib {
610 const is_libc = mem.eql(u8, name, "c");
611
612 if (is_libc) {
613 if (self.libc_link_lib) |libc_link_lib| {
614 return libc_link_lib;
615 }
616 }
617
618 for (self.link_libs_list.toSliceConst()) |existing_lib| {
619 if (mem.eql(u8, name, existing_lib.name)) {
620 return existing_lib;
621 }
622 }
623
624 const link_lib = try self.a().create(LinkLib{
625 .name = name,
626 .path = null,
627 .provided_explicitly = provided_explicitly,
628 .symbols = ArrayList([]u8).init(self.a()),
629 });
630 try self.link_libs_list.append(link_lib);
631 if (is_libc) {
632 self.libc_link_lib = link_lib;
633 }
634 return link_lib;
635 }
636
637 fn a(self: Compilation) *mem.Allocator {
638 return self.loop.allocator;
639 }
640};
641
642fn printError(comptime format: []const u8, args: ...) !void {
643 var stderr_file = try std.io.getStdErr();
644 var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
645 const out_stream = &stderr_file_out_stream.stream;
646 try out_stream.print(format, args);
647}
648
649fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
650 if (optional_token_index) |token_index| {
651 const token = tree.tokens.at(token_index);
652 assert(token.id == Token.Id.Keyword_pub);
653 return Visib.Pub;
654 } else {
655 return Visib.Private;
656 }
657}
658
659/// This declaration has been blessed as going into the final code generation.
660pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
661 if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
662 decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable);
663 decl.resolution.resolve();
664 return decl.resolution.data;
665 } else {
666 return (await (async decl.resolution.get() catch unreachable)).*;
667 }
668}
669
670/// The function that actually does the generation.
671async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
672 switch (decl.id) {
673 Decl.Id.Var => @panic("TODO"),
674 Decl.Id.Fn => {
675 const fn_decl = @fieldParentPtr(Decl.Fn, "base", decl);
676 return await (async generateDeclFn(comp, fn_decl) catch unreachable);
677 },
678 Decl.Id.CompTime => @panic("TODO"),
679 }
680}
681
682async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
683 const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl");
684
685 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
686 defer fndef_scope.base.deref(comp);
687
688 // TODO actually look at the return type of the AST
689 const return_type = &Type.Void.get(comp).base;
690 defer return_type.base.deref(comp);
691
692 const is_var_args = false;
693 const params = ([*]Type.Fn.Param)(undefined)[0..0];
694 const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args);
695 defer fn_type.base.base.deref(comp);
696
697 var symbol_name = try std.Buffer.init(comp.a(), fn_decl.base.name);
698 errdefer symbol_name.deinit();
699
700 const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
701 defer fn_val.base.deref(comp);
702
703 fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
704
705 const unanalyzed_code = (await (async ir.gen(
706 comp,
707 body_node,
708 &fndef_scope.base,
709 Span.token(body_node.lastToken()),
710 fn_decl.base.parsed_file,
711 ) catch unreachable)) catch |err| switch (err) {
712 // This poison value should not cause the errdefers to run. It simply means
713 // that self.compile_errors is populated.
714 // TODO https://github.com/ziglang/zig/issues/769
715 error.SemanticAnalysisFailed => return {},
716 else => return err,
717 };
718 defer unanalyzed_code.destroy(comp.a());
719
720 if (comp.verbose_ir) {
721 std.debug.warn("unanalyzed:\n");
722 unanalyzed_code.dump();
723 }
724
725 const analyzed_code = (await (async ir.analyze(
726 comp,
727 fn_decl.base.parsed_file,
728 unanalyzed_code,
729 null,
730 ) catch unreachable)) catch |err| switch (err) {
731 // This poison value should not cause the errdefers to run. It simply means
732 // that self.compile_errors is populated.
733 // TODO https://github.com/ziglang/zig/issues/769
734 error.SemanticAnalysisFailed => return {},
735 else => return err,
736 };
737 errdefer analyzed_code.destroy(comp.a());
738
739 if (comp.verbose_ir) {
740 std.debug.warn("analyzed:\n");
741 analyzed_code.dump();
742 }
743
744 // Kick off rendering to LLVM comp, but it doesn't block the fn decl
745 // analysis from being complete.
746 try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);
747}
src-self-hosted/decl.zig created+96
...@@ -0,0 +1,96 @@
1const std = @import("std");
2const Allocator = mem.Allocator;
3const mem = std.mem;
4const ast = std.zig.ast;
5const Visib = @import("visib.zig").Visib;
6const ParsedFile = @import("parsed_file.zig").ParsedFile;
7const event = std.event;
8const Value = @import("value.zig").Value;
9const Token = std.zig.Token;
10const errmsg = @import("errmsg.zig");
11const Scope = @import("scope.zig").Scope;
12const Compilation = @import("compilation.zig").Compilation;
13
14pub const Decl = struct {
15 id: Id,
16 name: []const u8,
17 visib: Visib,
18 resolution: event.Future(Compilation.BuildError!void),
19 resolution_in_progress: u8,
20 parsed_file: *ParsedFile,
21 parent_scope: *Scope,
22
23 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
24
25 pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
26 switch (base.id) {
27 Id.Fn => {
28 const fn_decl = @fieldParentPtr(Fn, "base", base);
29 return fn_decl.isExported(tree);
30 },
31 else => return false,
32 }
33 }
34
35 pub fn getSpan(base: *const Decl) errmsg.Span {
36 switch (base.id) {
37 Id.Fn => {
38 const fn_decl = @fieldParentPtr(Fn, "base", base);
39 const fn_proto = fn_decl.fn_proto;
40 const start = fn_proto.fn_token;
41 const end = fn_proto.name_token orelse start;
42 return errmsg.Span{
43 .first = start,
44 .last = end + 1,
45 };
46 },
47 else => @panic("TODO"),
48 }
49 }
50
51 pub const Id = enum {
52 Var,
53 Fn,
54 CompTime,
55 };
56
57 pub const Var = struct {
58 base: Decl,
59 };
60
61 pub const Fn = struct {
62 base: Decl,
63 value: Val,
64 fn_proto: *const ast.Node.FnProto,
65
66 // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
67 pub const Val = union {
68 Unresolved: void,
69 Ok: *Value.Fn,
70 };
71
72 pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
73 return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {
74 const token = tree.tokens.at(tok_index);
75 break :x switch (token.id) {
76 Token.Id.Extern => tree.tokenSlicePtr(token),
77 else => null,
78 };
79 } else null;
80 }
81
82 pub fn isExported(self: Fn, tree: *ast.Tree) bool {
83 if (self.fn_proto.extern_export_inline_token) |tok_index| {
84 const token = tree.tokens.at(tok_index);
85 return token.id == Token.Id.Keyword_export;
86 } else {
87 return false;
88 }
89 }
90 };
91
92 pub const CompTime = struct {
93 base: Decl,
94 };
95};
96
src-self-hosted/errmsg.zig+19-6
...@@ -11,11 +11,22 @@ pub const Color = enum {...@@ -11,11 +11,22 @@ pub const Color = enum {
11 On,11 On,
12};12};
1313
14pub const Span = struct {
15 first: ast.TokenIndex,
16 last: ast.TokenIndex,
17
18 pub fn token(i: TokenIndex) Span {
19 return Span {
20 .first = i,
21 .last = i,
22 };
23 }
24};
25
14pub const Msg = struct {26pub const Msg = struct {
15 path: []const u8,27 path: []const u8,
16 text: []u8,28 text: []u8,
17 first_token: TokenIndex,29 span: Span,
18 last_token: TokenIndex,
19 tree: *ast.Tree,30 tree: *ast.Tree,
20};31};
2132
...@@ -39,8 +50,10 @@ pub fn createFromParseError(...@@ -39,8 +50,10 @@ pub fn createFromParseError(
39 .tree = tree,50 .tree = tree,
40 .path = path,51 .path = path,
41 .text = text_buf.toOwnedSlice(),52 .text = text_buf.toOwnedSlice(),
42 .first_token = loc_token,53 .span = Span{
43 .last_token = loc_token,54 .first = loc_token,
55 .last = loc_token,
56 },
44 });57 });
45 errdefer allocator.destroy(msg);58 errdefer allocator.destroy(msg);
4659
...@@ -48,8 +61,8 @@ pub fn createFromParseError(...@@ -48,8 +61,8 @@ pub fn createFromParseError(
48}61}
4962
50pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void {63pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void {
51 const first_token = msg.tree.tokens.at(msg.first_token);64 const first_token = msg.tree.tokens.at(msg.span.first);
52 const last_token = msg.tree.tokens.at(msg.last_token);65 const last_token = msg.tree.tokens.at(msg.span.last);
53 const start_loc = msg.tree.tokenLocationPtr(0, first_token);66 const start_loc = msg.tree.tokenLocationPtr(0, first_token);
54 const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token);67 const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token);
55 if (!color_on) {68 if (!color_on) {
src-self-hosted/introspect.zig+5
...@@ -53,3 +53,8 @@ pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 {...@@ -53,3 +53,8 @@ pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 {
53 return error.ZigLibDirNotFound;53 return error.ZigLibDirNotFound;
54 };54 };
55}55}
56
57/// Caller must free result
58pub fn resolveZigCacheDir(allocator: *mem.Allocator) ![]u8 {
59 return std.mem.dupe(allocator, u8, "zig-cache");
60}
src-self-hosted/ir.zig+1023-100
...@@ -1,111 +1,1034 @@...@@ -1,111 +1,1034 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Compilation = @import("compilation.zig").Compilation;
1const Scope = @import("scope.zig").Scope;4const Scope = @import("scope.zig").Scope;
5const ast = std.zig.ast;
6const Allocator = std.mem.Allocator;
7const Value = @import("value.zig").Value;
8const Type = Value.Type;
9const assert = std.debug.assert;
10const Token = std.zig.Token;
11const ParsedFile = @import("parsed_file.zig").ParsedFile;
12const Span = @import("errmsg.zig").Span;
13
14pub const LVal = enum {
15 None,
16 Ptr,
17};
18
19pub const IrVal = union(enum) {
20 Unknown,
21 KnownType: *Type,
22 KnownValue: *Value,
23
24 const Init = enum {
25 Unknown,
26 NoReturn,
27 Void,
28 };
29
30 pub fn dump(self: IrVal) void {
31 switch (self) {
32 IrVal.Unknown => typeof.dump(),
33 IrVal.KnownType => |typeof| {
34 std.debug.warn("KnownType(");
35 typeof.dump();
36 std.debug.warn(")");
37 },
38 IrVal.KnownValue => |value| {
39 std.debug.warn("KnownValue(");
40 value.dump();
41 std.debug.warn(")");
42 },
43 }
44 }
45};
246
3pub const Instruction = struct {47pub const Instruction = struct {
4 id: Id,48 id: Id,
5 scope: *Scope,49 scope: *Scope,
50 debug_id: usize,
51 val: IrVal,
52 ref_count: usize,
53 span: Span,
54
55 /// true if this instruction was generated by zig and not from user code
56 is_generated: bool,
57
58 /// the instruction that is derived from this one in analysis
59 child: ?*Instruction,
60
61 /// the instruction that this one derives from in analysis
62 parent: ?*Instruction,
63
64 pub fn cast(base: *Instruction, comptime T: type) ?*T {
65 if (base.id == comptime typeToId(T)) {
66 return @fieldParentPtr(T, "base", base);
67 }
68 return null;
69 }
70
71 pub fn typeToId(comptime T: type) Id {
72 comptime var i = 0;
73 inline while (i < @memberCount(Id)) : (i += 1) {
74 if (T == @field(Instruction, @memberName(Id, i))) {
75 return @field(Id, @memberName(Id, i));
76 }
77 }
78 unreachable;
79 }
80
81 pub fn dump(base: *const Instruction) void {
82 comptime var i = 0;
83 inline while (i < @memberCount(Id)) : (i += 1) {
84 if (base.id == @field(Id, @memberName(Id, i))) {
85 const T = @field(Instruction, @memberName(Id, i));
86 std.debug.warn("#{} = {}(", base.debug_id, @tagName(base.id));
87 @fieldParentPtr(T, "base", base).dump();
88 std.debug.warn(")");
89 return;
90 }
91 }
92 unreachable;
93 }
94
95 pub fn hasSideEffects(base: *const Instruction) bool {
96 comptime var i = 0;
97 inline while (i < @memberCount(Id)) : (i += 1) {
98 if (base.id == @field(Id, @memberName(Id, i))) {
99 const T = @field(Instruction, @memberName(Id, i));
100 return @fieldParentPtr(T, "base", base).hasSideEffects();
101 }
102 }
103 unreachable;
104 }
105
106 pub fn analyze(base: *Instruction, ira: *Analyze) Analyze.Error!*Instruction {
107 comptime var i = 0;
108 inline while (i < @memberCount(Id)) : (i += 1) {
109 if (base.id == @field(Id, @memberName(Id, i))) {
110 const T = @field(Instruction, @memberName(Id, i));
111 const new_inst = try @fieldParentPtr(T, "base", base).analyze(ira);
112 new_inst.linkToParent(base);
113 return new_inst;
114 }
115 }
116 unreachable;
117 }
118
119 fn getAsParam(param: *Instruction) !*Instruction {
120 const child = param.child orelse return error.SemanticAnalysisFailed;
121 switch (child.val) {
122 IrVal.Unknown => return error.SemanticAnalysisFailed,
123 else => return child,
124 }
125 }
126
127 /// asserts that the type is known
128 fn getKnownType(self: *Instruction) *Type {
129 switch (self.val) {
130 IrVal.KnownType => |typeof| return typeof,
131 IrVal.KnownValue => |value| return value.typeof,
132 IrVal.Unknown => unreachable,
133 }
134 }
135
136 pub fn setGenerated(base: *Instruction) void {
137 base.is_generated = true;
138 }
139
140 pub fn isNoReturn(base: *const Instruction) bool {
141 switch (base.val) {
142 IrVal.Unknown => return false,
143 IrVal.KnownValue => |x| return x.typeof.id == Type.Id.NoReturn,
144 IrVal.KnownType => |typeof| return typeof.id == Type.Id.NoReturn,
145 }
146 }
147
148 pub fn linkToParent(self: *Instruction, parent: *Instruction) void {
149 assert(self.parent == null);
150 assert(parent.child == null);
151 self.parent = parent;
152 parent.child = self;
153 }
6154
7 pub const Id = enum {155 pub const Id = enum {
8 Br,
9 CondBr,
10 SwitchBr,
11 SwitchVar,
12 SwitchTarget,
13 Phi,
14 UnOp,
15 BinOp,
16 DeclVar,
17 LoadPtr,
18 StorePtr,
19 FieldPtr,
20 StructFieldPtr,
21 UnionFieldPtr,
22 ElemPtr,
23 VarPtr,
24 Call,
25 Const,
26 Return,156 Return,
27 Cast,157 Const,
28 ContainerInitList,
29 ContainerInitFields,
30 StructInit,
31 UnionInit,
32 Unreachable,
33 TypeOf,
34 ToPtrType,
35 PtrTypeChild,
36 SetRuntimeSafety,
37 SetFloatMode,
38 ArrayType,
39 SliceType,
40 Asm,
41 SizeOf,
42 TestNonNull,
43 UnwrapMaybe,
44 MaybeWrap,
45 UnionTag,
46 Clz,
47 Ctz,
48 Import,
49 CImport,
50 CInclude,
51 CDefine,
52 CUndef,
53 ArrayLen,
54 Ref,158 Ref,
55 MinValue,159 DeclVar,
56 MaxValue,160 CheckVoidStmt,
57 CompileErr,161 Phi,
58 CompileLog,162 Br,
59 ErrName,163 AddImplicitReturnType,
60 EmbedFile,164 };
61 Cmpxchg,165
62 Fence,166 pub const Const = struct {
63 Truncate,167 base: Instruction,
64 IntType,168 params: Params,
65 BoolNot,169
66 Memset,170 const Params = struct {};
67 Memcpy,171
68 Slice,172 // Use Builder.buildConst* methods, or, after building a Const instruction,
69 MemberCount,173 // manually set the ir_val field.
70 MemberType,174 const ir_val_init = IrVal.Init.Unknown;
71 MemberName,175
72 Breakpoint,176 pub fn dump(self: *const Const) void {
73 ReturnAddress,177 self.base.val.KnownValue.dump();
74 FrameAddress,178 }
75 AlignOf,179
76 OverflowOp,180 pub fn hasSideEffects(self: *const Const) bool {
77 TestErr,181 return false;
78 UnwrapErrCode,182 }
79 UnwrapErrPayload,183
80 ErrWrapCode,184 pub fn analyze(self: *const Const, ira: *Analyze) !*Instruction {
81 ErrWrapPayload,185 const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{});
82 FnProto,186 new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() };
83 TestComptime,187 return new_inst;
84 PtrCast,188 }
85 BitCast,189 };
86 WidenOrShorten,190
87 IntToPtr,191 pub const Return = struct {
88 PtrToInt,192 base: Instruction,
89 IntToEnum,193 params: Params,
90 IntToErr,194
91 ErrToInt,195 const Params = struct {
92 CheckSwitchProngs,196 return_value: *Instruction,
93 CheckStatementIsVoid,197 };
94 TypeName,198
95 CanImplicitCast,199 const ir_val_init = IrVal.Init.NoReturn;
96 DeclRef,200
97 Panic,201 pub fn dump(self: *const Return) void {
98 TagName,202 std.debug.warn("#{}", self.params.return_value.debug_id);
99 TagType,203 }
100 FieldParentPtr,204
101 OffsetOf,205 pub fn hasSideEffects(self: *const Return) bool {
102 TypeId,206 return true;
103 SetEvalBranchQuota,207 }
104 PtrTypeOf,208
105 AlignCast,209 pub fn analyze(self: *const Return, ira: *Analyze) !*Instruction {
106 OpaqueType,210 const value = try self.params.return_value.getAsParam();
107 SetAlignStack,211 const casted_value = try ira.implicitCast(value, ira.explicit_return_type);
108 ArgType,212
109 Export,213 // TODO detect returning local variable address
214
215 return ira.irb.build(Return, self.base.scope, self.base.span, Params{ .return_value = casted_value });
216 }
217 };
218
219 pub const Ref = struct {
220 base: Instruction,
221 params: Params,
222
223 const Params = struct {
224 target: *Instruction,
225 mut: Type.Pointer.Mut,
226 volatility: Type.Pointer.Vol,
227 };
228
229 const ir_val_init = IrVal.Init.Unknown;
230
231 pub fn dump(inst: *const Ref) void {}
232
233 pub fn hasSideEffects(inst: *const Ref) bool {
234 return false;
235 }
236
237 pub fn analyze(self: *const Ref, ira: *Analyze) !*Instruction {
238 const target = try self.params.target.getAsParam();
239
240 if (ira.getCompTimeValOrNullUndefOk(target)) |val| {
241 return ira.getCompTimeRef(
242 val,
243 Value.Ptr.Mut.CompTimeConst,
244 self.params.mut,
245 self.params.volatility,
246 val.typeof.getAbiAlignment(ira.irb.comp),
247 );
248 }
249
250 const new_inst = try ira.irb.build(Ref, self.base.scope, self.base.span, Params{
251 .target = target,
252 .mut = self.params.mut,
253 .volatility = self.params.volatility,
254 });
255 const elem_type = target.getKnownType();
256 const ptr_type = Type.Pointer.get(
257 ira.irb.comp,
258 elem_type,
259 self.params.mut,
260 self.params.volatility,
261 Type.Pointer.Size.One,
262 elem_type.getAbiAlignment(ira.irb.comp),
263 );
264 // TODO: potentially set the hint that this is a stack pointer. But it might not be - this
265 // could be a ref of a global, for example
266 new_inst.val = IrVal{ .KnownType = &ptr_type.base };
267 // TODO potentially add an alloca entry here
268 return new_inst;
269 }
270 };
271
272 pub const DeclVar = struct {
273 base: Instruction,
274 params: Params,
275
276 const Params = struct {
277 variable: *Variable,
278 };
279
280 const ir_val_init = IrVal.Init.Unknown;
281
282 pub fn dump(inst: *const DeclVar) void {}
283
284 pub fn hasSideEffects(inst: *const DeclVar) bool {
285 return true;
286 }
287
288 pub fn analyze(self: *const DeclVar, ira: *Analyze) !*Instruction {
289 return error.Unimplemented; // TODO
290 }
291 };
292
293 pub const CheckVoidStmt = struct {
294 base: Instruction,
295 params: Params,
296
297 const Params = struct {
298 target: *Instruction,
299 };
300
301 const ir_val_init = IrVal.Init.Unknown;
302
303 pub fn dump(inst: *const CheckVoidStmt) void {}
304
305 pub fn hasSideEffects(inst: *const CheckVoidStmt) bool {
306 return true;
307 }
308
309 pub fn analyze(self: *const CheckVoidStmt, ira: *Analyze) !*Instruction {
310 return error.Unimplemented; // TODO
311 }
312 };
313
314 pub const Phi = struct {
315 base: Instruction,
316 params: Params,
317
318 const Params = struct {
319 incoming_blocks: []*BasicBlock,
320 incoming_values: []*Instruction,
321 };
322
323 const ir_val_init = IrVal.Init.Unknown;
324
325 pub fn dump(inst: *const Phi) void {}
326
327 pub fn hasSideEffects(inst: *const Phi) bool {
328 return false;
329 }
330
331 pub fn analyze(self: *const Phi, ira: *Analyze) !*Instruction {
332 return error.Unimplemented; // TODO
333 }
334 };
335
336 pub const Br = struct {
337 base: Instruction,
338 params: Params,
339
340 const Params = struct {
341 dest_block: *BasicBlock,
342 is_comptime: *Instruction,
343 };
344
345 const ir_val_init = IrVal.Init.NoReturn;
346
347 pub fn dump(inst: *const Br) void {}
348
349 pub fn hasSideEffects(inst: *const Br) bool {
350 return true;
351 }
352
353 pub fn analyze(self: *const Br, ira: *Analyze) !*Instruction {
354 return error.Unimplemented; // TODO
355 }
110 };356 };
357
358 pub const AddImplicitReturnType = struct {
359 base: Instruction,
360 params: Params,
361
362 pub const Params = struct {
363 target: *Instruction,
364 };
365
366 const ir_val_init = IrVal.Init.Unknown;
367
368 pub fn dump(inst: *const AddImplicitReturnType) void {
369 std.debug.warn("#{}", inst.params.target.debug_id);
370 }
371
372 pub fn hasSideEffects(inst: *const AddImplicitReturnType) bool {
373 return true;
374 }
375
376 pub fn analyze(self: *const AddImplicitReturnType, ira: *Analyze) !*Instruction {
377 const target = try self.params.target.getAsParam();
378 try ira.src_implicit_return_type_list.append(target);
379 return ira.irb.buildConstVoid(self.base.scope, self.base.span, true);
380 }
381 };
382};
383
384pub const Variable = struct {
385 child_scope: *Scope,
111};386};
387
388pub const BasicBlock = struct {
389 ref_count: usize,
390 name_hint: []const u8,
391 debug_id: usize,
392 scope: *Scope,
393 instruction_list: std.ArrayList(*Instruction),
394 ref_instruction: ?*Instruction,
395
396 /// the basic block that is derived from this one in analysis
397 child: ?*BasicBlock,
398
399 /// the basic block that this one derives from in analysis
400 parent: ?*BasicBlock,
401
402 pub fn ref(self: *BasicBlock) void {
403 self.ref_count += 1;
404 }
405
406 pub fn linkToParent(self: *BasicBlock, parent: *BasicBlock) void {
407 assert(self.parent == null);
408 assert(parent.child == null);
409 self.parent = parent;
410 parent.child = self;
411 }
412};
413
414/// Stuff that survives longer than Builder
415pub const Code = struct {
416 basic_block_list: std.ArrayList(*BasicBlock),
417 arena: std.heap.ArenaAllocator,
418 return_type: ?*Type,
419
420 /// allocator is comp.a()
421 pub fn destroy(self: *Code, allocator: *Allocator) void {
422 self.arena.deinit();
423 allocator.destroy(self);
424 }
425
426 pub fn dump(self: *Code) void {
427 var bb_i: usize = 0;
428 for (self.basic_block_list.toSliceConst()) |bb| {
429 std.debug.warn("{}_{}:\n", bb.name_hint, bb.debug_id);
430 for (bb.instruction_list.toSliceConst()) |instr| {
431 std.debug.warn(" ");
432 instr.dump();
433 std.debug.warn("\n");
434 }
435 }
436 }
437};
438
439pub const Builder = struct {
440 comp: *Compilation,
441 code: *Code,
442 current_basic_block: *BasicBlock,
443 next_debug_id: usize,
444 parsed_file: *ParsedFile,
445 is_comptime: bool,
446
447 pub const Error = Analyze.Error;
448
449 pub fn init(comp: *Compilation, parsed_file: *ParsedFile) !Builder {
450 const code = try comp.a().create(Code{
451 .basic_block_list = undefined,
452 .arena = std.heap.ArenaAllocator.init(comp.a()),
453 .return_type = null,
454 });
455 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
456 errdefer code.destroy(comp.a());
457
458 return Builder{
459 .comp = comp,
460 .parsed_file = parsed_file,
461 .current_basic_block = undefined,
462 .code = code,
463 .next_debug_id = 0,
464 .is_comptime = false,
465 };
466 }
467
468 pub fn abort(self: *Builder) void {
469 self.code.destroy(self.comp.a());
470 }
471
472 /// Call code.destroy() when done
473 pub fn finish(self: *Builder) *Code {
474 return self.code;
475 }
476
477 /// No need to clean up resources thanks to the arena allocator.
478 pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: []const u8) !*BasicBlock {
479 const basic_block = try self.arena().create(BasicBlock{
480 .ref_count = 0,
481 .name_hint = name_hint,
482 .debug_id = self.next_debug_id,
483 .scope = scope,
484 .instruction_list = std.ArrayList(*Instruction).init(self.arena()),
485 .child = null,
486 .parent = null,
487 .ref_instruction = null,
488 });
489 self.next_debug_id += 1;
490 return basic_block;
491 }
492
493 pub fn setCursorAtEndAndAppendBlock(self: *Builder, basic_block: *BasicBlock) !void {
494 try self.code.basic_block_list.append(basic_block);
495 self.setCursorAtEnd(basic_block);
496 }
497
498 pub fn setCursorAtEnd(self: *Builder, basic_block: *BasicBlock) void {
499 self.current_basic_block = basic_block;
500 }
501
502 pub fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Instruction {
503 switch (node.id) {
504 ast.Node.Id.Root => unreachable,
505 ast.Node.Id.Use => unreachable,
506 ast.Node.Id.TestDecl => unreachable,
507 ast.Node.Id.VarDecl => @panic("TODO"),
508 ast.Node.Id.Defer => @panic("TODO"),
509 ast.Node.Id.InfixOp => @panic("TODO"),
510 ast.Node.Id.PrefixOp => @panic("TODO"),
511 ast.Node.Id.SuffixOp => @panic("TODO"),
512 ast.Node.Id.Switch => @panic("TODO"),
513 ast.Node.Id.While => @panic("TODO"),
514 ast.Node.Id.For => @panic("TODO"),
515 ast.Node.Id.If => @panic("TODO"),
516 ast.Node.Id.ControlFlowExpression => return error.Unimplemented,
517 ast.Node.Id.Suspend => @panic("TODO"),
518 ast.Node.Id.VarType => @panic("TODO"),
519 ast.Node.Id.ErrorType => @panic("TODO"),
520 ast.Node.Id.FnProto => @panic("TODO"),
521 ast.Node.Id.PromiseType => @panic("TODO"),
522 ast.Node.Id.IntegerLiteral => @panic("TODO"),
523 ast.Node.Id.FloatLiteral => @panic("TODO"),
524 ast.Node.Id.StringLiteral => @panic("TODO"),
525 ast.Node.Id.MultilineStringLiteral => @panic("TODO"),
526 ast.Node.Id.CharLiteral => @panic("TODO"),
527 ast.Node.Id.BoolLiteral => @panic("TODO"),
528 ast.Node.Id.NullLiteral => @panic("TODO"),
529 ast.Node.Id.UndefinedLiteral => @panic("TODO"),
530 ast.Node.Id.ThisLiteral => @panic("TODO"),
531 ast.Node.Id.Unreachable => @panic("TODO"),
532 ast.Node.Id.Identifier => @panic("TODO"),
533 ast.Node.Id.GroupedExpression => {
534 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node);
535 return irb.genNode(grouped_expr.expr, scope, lval);
536 },
537 ast.Node.Id.BuiltinCall => @panic("TODO"),
538 ast.Node.Id.ErrorSetDecl => @panic("TODO"),
539 ast.Node.Id.ContainerDecl => @panic("TODO"),
540 ast.Node.Id.Asm => @panic("TODO"),
541 ast.Node.Id.Comptime => @panic("TODO"),
542 ast.Node.Id.Block => {
543 const block = @fieldParentPtr(ast.Node.Block, "base", node);
544 return irb.lvalWrap(scope, try irb.genBlock(block, scope), lval);
545 },
546 ast.Node.Id.DocComment => @panic("TODO"),
547 ast.Node.Id.SwitchCase => @panic("TODO"),
548 ast.Node.Id.SwitchElse => @panic("TODO"),
549 ast.Node.Id.Else => @panic("TODO"),
550 ast.Node.Id.Payload => @panic("TODO"),
551 ast.Node.Id.PointerPayload => @panic("TODO"),
552 ast.Node.Id.PointerIndexPayload => @panic("TODO"),
553 ast.Node.Id.StructField => @panic("TODO"),
554 ast.Node.Id.UnionTag => @panic("TODO"),
555 ast.Node.Id.EnumTag => @panic("TODO"),
556 ast.Node.Id.ErrorTag => @panic("TODO"),
557 ast.Node.Id.AsmInput => @panic("TODO"),
558 ast.Node.Id.AsmOutput => @panic("TODO"),
559 ast.Node.Id.AsyncAttribute => @panic("TODO"),
560 ast.Node.Id.ParamDecl => @panic("TODO"),
561 ast.Node.Id.FieldInitializer => @panic("TODO"),
562 }
563 }
564
565 fn isCompTime(irb: *Builder, target_scope: *Scope) bool {
566 if (irb.is_comptime)
567 return true;
568
569 var scope = target_scope;
570 while (true) {
571 switch (scope.id) {
572 Scope.Id.CompTime => return true,
573 Scope.Id.FnDef => return false,
574 Scope.Id.Decls => unreachable,
575 Scope.Id.Block,
576 Scope.Id.Defer,
577 Scope.Id.DeferExpr,
578 => scope = scope.parent orelse return false,
579 }
580 }
581 }
582
583 pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Instruction {
584 const block_scope = try Scope.Block.create(irb.comp, parent_scope);
585
586 const outer_block_scope = &block_scope.base;
587 var child_scope = outer_block_scope;
588
589 if (parent_scope.findFnDef()) |fndef_scope| {
590 if (fndef_scope.fn_val.child_scope == parent_scope) {
591 fndef_scope.fn_val.block_scope = block_scope;
592 }
593 }
594
595 if (block.statements.len == 0) {
596 // {}
597 return irb.buildConstVoid(child_scope, Span.token(block.lbrace), false);
598 }
599
600 if (block.label) |label| {
601 block_scope.incoming_values = std.ArrayList(*Instruction).init(irb.arena());
602 block_scope.incoming_blocks = std.ArrayList(*BasicBlock).init(irb.arena());
603 block_scope.end_block = try irb.createBasicBlock(parent_scope, "BlockEnd");
604 block_scope.is_comptime = try irb.buildConstBool(
605 parent_scope,
606 Span.token(block.lbrace),
607 irb.isCompTime(parent_scope),
608 );
609 }
610
611 var is_continuation_unreachable = false;
612 var noreturn_return_value: ?*Instruction = null;
613
614 var stmt_it = block.statements.iterator(0);
615 while (stmt_it.next()) |statement_node_ptr| {
616 const statement_node = statement_node_ptr.*;
617
618 if (statement_node.cast(ast.Node.Defer)) |defer_node| {
619 // defer starts a new scope
620 const defer_token = irb.parsed_file.tree.tokens.at(defer_node.defer_token);
621 const kind = switch (defer_token.id) {
622 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
623 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
624 else => unreachable,
625 };
626 const defer_expr_scope = try Scope.DeferExpr.create(irb.comp, parent_scope, defer_node.expr);
627 const defer_child_scope = try Scope.Defer.create(irb.comp, parent_scope, kind, defer_expr_scope);
628 child_scope = &defer_child_scope.base;
629 continue;
630 }
631 const statement_value = try irb.genNode(statement_node, child_scope, LVal.None);
632
633 is_continuation_unreachable = statement_value.isNoReturn();
634 if (is_continuation_unreachable) {
635 // keep the last noreturn statement value around in case we need to return it
636 noreturn_return_value = statement_value;
637 }
638
639 if (statement_value.cast(Instruction.DeclVar)) |decl_var| {
640 // variable declarations start a new scope
641 child_scope = decl_var.params.variable.child_scope;
642 } else if (!is_continuation_unreachable) {
643 // this statement's value must be void
644 _ = irb.build(
645 Instruction.CheckVoidStmt,
646 child_scope,
647 statement_value.span,
648 Instruction.CheckVoidStmt.Params{ .target = statement_value },
649 );
650 }
651 }
652
653 if (is_continuation_unreachable) {
654 assert(noreturn_return_value != null);
655 if (block.label == null or block_scope.incoming_blocks.len == 0) {
656 return noreturn_return_value.?;
657 }
658
659 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
660 return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{
661 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
662 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
663 });
664 }
665
666 if (block.label) |label| {
667 try block_scope.incoming_blocks.append(irb.current_basic_block);
668 try block_scope.incoming_values.append(
669 try irb.buildConstVoid(parent_scope, Span.token(block.rbrace), true),
670 );
671 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);
672
673 _ = try irb.buildGen(Instruction.Br, parent_scope, Span.token(block.rbrace), Instruction.Br.Params{
674 .dest_block = block_scope.end_block,
675 .is_comptime = block_scope.is_comptime,
676 });
677
678 try irb.setCursorAtEndAndAppendBlock(block_scope.end_block);
679
680 return irb.build(Instruction.Phi, parent_scope, Span.token(block.rbrace), Instruction.Phi.Params{
681 .incoming_blocks = block_scope.incoming_blocks.toOwnedSlice(),
682 .incoming_values = block_scope.incoming_values.toOwnedSlice(),
683 });
684 }
685
686 _ = try irb.genDefersForBlock(child_scope, outer_block_scope, Scope.Defer.Kind.ScopeExit);
687 return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);
688 }
689
690 fn genDefersForBlock(
691 irb: *Builder,
692 inner_scope: *Scope,
693 outer_scope: *Scope,
694 gen_kind: Scope.Defer.Kind,
695 ) !bool {
696 var scope = inner_scope;
697 var is_noreturn = false;
698 while (true) {
699 switch (scope.id) {
700 Scope.Id.Defer => {
701 const defer_scope = @fieldParentPtr(Scope.Defer, "base", scope);
702 const generate = switch (defer_scope.kind) {
703 Scope.Defer.Kind.ScopeExit => true,
704 Scope.Defer.Kind.ErrorExit => gen_kind == Scope.Defer.Kind.ErrorExit,
705 };
706 if (generate) {
707 const defer_expr_scope = defer_scope.defer_expr_scope;
708 const instruction = try irb.genNode(
709 defer_expr_scope.expr_node,
710 &defer_expr_scope.base,
711 LVal.None,
712 );
713 if (instruction.isNoReturn()) {
714 is_noreturn = true;
715 } else {
716 _ = try irb.build(
717 Instruction.CheckVoidStmt,
718 &defer_expr_scope.base,
719 Span.token(defer_expr_scope.expr_node.lastToken()),
720 Instruction.CheckVoidStmt.Params{ .target = instruction },
721 );
722 }
723 }
724 },
725 Scope.Id.FnDef,
726 Scope.Id.Decls,
727 => return is_noreturn,
728
729 Scope.Id.CompTime,
730 Scope.Id.Block,
731 => scope = scope.parent orelse return is_noreturn,
732
733 Scope.Id.DeferExpr => unreachable,
734 }
735 }
736 }
737
738 pub fn lvalWrap(irb: *Builder, scope: *Scope, instruction: *Instruction, lval: LVal) !*Instruction {
739 switch (lval) {
740 LVal.None => return instruction,
741 LVal.Ptr => {
742 // We needed a pointer to a value, but we got a value. So we create
743 // an instruction which just makes a const pointer of it.
744 return irb.build(Instruction.Ref, scope, instruction.span, Instruction.Ref.Params{
745 .target = instruction,
746 .mut = Type.Pointer.Mut.Const,
747 .volatility = Type.Pointer.Vol.Non,
748 });
749 },
750 }
751 }
752
753 fn arena(self: *Builder) *Allocator {
754 return &self.code.arena.allocator;
755 }
756
757 fn buildExtra(
758 self: *Builder,
759 comptime I: type,
760 scope: *Scope,
761 span: Span,
762 params: I.Params,
763 is_generated: bool,
764 ) !*Instruction {
765 const inst = try self.arena().create(I{
766 .base = Instruction{
767 .id = Instruction.typeToId(I),
768 .is_generated = is_generated,
769 .scope = scope,
770 .debug_id = self.next_debug_id,
771 .val = switch (I.ir_val_init) {
772 IrVal.Init.Unknown => IrVal.Unknown,
773 IrVal.Init.NoReturn => IrVal{ .KnownValue = &Value.NoReturn.get(self.comp).base },
774 IrVal.Init.Void => IrVal{ .KnownValue = &Value.Void.get(self.comp).base },
775 },
776 .ref_count = 0,
777 .span = span,
778 .child = null,
779 .parent = null,
780 },
781 .params = params,
782 });
783
784 // Look at the params and ref() other instructions
785 comptime var i = 0;
786 inline while (i < @memberCount(I.Params)) : (i += 1) {
787 const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i)));
788 switch (FieldType) {
789 *Instruction => @field(inst.params, @memberName(I.Params, i)).ref_count += 1,
790 ?*Instruction => if (@field(inst.params, @memberName(I.Params, i))) |other| other.ref_count += 1,
791 else => {},
792 }
793 }
794
795 self.next_debug_id += 1;
796 try self.current_basic_block.instruction_list.append(&inst.base);
797 return &inst.base;
798 }
799
800 fn build(
801 self: *Builder,
802 comptime I: type,
803 scope: *Scope,
804 span: Span,
805 params: I.Params,
806 ) !*Instruction {
807 return self.buildExtra(I, scope, span, params, false);
808 }
809
810 fn buildGen(
811 self: *Builder,
812 comptime I: type,
813 scope: *Scope,
814 span: Span,
815 params: I.Params,
816 ) !*Instruction {
817 return self.buildExtra(I, scope, span, params, true);
818 }
819
820 fn buildConstBool(self: *Builder, scope: *Scope, span: Span, x: bool) !*Instruction {
821 const inst = try self.build(Instruction.Const, scope, span, Instruction.Const.Params{});
822 inst.val = IrVal{ .KnownValue = &Value.Bool.get(self.comp, x).base };
823 return inst;
824 }
825
826 fn buildConstVoid(self: *Builder, scope: *Scope, span: Span, is_generated: bool) !*Instruction {
827 const inst = try self.buildExtra(Instruction.Const, scope, span, Instruction.Const.Params{}, is_generated);
828 inst.val = IrVal{ .KnownValue = &Value.Void.get(self.comp).base };
829 return inst;
830 }
831};
832
833const Analyze = struct {
834 irb: Builder,
835 old_bb_index: usize,
836 const_predecessor_bb: ?*BasicBlock,
837 parent_basic_block: *BasicBlock,
838 instruction_index: usize,
839 src_implicit_return_type_list: std.ArrayList(*Instruction),
840 explicit_return_type: ?*Type,
841
842 pub const Error = error{
843 /// This is only for when we have already reported a compile error. It is the poison value.
844 SemanticAnalysisFailed,
845
846 /// This is a placeholder - it is useful to use instead of panicking but once the compiler is
847 /// done this error code will be removed.
848 Unimplemented,
849
850 OutOfMemory,
851 };
852
853 pub fn init(comp: *Compilation, parsed_file: *ParsedFile, explicit_return_type: ?*Type) !Analyze {
854 var irb = try Builder.init(comp, parsed_file);
855 errdefer irb.abort();
856
857 return Analyze{
858 .irb = irb,
859 .old_bb_index = 0,
860 .const_predecessor_bb = null,
861 .parent_basic_block = undefined, // initialized with startBasicBlock
862 .instruction_index = undefined, // initialized with startBasicBlock
863 .src_implicit_return_type_list = std.ArrayList(*Instruction).init(irb.arena()),
864 .explicit_return_type = explicit_return_type,
865 };
866 }
867
868 pub fn abort(self: *Analyze) void {
869 self.irb.abort();
870 }
871
872 pub fn getNewBasicBlock(self: *Analyze, old_bb: *BasicBlock, ref_old_instruction: ?*Instruction) !*BasicBlock {
873 if (old_bb.child) |child| {
874 if (ref_old_instruction == null or child.ref_instruction != ref_old_instruction)
875 return child;
876 }
877
878 const new_bb = try self.irb.createBasicBlock(old_bb.scope, old_bb.name_hint);
879 new_bb.linkToParent(old_bb);
880 new_bb.ref_instruction = ref_old_instruction;
881 return new_bb;
882 }
883
884 pub fn startBasicBlock(self: *Analyze, old_bb: *BasicBlock, const_predecessor_bb: ?*BasicBlock) void {
885 self.instruction_index = 0;
886 self.parent_basic_block = old_bb;
887 self.const_predecessor_bb = const_predecessor_bb;
888 }
889
890 pub fn finishBasicBlock(ira: *Analyze, old_code: *Code) !void {
891 try ira.irb.code.basic_block_list.append(ira.irb.current_basic_block);
892 ira.instruction_index += 1;
893
894 while (ira.instruction_index < ira.parent_basic_block.instruction_list.len) {
895 const next_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index);
896
897 if (!next_instruction.is_generated) {
898 try ira.addCompileError(next_instruction.span, "unreachable code");
899 break;
900 }
901 ira.instruction_index += 1;
902 }
903
904 ira.old_bb_index += 1;
905
906 var need_repeat = true;
907 while (true) {
908 while (ira.old_bb_index < old_code.basic_block_list.len) {
909 const old_bb = old_code.basic_block_list.at(ira.old_bb_index);
910 const new_bb = old_bb.child orelse {
911 ira.old_bb_index += 1;
912 continue;
913 };
914 if (new_bb.instruction_list.len != 0) {
915 ira.old_bb_index += 1;
916 continue;
917 }
918 ira.irb.current_basic_block = new_bb;
919
920 ira.startBasicBlock(old_bb, null);
921 return;
922 }
923 if (!need_repeat)
924 return;
925 need_repeat = false;
926 ira.old_bb_index = 0;
927 continue;
928 }
929 }
930
931 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {
932 return self.irb.comp.addCompileError(self.irb.parsed_file, span, fmt, args);
933 }
934
935 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Instruction) Analyze.Error!*Type {
936 // TODO actual implementation
937 return &Type.Void.get(self.irb.comp).base;
938 }
939
940 fn implicitCast(self: *Analyze, target: *Instruction, optional_dest_type: ?*Type) Analyze.Error!*Instruction {
941 const dest_type = optional_dest_type orelse return target;
942 @panic("TODO implicitCast");
943 }
944
945 fn getCompTimeValOrNullUndefOk(self: *Analyze, target: *Instruction) ?*Value {
946 @panic("TODO getCompTimeValOrNullUndefOk");
947 }
948
949 fn getCompTimeRef(
950 self: *Analyze,
951 value: *Value,
952 ptr_mut: Value.Ptr.Mut,
953 mut: Type.Pointer.Mut,
954 volatility: Type.Pointer.Vol,
955 ptr_align: u32,
956 ) Analyze.Error!*Instruction {
957 @panic("TODO getCompTimeRef");
958 }
959};
960
961pub async fn gen(
962 comp: *Compilation,
963 body_node: *ast.Node,
964 scope: *Scope,
965 end_span: Span,
966 parsed_file: *ParsedFile,
967) !*Code {
968 var irb = try Builder.init(comp, parsed_file);
969 errdefer irb.abort();
970
971 const entry_block = try irb.createBasicBlock(scope, "Entry");
972 entry_block.ref(); // Entry block gets a reference because we enter it to begin.
973 try irb.setCursorAtEndAndAppendBlock(entry_block);
974
975 const result = try irb.genNode(body_node, scope, LVal.None);
976 if (!result.isNoReturn()) {
977 _ = irb.buildGen(
978 Instruction.AddImplicitReturnType,
979 scope,
980 end_span,
981 Instruction.AddImplicitReturnType.Params{ .target = result },
982 );
983 _ = irb.buildGen(
984 Instruction.Return,
985 scope,
986 end_span,
987 Instruction.Return.Params{ .return_value = result },
988 );
989 }
990
991 return irb.finish();
992}
993
994pub async fn analyze(comp: *Compilation, parsed_file: *ParsedFile, old_code: *Code, expected_type: ?*Type) !*Code {
995 var ira = try Analyze.init(comp, parsed_file, expected_type);
996 errdefer ira.abort();
997
998 const old_entry_bb = old_code.basic_block_list.at(0);
999
1000 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
1001 new_entry_bb.ref();
1002
1003 ira.irb.current_basic_block = new_entry_bb;
1004
1005 ira.startBasicBlock(old_entry_bb, null);
1006
1007 while (ira.old_bb_index < old_code.basic_block_list.len) {
1008 const old_instruction = ira.parent_basic_block.instruction_list.at(ira.instruction_index);
1009
1010 if (old_instruction.ref_count == 0 and !old_instruction.hasSideEffects()) {
1011 ira.instruction_index += 1;
1012 continue;
1013 }
1014
1015 const return_inst = try old_instruction.analyze(&ira);
1016 // Note: if we ever modify the above to handle error.CompileError by continuing analysis,
1017 // then here we want to check if ira.isCompTime() and return early if true
1018
1019 if (return_inst.isNoReturn()) {
1020 try ira.finishBasicBlock(old_code);
1021 continue;
1022 }
1023
1024 ira.instruction_index += 1;
1025 }
1026
1027 if (ira.src_implicit_return_type_list.len == 0) {
1028 ira.irb.code.return_type = &Type.NoReturn.get(comp).base;
1029 return ira.irb.finish();
1030 }
1031
1032 ira.irb.code.return_type = try ira.resolvePeerTypes(expected_type, ira.src_implicit_return_type_list.toSliceConst());
1033 return ira.irb.finish();
1034}
src-self-hosted/llvm.zig+20-3
...@@ -2,10 +2,27 @@ const builtin = @import("builtin");...@@ -2,10 +2,27 @@ const builtin = @import("builtin");
2const c = @import("c.zig");2const c = @import("c.zig");
3const assert = @import("std").debug.assert;3const assert = @import("std").debug.assert;
44
5pub const ValueRef = removeNullability(c.LLVMValueRef);
6pub const ModuleRef = removeNullability(c.LLVMModuleRef);
7pub const ContextRef = removeNullability(c.LLVMContextRef);
8pub const BuilderRef = removeNullability(c.LLVMBuilderRef);5pub const BuilderRef = removeNullability(c.LLVMBuilderRef);
6pub const ContextRef = removeNullability(c.LLVMContextRef);
7pub const ModuleRef = removeNullability(c.LLVMModuleRef);
8pub const ValueRef = removeNullability(c.LLVMValueRef);
9pub const TypeRef = removeNullability(c.LLVMTypeRef);
10
11pub const AddFunction = c.LLVMAddFunction;
12pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext;
13pub const DisposeBuilder = c.LLVMDisposeBuilder;
14pub const DisposeModule = c.LLVMDisposeModule;
15pub const DumpModule = c.LLVMDumpModule;
16pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
17pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
18
19pub const FunctionType = LLVMFunctionType;
20extern fn LLVMFunctionType(
21 ReturnType: TypeRef,
22 ParamTypes: [*]TypeRef,
23 ParamCount: c_uint,
24 IsVarArg: c_int,
25) ?TypeRef;
926
10fn removeNullability(comptime T: type) type {27fn removeNullability(comptime T: type) type {
11 comptime assert(@typeId(T) == builtin.TypeId.Optional);28 comptime assert(@typeId(T) == builtin.TypeId.Optional);
src-self-hosted/main.zig+114-74
...@@ -14,7 +14,8 @@ const c = @import("c.zig");...@@ -14,7 +14,8 @@ const c = @import("c.zig");
14const introspect = @import("introspect.zig");14const introspect = @import("introspect.zig");
15const Args = arg.Args;15const Args = arg.Args;
16const Flag = arg.Flag;16const Flag = arg.Flag;
17const Module = @import("module.zig").Module;17const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
18const Compilation = @import("compilation.zig").Compilation;
18const Target = @import("target.zig").Target;19const Target = @import("target.zig").Target;
19const errmsg = @import("errmsg.zig");20const errmsg = @import("errmsg.zig");
2021
...@@ -257,7 +258,7 @@ const args_build_generic = []Flag{...@@ -257,7 +258,7 @@ const args_build_generic = []Flag{
257 Flag.Arg1("--ver-patch"),258 Flag.Arg1("--ver-patch"),
258};259};
259260
260fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Module.Kind) !void {261fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Compilation.Kind) !void {
261 var flags = try Args.parse(allocator, args_build_generic, args);262 var flags = try Args.parse(allocator, args_build_generic, args);
262 defer flags.deinit();263 defer flags.deinit();
263264
...@@ -299,14 +300,14 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo...@@ -299,14 +300,14 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
299 const emit_type = blk: {300 const emit_type = blk: {
300 if (flags.single("emit")) |emit_flag| {301 if (flags.single("emit")) |emit_flag| {
301 if (mem.eql(u8, emit_flag, "asm")) {302 if (mem.eql(u8, emit_flag, "asm")) {
302 break :blk Module.Emit.Assembly;303 break :blk Compilation.Emit.Assembly;
303 } else if (mem.eql(u8, emit_flag, "bin")) {304 } else if (mem.eql(u8, emit_flag, "bin")) {
304 break :blk Module.Emit.Binary;305 break :blk Compilation.Emit.Binary;
305 } else if (mem.eql(u8, emit_flag, "llvm-ir")) {306 } else if (mem.eql(u8, emit_flag, "llvm-ir")) {
306 break :blk Module.Emit.LlvmIr;307 break :blk Compilation.Emit.LlvmIr;
307 } else unreachable;308 } else unreachable;
308 } else {309 } else {
309 break :blk Module.Emit.Binary;310 break :blk Compilation.Emit.Binary;
310 }311 }
311 };312 };
312313
...@@ -369,7 +370,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo...@@ -369,7 +370,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
369 os.exit(1);370 os.exit(1);
370 }371 }
371372
372 if (out_type == Module.Kind.Obj and link_objects.len != 0) {373 if (out_type == Compilation.Kind.Obj and link_objects.len != 0) {
373 try stderr.write("When building an object file, --object arguments are invalid\n");374 try stderr.write("When building an object file, --object arguments are invalid\n");
374 os.exit(1);375 os.exit(1);
375 }376 }
...@@ -386,9 +387,13 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo...@@ -386,9 +387,13 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
386387
387 var loop: event.Loop = undefined;388 var loop: event.Loop = undefined;
388 try loop.initMultiThreaded(allocator);389 try loop.initMultiThreaded(allocator);
390 defer loop.deinit();
389391
390 var module = try Module.create(392 var event_loop_local = EventLoopLocal.init(&loop);
391 &loop,393 defer event_loop_local.deinit();
394
395 var comp = try Compilation.create(
396 &event_loop_local,
392 root_name,397 root_name,
393 root_source_file,398 root_source_file,
394 Target.Native,399 Target.Native,
...@@ -397,16 +402,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo...@@ -397,16 +402,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
397 zig_lib_dir,402 zig_lib_dir,
398 full_cache_dir,403 full_cache_dir,
399 );404 );
400 defer module.destroy();405 defer comp.destroy();
401406
402 module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);407 comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
403 module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);408 comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
404 module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);409 comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
405410
406 module.is_test = false;411 comp.is_test = false;
407412
408 module.linker_script = flags.single("linker-script");413 comp.linker_script = flags.single("linker-script");
409 module.each_lib_rpath = flags.present("each-lib-rpath");414 comp.each_lib_rpath = flags.present("each-lib-rpath");
410415
411 var clang_argv_buf = ArrayList([]const u8).init(allocator);416 var clang_argv_buf = ArrayList([]const u8).init(allocator);
412 defer clang_argv_buf.deinit();417 defer clang_argv_buf.deinit();
...@@ -417,51 +422,51 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo...@@ -417,51 +422,51 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
417 try clang_argv_buf.append(mllvm);422 try clang_argv_buf.append(mllvm);
418 }423 }
419424
420 module.llvm_argv = mllvm_flags;425 comp.llvm_argv = mllvm_flags;
421 module.clang_argv = clang_argv_buf.toSliceConst();426 comp.clang_argv = clang_argv_buf.toSliceConst();
422427
423 module.strip = flags.present("strip");428 comp.strip = flags.present("strip");
424 module.is_static = flags.present("static");429 comp.is_static = flags.present("static");
425430
426 if (flags.single("libc-lib-dir")) |libc_lib_dir| {431 if (flags.single("libc-lib-dir")) |libc_lib_dir| {
427 module.libc_lib_dir = libc_lib_dir;432 comp.libc_lib_dir = libc_lib_dir;
428 }433 }
429 if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {434 if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
430 module.libc_static_lib_dir = libc_static_lib_dir;435 comp.libc_static_lib_dir = libc_static_lib_dir;
431 }436 }
432 if (flags.single("libc-include-dir")) |libc_include_dir| {437 if (flags.single("libc-include-dir")) |libc_include_dir| {
433 module.libc_include_dir = libc_include_dir;438 comp.libc_include_dir = libc_include_dir;
434 }439 }
435 if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {440 if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {
436 module.msvc_lib_dir = msvc_lib_dir;441 comp.msvc_lib_dir = msvc_lib_dir;
437 }442 }
438 if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {443 if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {
439 module.kernel32_lib_dir = kernel32_lib_dir;444 comp.kernel32_lib_dir = kernel32_lib_dir;
440 }445 }
441 if (flags.single("dynamic-linker")) |dynamic_linker| {446 if (flags.single("dynamic-linker")) |dynamic_linker| {
442 module.dynamic_linker = dynamic_linker;447 comp.dynamic_linker = dynamic_linker;
443 }448 }
444449
445 module.verbose_tokenize = flags.present("verbose-tokenize");450 comp.verbose_tokenize = flags.present("verbose-tokenize");
446 module.verbose_ast_tree = flags.present("verbose-ast-tree");451 comp.verbose_ast_tree = flags.present("verbose-ast-tree");
447 module.verbose_ast_fmt = flags.present("verbose-ast-fmt");452 comp.verbose_ast_fmt = flags.present("verbose-ast-fmt");
448 module.verbose_link = flags.present("verbose-link");453 comp.verbose_link = flags.present("verbose-link");
449 module.verbose_ir = flags.present("verbose-ir");454 comp.verbose_ir = flags.present("verbose-ir");
450 module.verbose_llvm_ir = flags.present("verbose-llvm-ir");455 comp.verbose_llvm_ir = flags.present("verbose-llvm-ir");
451 module.verbose_cimport = flags.present("verbose-cimport");456 comp.verbose_cimport = flags.present("verbose-cimport");
452457
453 module.err_color = color;458 comp.err_color = color;
454 module.lib_dirs = flags.many("library-path");459 comp.lib_dirs = flags.many("library-path");
455 module.darwin_frameworks = flags.many("framework");460 comp.darwin_frameworks = flags.many("framework");
456 module.rpath_list = flags.many("rpath");461 comp.rpath_list = flags.many("rpath");
457462
458 if (flags.single("output-h")) |output_h| {463 if (flags.single("output-h")) |output_h| {
459 module.out_h_path = output_h;464 comp.out_h_path = output_h;
460 }465 }
461466
462 module.windows_subsystem_windows = flags.present("mwindows");467 comp.windows_subsystem_windows = flags.present("mwindows");
463 module.windows_subsystem_console = flags.present("mconsole");468 comp.windows_subsystem_console = flags.present("mconsole");
464 module.linker_rdynamic = flags.present("rdynamic");469 comp.linker_rdynamic = flags.present("rdynamic");
465470
466 if (flags.single("mmacosx-version-min") != null and flags.single("mios-version-min") != null) {471 if (flags.single("mmacosx-version-min") != null and flags.single("mios-version-min") != null) {
467 try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n");472 try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n");
...@@ -469,54 +474,54 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo...@@ -469,54 +474,54 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
469 }474 }
470475
471 if (flags.single("mmacosx-version-min")) |ver| {476 if (flags.single("mmacosx-version-min")) |ver| {
472 module.darwin_version_min = Module.DarwinVersionMin{ .MacOS = ver };477 comp.darwin_version_min = Compilation.DarwinVersionMin{ .MacOS = ver };
473 }478 }
474 if (flags.single("mios-version-min")) |ver| {479 if (flags.single("mios-version-min")) |ver| {
475 module.darwin_version_min = Module.DarwinVersionMin{ .Ios = ver };480 comp.darwin_version_min = Compilation.DarwinVersionMin{ .Ios = ver };
476 }481 }
477482
478 module.emit_file_type = emit_type;483 comp.emit_file_type = emit_type;
479 module.link_objects = link_objects;484 comp.link_objects = link_objects;
480 module.assembly_files = assembly_files;485 comp.assembly_files = assembly_files;
481 module.link_out_file = flags.single("out-file");486 comp.link_out_file = flags.single("out-file");
482487
483 try module.build();488 try comp.build();
484 const process_build_events_handle = try async<loop.allocator> processBuildEvents(module, true);489 const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);
485 defer cancel process_build_events_handle;490 defer cancel process_build_events_handle;
486 loop.run();491 loop.run();
487}492}
488493
489async fn processBuildEvents(module: *Module, watch: bool) void {494async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
490 while (watch) {495 // TODO directly awaiting async should guarantee memory allocation elision
491 // TODO directly awaiting async should guarantee memory allocation elision496 const build_event = await (async comp.events.get() catch unreachable);
492 const build_event = await (async module.events.get() catch unreachable);
493497
494 switch (build_event) {498 switch (build_event) {
495 Module.Event.Ok => {499 Compilation.Event.Ok => {
496 std.debug.warn("Build succeeded\n");500 std.debug.warn("Build succeeded\n");
497 return;501 return;
498 },502 },
499 Module.Event.Error => |err| {503 Compilation.Event.Error => |err| {
500 std.debug.warn("build failed: {}\n", @errorName(err));504 std.debug.warn("build failed: {}\n", @errorName(err));
501 @panic("TODO error return trace");505 os.exit(1);
502 },506 },
503 Module.Event.Fail => |errs| {507 Compilation.Event.Fail => |msgs| {
504 @panic("TODO print compile error messages");508 for (msgs) |msg| {
505 },509 errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1);
506 }510 }
511 },
507 }512 }
508}513}
509514
510fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void {515fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void {
511 return buildOutputType(allocator, args, Module.Kind.Exe);516 return buildOutputType(allocator, args, Compilation.Kind.Exe);
512}517}
513518
514fn cmdBuildLib(allocator: *Allocator, args: []const []const u8) !void {519fn cmdBuildLib(allocator: *Allocator, args: []const []const u8) !void {
515 return buildOutputType(allocator, args, Module.Kind.Lib);520 return buildOutputType(allocator, args, Compilation.Kind.Lib);
516}521}
517522
518fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void {523fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void {
519 return buildOutputType(allocator, args, Module.Kind.Obj);524 return buildOutputType(allocator, args, Compilation.Kind.Obj);
520}525}
521526
522const usage_fmt =527const usage_fmt =
...@@ -527,6 +532,7 @@ const usage_fmt =...@@ -527,6 +532,7 @@ const usage_fmt =
527 \\Options:532 \\Options:
528 \\ --help Print this help and exit533 \\ --help Print this help and exit
529 \\ --color [auto|off|on] Enable or disable colored error messages534 \\ --color [auto|off|on] Enable or disable colored error messages
535 \\ --stdin Format code from stdin
530 \\536 \\
531 \\537 \\
532;538;
...@@ -538,6 +544,7 @@ const args_fmt_spec = []Flag{...@@ -538,6 +544,7 @@ const args_fmt_spec = []Flag{
538 "off",544 "off",
539 "on",545 "on",
540 }),546 }),
547 Flag.Bool("--stdin"),
541};548};
542549
543const Fmt = struct {550const Fmt = struct {
...@@ -579,11 +586,6 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {...@@ -579,11 +586,6 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
579 os.exit(0);586 os.exit(0);
580 }587 }
581588
582 if (flags.positionals.len == 0) {
583 try stderr.write("expected at least one source file argument\n");
584 os.exit(1);
585 }
586
587 const color = blk: {589 const color = blk: {
588 if (flags.single("color")) |color_flag| {590 if (flags.single("color")) |color_flag| {
589 if (mem.eql(u8, color_flag, "auto")) {591 if (mem.eql(u8, color_flag, "auto")) {
...@@ -598,6 +600,44 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {...@@ -598,6 +600,44 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
598 }600 }
599 };601 };
600602
603 if (flags.present("stdin")) {
604 if (flags.positionals.len != 0) {
605 try stderr.write("cannot use --stdin with positional arguments\n");
606 os.exit(1);
607 }
608
609 var stdin_file = try io.getStdIn();
610 var stdin = io.FileInStream.init(&stdin_file);
611
612 const source_code = try stdin.stream.readAllAlloc(allocator, @maxValue(usize));
613 defer allocator.free(source_code);
614
615 var tree = std.zig.parse(allocator, source_code) catch |err| {
616 try stderr.print("error parsing stdin: {}\n", err);
617 os.exit(1);
618 };
619 defer tree.deinit();
620
621 var error_it = tree.errors.iterator(0);
622 while (error_it.next()) |parse_error| {
623 const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
624 defer allocator.destroy(msg);
625
626 try errmsg.printToFile(&stderr_file, msg, color);
627 }
628 if (tree.errors.len != 0) {
629 os.exit(1);
630 }
631
632 _ = try std.zig.render(allocator, stdout, &tree);
633 return;
634 }
635
636 if (flags.positionals.len == 0) {
637 try stderr.write("expected at least one source file argument\n");
638 os.exit(1);
639 }
640
601 var fmt = Fmt{641 var fmt = Fmt{
602 .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator),642 .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator),
603 .queue = std.LinkedList([]const u8).init(),643 .queue = std.LinkedList([]const u8).init(),
src-self-hosted/module.zig deleted-579
...@@ -1,579 +0,0 @@
1const std = @import("std");
2const os = std.os;
3const io = std.io;
4const mem = std.mem;
5const Allocator = mem.Allocator;
6const Buffer = std.Buffer;
7const llvm = @import("llvm.zig");
8const c = @import("c.zig");
9const builtin = @import("builtin");
10const Target = @import("target.zig").Target;
11const warn = std.debug.warn;
12const Token = std.zig.Token;
13const ArrayList = std.ArrayList;
14const errmsg = @import("errmsg.zig");
15const ast = std.zig.ast;
16const event = std.event;
17const assert = std.debug.assert;
18
19pub const Module = struct {
20 loop: *event.Loop,
21 name: Buffer,
22 root_src_path: ?[]const u8,
23 module: llvm.ModuleRef,
24 context: llvm.ContextRef,
25 builder: llvm.BuilderRef,
26 target: Target,
27 build_mode: builtin.Mode,
28 zig_lib_dir: []const u8,
29
30 version_major: u32,
31 version_minor: u32,
32 version_patch: u32,
33
34 linker_script: ?[]const u8,
35 cache_dir: []const u8,
36 libc_lib_dir: ?[]const u8,
37 libc_static_lib_dir: ?[]const u8,
38 libc_include_dir: ?[]const u8,
39 msvc_lib_dir: ?[]const u8,
40 kernel32_lib_dir: ?[]const u8,
41 dynamic_linker: ?[]const u8,
42 out_h_path: ?[]const u8,
43
44 is_test: bool,
45 each_lib_rpath: bool,
46 strip: bool,
47 is_static: bool,
48 linker_rdynamic: bool,
49
50 clang_argv: []const []const u8,
51 llvm_argv: []const []const u8,
52 lib_dirs: []const []const u8,
53 rpath_list: []const []const u8,
54 assembly_files: []const []const u8,
55 link_objects: []const []const u8,
56
57 windows_subsystem_windows: bool,
58 windows_subsystem_console: bool,
59
60 link_libs_list: ArrayList(*LinkLib),
61 libc_link_lib: ?*LinkLib,
62
63 err_color: errmsg.Color,
64
65 verbose_tokenize: bool,
66 verbose_ast_tree: bool,
67 verbose_ast_fmt: bool,
68 verbose_cimport: bool,
69 verbose_ir: bool,
70 verbose_llvm_ir: bool,
71 verbose_link: bool,
72
73 darwin_frameworks: []const []const u8,
74 darwin_version_min: DarwinVersionMin,
75
76 test_filters: []const []const u8,
77 test_name_prefix: ?[]const u8,
78
79 emit_file_type: Emit,
80
81 kind: Kind,
82
83 link_out_file: ?[]const u8,
84 events: *event.Channel(Event),
85
86 exported_symbol_names: event.Locked(Decl.Table),
87
88 // TODO handle some of these earlier and report them in a way other than error codes
89 pub const BuildError = error{
90 OutOfMemory,
91 EndOfStream,
92 BadFd,
93 Io,
94 IsDir,
95 Unexpected,
96 SystemResources,
97 SharingViolation,
98 PathAlreadyExists,
99 FileNotFound,
100 AccessDenied,
101 PipeBusy,
102 FileTooBig,
103 SymLinkLoop,
104 ProcessFdQuotaExceeded,
105 NameTooLong,
106 SystemFdQuotaExceeded,
107 NoDevice,
108 PathNotFound,
109 NoSpaceLeft,
110 NotDir,
111 FileSystem,
112 OperationAborted,
113 IoPending,
114 BrokenPipe,
115 WouldBlock,
116 FileClosed,
117 DestinationAddressRequired,
118 DiskQuota,
119 InputOutput,
120 NoStdHandles,
121 Overflow,
122 NotSupported,
123 };
124
125 pub const Event = union(enum) {
126 Ok,
127 Fail: []errmsg.Msg,
128 Error: BuildError,
129 };
130
131 pub const DarwinVersionMin = union(enum) {
132 None,
133 MacOS: []const u8,
134 Ios: []const u8,
135 };
136
137 pub const Kind = enum {
138 Exe,
139 Lib,
140 Obj,
141 };
142
143 pub const LinkLib = struct {
144 name: []const u8,
145 path: ?[]const u8,
146
147 /// the list of symbols we depend on from this lib
148 symbols: ArrayList([]u8),
149 provided_explicitly: bool,
150 };
151
152 pub const Emit = enum {
153 Binary,
154 Assembly,
155 LlvmIr,
156 };
157
158 pub fn create(
159 loop: *event.Loop,
160 name: []const u8,
161 root_src_path: ?[]const u8,
162 target: *const Target,
163 kind: Kind,
164 build_mode: builtin.Mode,
165 zig_lib_dir: []const u8,
166 cache_dir: []const u8,
167 ) !*Module {
168 var name_buffer = try Buffer.init(loop.allocator, name);
169 errdefer name_buffer.deinit();
170
171 const context = c.LLVMContextCreate() orelse return error.OutOfMemory;
172 errdefer c.LLVMContextDispose(context);
173
174 const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory;
175 errdefer c.LLVMDisposeModule(module);
176
177 const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory;
178 errdefer c.LLVMDisposeBuilder(builder);
179
180 const events = try event.Channel(Event).create(loop, 0);
181 errdefer events.destroy();
182
183 return loop.allocator.create(Module{
184 .loop = loop,
185 .events = events,
186 .name = name_buffer,
187 .root_src_path = root_src_path,
188 .module = module,
189 .context = context,
190 .builder = builder,
191 .target = target.*,
192 .kind = kind,
193 .build_mode = build_mode,
194 .zig_lib_dir = zig_lib_dir,
195 .cache_dir = cache_dir,
196
197 .version_major = 0,
198 .version_minor = 0,
199 .version_patch = 0,
200
201 .verbose_tokenize = false,
202 .verbose_ast_tree = false,
203 .verbose_ast_fmt = false,
204 .verbose_cimport = false,
205 .verbose_ir = false,
206 .verbose_llvm_ir = false,
207 .verbose_link = false,
208
209 .linker_script = null,
210 .libc_lib_dir = null,
211 .libc_static_lib_dir = null,
212 .libc_include_dir = null,
213 .msvc_lib_dir = null,
214 .kernel32_lib_dir = null,
215 .dynamic_linker = null,
216 .out_h_path = null,
217 .is_test = false,
218 .each_lib_rpath = false,
219 .strip = false,
220 .is_static = false,
221 .linker_rdynamic = false,
222 .clang_argv = [][]const u8{},
223 .llvm_argv = [][]const u8{},
224 .lib_dirs = [][]const u8{},
225 .rpath_list = [][]const u8{},
226 .assembly_files = [][]const u8{},
227 .link_objects = [][]const u8{},
228 .windows_subsystem_windows = false,
229 .windows_subsystem_console = false,
230 .link_libs_list = ArrayList(*LinkLib).init(loop.allocator),
231 .libc_link_lib = null,
232 .err_color = errmsg.Color.Auto,
233 .darwin_frameworks = [][]const u8{},
234 .darwin_version_min = DarwinVersionMin.None,
235 .test_filters = [][]const u8{},
236 .test_name_prefix = null,
237 .emit_file_type = Emit.Binary,
238 .link_out_file = null,
239 .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
240 });
241 }
242
243 fn dump(self: *Module) void {
244 c.LLVMDumpModule(self.module);
245 }
246
247 pub fn destroy(self: *Module) void {
248 self.events.destroy();
249 c.LLVMDisposeBuilder(self.builder);
250 c.LLVMDisposeModule(self.module);
251 c.LLVMContextDispose(self.context);
252 self.name.deinit();
253
254 self.a().destroy(self);
255 }
256
257 pub fn build(self: *Module) !void {
258 if (self.llvm_argv.len != 0) {
259 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{
260 [][]const u8{"zig (LLVM option parsing)"},
261 self.llvm_argv,
262 });
263 defer c_compatible_args.deinit();
264 // TODO this sets global state
265 c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
266 }
267
268 _ = try async<self.a()> self.buildAsync();
269 }
270
271 async fn buildAsync(self: *Module) void {
272 while (true) {
273 // TODO directly awaiting async should guarantee memory allocation elision
274 // TODO also async before suspending should guarantee memory allocation elision
275 (await (async self.addRootSrc() catch unreachable)) catch |err| {
276 await (async self.events.put(Event{ .Error = err }) catch unreachable);
277 return;
278 };
279 await (async self.events.put(Event.Ok) catch unreachable);
280 // for now we stop after 1
281 return;
282 }
283 }
284
285 async fn addRootSrc(self: *Module) !void {
286 const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
287 // TODO async/await os.path.real
288 const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| {
289 try printError("unable to get real path '{}': {}", root_src_path, err);
290 return err;
291 };
292 errdefer self.a().free(root_src_real_path);
293
294 // TODO async/await readFileAlloc()
295 const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| {
296 try printError("unable to open '{}': {}", root_src_real_path, err);
297 return err;
298 };
299 errdefer self.a().free(source_code);
300
301 var parsed_file = ParsedFile{
302 .tree = try std.zig.parse(self.a(), source_code),
303 .realpath = root_src_real_path,
304 };
305 errdefer parsed_file.tree.deinit();
306
307 const tree = &parsed_file.tree;
308
309 // create empty struct for it
310 const decls = try Scope.Decls.create(self.a(), null);
311 errdefer decls.destroy();
312
313 var it = tree.root_node.decls.iterator(0);
314 while (it.next()) |decl_ptr| {
315 const decl = decl_ptr.*;
316 switch (decl.id) {
317 ast.Node.Id.Comptime => @panic("TODO"),
318 ast.Node.Id.VarDecl => @panic("TODO"),
319 ast.Node.Id.FnProto => {
320 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
321
322 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
323 @panic("TODO add compile error");
324 //try self.addCompileError(
325 // &parsed_file,
326 // fn_proto.fn_token,
327 // fn_proto.fn_token + 1,
328 // "missing function name",
329 //);
330 continue;
331 };
332
333 const fn_decl = try self.a().create(Decl.Fn{
334 .base = Decl{
335 .id = Decl.Id.Fn,
336 .name = name,
337 .visib = parseVisibToken(tree, fn_proto.visib_token),
338 .resolution = Decl.Resolution.Unresolved,
339 },
340 .value = Decl.Fn.Val{ .Unresolved = {} },
341 .fn_proto = fn_proto,
342 });
343 errdefer self.a().destroy(fn_decl);
344
345 // TODO make this parallel
346 try await try async self.addTopLevelDecl(tree, &fn_decl.base);
347 },
348 ast.Node.Id.TestDecl => @panic("TODO"),
349 else => unreachable,
350 }
351 }
352 }
353
354 async fn addTopLevelDecl(self: *Module, tree: *ast.Tree, decl: *Decl) !void {
355 const is_export = decl.isExported(tree);
356
357 {
358 const exported_symbol_names = await try async self.exported_symbol_names.acquire();
359 defer exported_symbol_names.release();
360
361 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
362 @panic("TODO report compile error");
363 }
364 }
365 }
366
367 pub fn link(self: *Module, out_file: ?[]const u8) !void {
368 warn("TODO link");
369 return error.Todo;
370 }
371
372 pub fn addLinkLib(self: *Module, name: []const u8, provided_explicitly: bool) !*LinkLib {
373 const is_libc = mem.eql(u8, name, "c");
374
375 if (is_libc) {
376 if (self.libc_link_lib) |libc_link_lib| {
377 return libc_link_lib;
378 }
379 }
380
381 for (self.link_libs_list.toSliceConst()) |existing_lib| {
382 if (mem.eql(u8, name, existing_lib.name)) {
383 return existing_lib;
384 }
385 }
386
387 const link_lib = try self.a().create(LinkLib{
388 .name = name,
389 .path = null,
390 .provided_explicitly = provided_explicitly,
391 .symbols = ArrayList([]u8).init(self.a()),
392 });
393 try self.link_libs_list.append(link_lib);
394 if (is_libc) {
395 self.libc_link_lib = link_lib;
396 }
397 return link_lib;
398 }
399
400 fn a(self: Module) *mem.Allocator {
401 return self.loop.allocator;
402 }
403};
404
405fn printError(comptime format: []const u8, args: ...) !void {
406 var stderr_file = try std.io.getStdErr();
407 var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
408 const out_stream = &stderr_file_out_stream.stream;
409 try out_stream.print(format, args);
410}
411
412fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
413 if (optional_token_index) |token_index| {
414 const token = tree.tokens.at(token_index);
415 assert(token.id == Token.Id.Keyword_pub);
416 return Visib.Pub;
417 } else {
418 return Visib.Private;
419 }
420}
421
422pub const Scope = struct {
423 id: Id,
424 parent: ?*Scope,
425
426 pub const Id = enum {
427 Decls,
428 Block,
429 };
430
431 pub const Decls = struct {
432 base: Scope,
433 table: Decl.Table,
434
435 pub fn create(a: *Allocator, parent: ?*Scope) !*Decls {
436 const self = try a.create(Decls{
437 .base = Scope{
438 .id = Id.Decls,
439 .parent = parent,
440 },
441 .table = undefined,
442 });
443 errdefer a.destroy(self);
444
445 self.table = Decl.Table.init(a);
446 errdefer self.table.deinit();
447
448 return self;
449 }
450
451 pub fn destroy(self: *Decls) void {
452 self.table.deinit();
453 self.table.allocator.destroy(self);
454 self.* = undefined;
455 }
456 };
457
458 pub const Block = struct {
459 base: Scope,
460 };
461};
462
463pub const Visib = enum {
464 Private,
465 Pub,
466};
467
468pub const Decl = struct {
469 id: Id,
470 name: []const u8,
471 visib: Visib,
472 resolution: Resolution,
473
474 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
475
476 pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
477 switch (base.id) {
478 Id.Fn => {
479 const fn_decl = @fieldParentPtr(Fn, "base", base);
480 return fn_decl.isExported(tree);
481 },
482 else => return false,
483 }
484 }
485
486 pub const Resolution = enum {
487 Unresolved,
488 InProgress,
489 Invalid,
490 Ok,
491 };
492
493 pub const Id = enum {
494 Var,
495 Fn,
496 CompTime,
497 };
498
499 pub const Var = struct {
500 base: Decl,
501 };
502
503 pub const Fn = struct {
504 base: Decl,
505 value: Val,
506 fn_proto: *const ast.Node.FnProto,
507
508 // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
509 pub const Val = union {
510 Unresolved: void,
511 Ok: *Value.Fn,
512 };
513
514 pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
515 return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {
516 const token = tree.tokens.at(tok_index);
517 break :x switch (token.id) {
518 Token.Id.Extern => tree.tokenSlicePtr(token),
519 else => null,
520 };
521 } else null;
522 }
523
524 pub fn isExported(self: Fn, tree: *ast.Tree) bool {
525 if (self.fn_proto.extern_export_inline_token) |tok_index| {
526 const token = tree.tokens.at(tok_index);
527 return token.id == Token.Id.Keyword_export;
528 } else {
529 return false;
530 }
531 }
532 };
533
534 pub const CompTime = struct {
535 base: Decl,
536 };
537};
538
539pub const Value = struct {
540 pub const Fn = struct {};
541};
542
543pub const Type = struct {
544 id: Id,
545
546 pub const Id = enum {
547 Type,
548 Void,
549 Bool,
550 NoReturn,
551 Int,
552 Float,
553 Pointer,
554 Array,
555 Struct,
556 ComptimeFloat,
557 ComptimeInt,
558 Undefined,
559 Null,
560 Optional,
561 ErrorUnion,
562 ErrorSet,
563 Enum,
564 Union,
565 Fn,
566 Opaque,
567 Promise,
568 };
569
570 pub const Struct = struct {
571 base: Type,
572 decls: *Scope.Decls,
573 };
574};
575
576pub const ParsedFile = struct {
577 tree: ast.Tree,
578 realpath: []const u8,
579};
src-self-hosted/parsed_file.zig created+6
...@@ -0,0 +1,6 @@
1const ast = @import("std").zig.ast;
2
3pub const ParsedFile = struct {
4 tree: ast.Tree,
5 realpath: []const u8,
6};
src-self-hosted/scope.zig+224-6
...@@ -1,16 +1,234 @@...@@ -1,16 +1,234 @@
1const std = @import("std");
2const Allocator = mem.Allocator;
3const Decl = @import("decl.zig").Decl;
4const Compilation = @import("compilation.zig").Compilation;
5const mem = std.mem;
6const ast = std.zig.ast;
7const Value = @import("value.zig").Value;
8const ir = @import("ir.zig");
9
1pub const Scope = struct {10pub const Scope = struct {
2 id: Id,11 id: Id,
3 parent: *Scope,12 parent: ?*Scope,
13 ref_count: usize,
14
15 pub fn ref(base: *Scope) void {
16 base.ref_count += 1;
17 }
18
19 pub fn deref(base: *Scope, comp: *Compilation) void {
20 base.ref_count -= 1;
21 if (base.ref_count == 0) {
22 if (base.parent) |parent| parent.deref(comp);
23 switch (base.id) {
24 Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(),
25 Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp),
26 Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp),
27 Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
28 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),
29 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),
30 }
31 }
32 }
33
34 pub fn findFnDef(base: *Scope) ?*FnDef {
35 var scope = base;
36 while (true) {
37 switch (scope.id) {
38 Id.FnDef => return @fieldParentPtr(FnDef, "base", base),
39 Id.Decls => return null,
40
41 Id.Block,
42 Id.Defer,
43 Id.DeferExpr,
44 Id.CompTime,
45 => scope = scope.parent orelse return null,
46 }
47 }
48 }
449
5 pub const Id = enum {50 pub const Id = enum {
6 Decls,51 Decls,
7 Block,52 Block,
8 Defer,
9 DeferExpr,
10 VarDecl,
11 CImport,
12 Loop,
13 FnDef,53 FnDef,
14 CompTime,54 CompTime,
55 Defer,
56 DeferExpr,
57 };
58
59 pub const Decls = struct {
60 base: Scope,
61 table: Decl.Table,
62
63 /// Creates a Decls scope with 1 reference
64 pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls {
65 const self = try comp.a().create(Decls{
66 .base = Scope{
67 .id = Id.Decls,
68 .parent = parent,
69 .ref_count = 1,
70 },
71 .table = undefined,
72 });
73 errdefer comp.a().destroy(self);
74
75 self.table = Decl.Table.init(comp.a());
76 errdefer self.table.deinit();
77
78 if (parent) |p| p.ref();
79
80 return self;
81 }
82
83 pub fn destroy(self: *Decls) void {
84 self.table.deinit();
85 self.table.allocator.destroy(self);
86 }
87 };
88
89 pub const Block = struct {
90 base: Scope,
91 incoming_values: std.ArrayList(*ir.Instruction),
92 incoming_blocks: std.ArrayList(*ir.BasicBlock),
93 end_block: *ir.BasicBlock,
94 is_comptime: *ir.Instruction,
95
96 /// Creates a Block scope with 1 reference
97 pub fn create(comp: *Compilation, parent: ?*Scope) !*Block {
98 const self = try comp.a().create(Block{
99 .base = Scope{
100 .id = Id.Block,
101 .parent = parent,
102 .ref_count = 1,
103 },
104 .incoming_values = undefined,
105 .incoming_blocks = undefined,
106 .end_block = undefined,
107 .is_comptime = undefined,
108 });
109 errdefer comp.a().destroy(self);
110
111 if (parent) |p| p.ref();
112 return self;
113 }
114
115 pub fn destroy(self: *Block, comp: *Compilation) void {
116 comp.a().destroy(self);
117 }
118 };
119
120 pub const FnDef = struct {
121 base: Scope,
122
123 /// This reference is not counted so that the scope can get destroyed with the function
124 fn_val: *Value.Fn,
125
126 /// Creates a FnDef scope with 1 reference
127 /// Must set the fn_val later
128 pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef {
129 const self = try comp.a().create(FnDef{
130 .base = Scope{
131 .id = Id.FnDef,
132 .parent = parent,
133 .ref_count = 1,
134 },
135 .fn_val = undefined,
136 });
137
138 if (parent) |p| p.ref();
139
140 return self;
141 }
142
143 pub fn destroy(self: *FnDef, comp: *Compilation) void {
144 comp.a().destroy(self);
145 }
146 };
147
148 pub const CompTime = struct {
149 base: Scope,
150
151 /// Creates a CompTime scope with 1 reference
152 pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime {
153 const self = try comp.a().create(CompTime{
154 .base = Scope{
155 .id = Id.CompTime,
156 .parent = parent,
157 .ref_count = 1,
158 },
159 });
160
161 if (parent) |p| p.ref();
162 return self;
163 }
164
165 pub fn destroy(self: *CompTime, comp: *Compilation) void {
166 comp.a().destroy(self);
167 }
168 };
169
170 pub const Defer = struct {
171 base: Scope,
172 defer_expr_scope: *DeferExpr,
173 kind: Kind,
174
175 pub const Kind = enum {
176 ScopeExit,
177 ErrorExit,
178 };
179
180 /// Creates a Defer scope with 1 reference
181 pub fn create(
182 comp: *Compilation,
183 parent: ?*Scope,
184 kind: Kind,
185 defer_expr_scope: *DeferExpr,
186 ) !*Defer {
187 const self = try comp.a().create(Defer{
188 .base = Scope{
189 .id = Id.Defer,
190 .parent = parent,
191 .ref_count = 1,
192 },
193 .defer_expr_scope = defer_expr_scope,
194 .kind = kind,
195 });
196 errdefer comp.a().destroy(self);
197
198 defer_expr_scope.base.ref();
199
200 if (parent) |p| p.ref();
201 return self;
202 }
203
204 pub fn destroy(self: *Defer, comp: *Compilation) void {
205 self.defer_expr_scope.base.deref(comp);
206 comp.a().destroy(self);
207 }
208 };
209
210 pub const DeferExpr = struct {
211 base: Scope,
212 expr_node: *ast.Node,
213
214 /// Creates a DeferExpr scope with 1 reference
215 pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr {
216 const self = try comp.a().create(DeferExpr{
217 .base = Scope{
218 .id = Id.DeferExpr,
219 .parent = parent,
220 .ref_count = 1,
221 },
222 .expr_node = expr_node,
223 });
224 errdefer comp.a().destroy(self);
225
226 if (parent) |p| p.ref();
227 return self;
228 }
229
230 pub fn destroy(self: *DeferExpr, comp: *Compilation) void {
231 comp.a().destroy(self);
232 }
15 };233 };
16};234};
src-self-hosted/test.zig created+168
...@@ -0,0 +1,168 @@
1const std = @import("std");
2const mem = std.mem;
3const builtin = @import("builtin");
4const Target = @import("target.zig").Target;
5const Compilation = @import("compilation.zig").Compilation;
6const introspect = @import("introspect.zig");
7const assertOrPanic = std.debug.assertOrPanic;
8const errmsg = @import("errmsg.zig");
9const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
10
11test "compile errors" {
12 var ctx: TestContext = undefined;
13 try ctx.init();
14 defer ctx.deinit();
15
16 try @import("../test/stage2/compile_errors.zig").addCases(&ctx);
17
18 try ctx.run();
19}
20
21const file1 = "1.zig";
22const allocator = std.heap.c_allocator;
23
24pub const TestContext = struct {
25 loop: std.event.Loop,
26 event_loop_local: EventLoopLocal,
27 zig_lib_dir: []u8,
28 zig_cache_dir: []u8,
29 file_index: std.atomic.Int(usize),
30 group: std.event.Group(error!void),
31 any_err: error!void,
32
33 const tmp_dir_name = "stage2_test_tmp";
34
35 fn init(self: *TestContext) !void {
36 self.* = TestContext{
37 .any_err = {},
38 .loop = undefined,
39 .event_loop_local = undefined,
40 .zig_lib_dir = undefined,
41 .zig_cache_dir = undefined,
42 .group = undefined,
43 .file_index = std.atomic.Int(usize).init(0),
44 };
45
46 try self.loop.initMultiThreaded(allocator);
47 errdefer self.loop.deinit();
48
49 self.event_loop_local = EventLoopLocal.init(&self.loop);
50 errdefer self.event_loop_local.deinit();
51
52 self.group = std.event.Group(error!void).init(&self.loop);
53 errdefer self.group.cancelAll();
54
55 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
56 errdefer allocator.free(self.zig_lib_dir);
57
58 self.zig_cache_dir = try introspect.resolveZigCacheDir(allocator);
59 errdefer allocator.free(self.zig_cache_dir);
60
61 try std.os.makePath(allocator, tmp_dir_name);
62 errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {};
63 }
64
65 fn deinit(self: *TestContext) void {
66 std.os.deleteTree(allocator, tmp_dir_name) catch {};
67 allocator.free(self.zig_cache_dir);
68 allocator.free(self.zig_lib_dir);
69 self.event_loop_local.deinit();
70 self.loop.deinit();
71 }
72
73 fn run(self: *TestContext) !void {
74 const handle = try self.loop.call(waitForGroup, self);
75 defer cancel handle;
76 self.loop.run();
77 return self.any_err;
78 }
79
80 async fn waitForGroup(self: *TestContext) void {
81 self.any_err = await (async self.group.wait() catch unreachable);
82 }
83
84 fn testCompileError(
85 self: *TestContext,
86 source: []const u8,
87 path: []const u8,
88 line: usize,
89 column: usize,
90 msg: []const u8,
91 ) !void {
92 var file_index_buf: [20]u8 = undefined;
93 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
94 const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
95
96 if (std.os.path.dirname(file1_path)) |dirname| {
97 try std.os.makePath(allocator, dirname);
98 }
99
100 // TODO async I/O
101 try std.io.writeFile(allocator, file1_path, source);
102
103 var comp = try Compilation.create(
104 &self.event_loop_local,
105 "test",
106 file1_path,
107 Target.Native,
108 Compilation.Kind.Obj,
109 builtin.Mode.Debug,
110 self.zig_lib_dir,
111 self.zig_cache_dir,
112 );
113 errdefer comp.destroy();
114
115 try comp.build();
116
117 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
118 }
119
120 async fn getModuleEvent(
121 comp: *Compilation,
122 source: []const u8,
123 path: []const u8,
124 line: usize,
125 column: usize,
126 text: []const u8,
127 ) !void {
128 defer comp.destroy();
129 const build_event = await (async comp.events.get() catch unreachable);
130
131 switch (build_event) {
132 Compilation.Event.Ok => {
133 @panic("build incorrectly succeeded");
134 },
135 Compilation.Event.Error => |err| {
136 @panic("build incorrectly failed");
137 },
138 Compilation.Event.Fail => |msgs| {
139 assertOrPanic(msgs.len != 0);
140 for (msgs) |msg| {
141 if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) {
142 const first_token = msg.tree.tokens.at(msg.span.first);
143 const last_token = msg.tree.tokens.at(msg.span.first);
144 const start_loc = msg.tree.tokenLocationPtr(0, first_token);
145 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
146 return;
147 }
148 }
149 }
150 std.debug.warn(
151 "\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n",
152 source,
153 path,
154 line,
155 column,
156 text,
157 );
158 std.debug.warn("\n====found:========\n");
159 var stderr = try std.io.getStdErr();
160 for (msgs) |msg| {
161 try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto);
162 }
163 std.debug.warn("============\n");
164 return error.TestFailed;
165 },
166 }
167 }
168};
src-self-hosted/type.zig created+442
...@@ -0,0 +1,442 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Scope = @import("scope.zig").Scope;
4const Compilation = @import("compilation.zig").Compilation;
5const Value = @import("value.zig").Value;
6const llvm = @import("llvm.zig");
7const ObjectFile = @import("codegen.zig").ObjectFile;
8
9pub const Type = struct {
10 base: Value,
11 id: Id,
12
13 pub const Id = builtin.TypeId;
14
15 pub fn destroy(base: *Type, comp: *Compilation) void {
16 switch (base.id) {
17 Id.Struct => @fieldParentPtr(Struct, "base", base).destroy(comp),
18 Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
19 Id.Type => @fieldParentPtr(MetaType, "base", base).destroy(comp),
20 Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp),
21 Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
22 Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
23 Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp),
24 Id.Float => @fieldParentPtr(Float, "base", base).destroy(comp),
25 Id.Pointer => @fieldParentPtr(Pointer, "base", base).destroy(comp),
26 Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp),
27 Id.ComptimeFloat => @fieldParentPtr(ComptimeFloat, "base", base).destroy(comp),
28 Id.ComptimeInt => @fieldParentPtr(ComptimeInt, "base", base).destroy(comp),
29 Id.Undefined => @fieldParentPtr(Undefined, "base", base).destroy(comp),
30 Id.Null => @fieldParentPtr(Null, "base", base).destroy(comp),
31 Id.Optional => @fieldParentPtr(Optional, "base", base).destroy(comp),
32 Id.ErrorUnion => @fieldParentPtr(ErrorUnion, "base", base).destroy(comp),
33 Id.ErrorSet => @fieldParentPtr(ErrorSet, "base", base).destroy(comp),
34 Id.Enum => @fieldParentPtr(Enum, "base", base).destroy(comp),
35 Id.Union => @fieldParentPtr(Union, "base", base).destroy(comp),
36 Id.Namespace => @fieldParentPtr(Namespace, "base", base).destroy(comp),
37 Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp),
38 Id.BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(comp),
39 Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(comp),
40 Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(comp),
41 Id.Promise => @fieldParentPtr(Promise, "base", base).destroy(comp),
42 }
43 }
44
45 pub fn getLlvmType(base: *Type, ofile: *ObjectFile) (error{OutOfMemory}!llvm.TypeRef) {
46 switch (base.id) {
47 Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(ofile),
48 Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(ofile),
49 Id.Type => unreachable,
50 Id.Void => unreachable,
51 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(ofile),
52 Id.NoReturn => unreachable,
53 Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(ofile),
54 Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(ofile),
55 Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(ofile),
56 Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(ofile),
57 Id.ComptimeFloat => unreachable,
58 Id.ComptimeInt => unreachable,
59 Id.Undefined => unreachable,
60 Id.Null => unreachable,
61 Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(ofile),
62 Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(ofile),
63 Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(ofile),
64 Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(ofile),
65 Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(ofile),
66 Id.Namespace => unreachable,
67 Id.Block => unreachable,
68 Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(ofile),
69 Id.ArgTuple => unreachable,
70 Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(ofile),
71 Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(ofile),
72 }
73 }
74
75 pub fn dump(base: *const Type) void {
76 std.debug.warn("{}", @tagName(base.id));
77 }
78
79 pub fn getAbiAlignment(base: *Type, comp: *Compilation) u32 {
80 @panic("TODO getAbiAlignment");
81 }
82
83 pub const Struct = struct {
84 base: Type,
85 decls: *Scope.Decls,
86
87 pub fn destroy(self: *Struct, comp: *Compilation) void {
88 comp.a().destroy(self);
89 }
90
91 pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef {
92 @panic("TODO");
93 }
94 };
95
96 pub const Fn = struct {
97 base: Type,
98 return_type: *Type,
99 params: []Param,
100 is_var_args: bool,
101
102 pub const Param = struct {
103 is_noalias: bool,
104 typeof: *Type,
105 };
106
107 pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
108 const result = try comp.a().create(Fn{
109 .base = Type{
110 .base = Value{
111 .id = Value.Id.Type,
112 .typeof = &MetaType.get(comp).base,
113 .ref_count = std.atomic.Int(usize).init(1),
114 },
115 .id = builtin.TypeId.Fn,
116 },
117 .return_type = return_type,
118 .params = params,
119 .is_var_args = is_var_args,
120 });
121 errdefer comp.a().destroy(result);
122
123 result.return_type.base.ref();
124 for (result.params) |param| {
125 param.typeof.base.ref();
126 }
127 return result;
128 }
129
130 pub fn destroy(self: *Fn, comp: *Compilation) void {
131 self.return_type.base.deref(comp);
132 for (self.params) |param| {
133 param.typeof.base.deref(comp);
134 }
135 comp.a().destroy(self);
136 }
137
138 pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef {
139 const llvm_return_type = switch (self.return_type.id) {
140 Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory,
141 else => try self.return_type.getLlvmType(ofile),
142 };
143 const llvm_param_types = try ofile.a().alloc(llvm.TypeRef, self.params.len);
144 defer ofile.a().free(llvm_param_types);
145 for (llvm_param_types) |*llvm_param_type, i| {
146 llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile);
147 }
148
149 return llvm.FunctionType(
150 llvm_return_type,
151 llvm_param_types.ptr,
152 @intCast(c_uint, llvm_param_types.len),
153 @boolToInt(self.is_var_args),
154 ) orelse error.OutOfMemory;
155 }
156 };
157
158 pub const MetaType = struct {
159 base: Type,
160 value: *Type,
161
162 /// Adds 1 reference to the resulting type
163 pub fn get(comp: *Compilation) *MetaType {
164 comp.meta_type.base.base.ref();
165 return comp.meta_type;
166 }
167
168 pub fn destroy(self: *MetaType, comp: *Compilation) void {
169 comp.a().destroy(self);
170 }
171 };
172
173 pub const Void = struct {
174 base: Type,
175
176 /// Adds 1 reference to the resulting type
177 pub fn get(comp: *Compilation) *Void {
178 comp.void_type.base.base.ref();
179 return comp.void_type;
180 }
181
182 pub fn destroy(self: *Void, comp: *Compilation) void {
183 comp.a().destroy(self);
184 }
185 };
186
187 pub const Bool = struct {
188 base: Type,
189
190 /// Adds 1 reference to the resulting type
191 pub fn get(comp: *Compilation) *Bool {
192 comp.bool_type.base.base.ref();
193 return comp.bool_type;
194 }
195
196 pub fn destroy(self: *Bool, comp: *Compilation) void {
197 comp.a().destroy(self);
198 }
199
200 pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef {
201 @panic("TODO");
202 }
203 };
204
205 pub const NoReturn = struct {
206 base: Type,
207
208 /// Adds 1 reference to the resulting type
209 pub fn get(comp: *Compilation) *NoReturn {
210 comp.noreturn_type.base.base.ref();
211 return comp.noreturn_type;
212 }
213
214 pub fn destroy(self: *NoReturn, comp: *Compilation) void {
215 comp.a().destroy(self);
216 }
217 };
218
219 pub const Int = struct {
220 base: Type,
221
222 pub fn destroy(self: *Int, comp: *Compilation) void {
223 comp.a().destroy(self);
224 }
225
226 pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef {
227 @panic("TODO");
228 }
229 };
230
231 pub const Float = struct {
232 base: Type,
233
234 pub fn destroy(self: *Float, comp: *Compilation) void {
235 comp.a().destroy(self);
236 }
237
238 pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef {
239 @panic("TODO");
240 }
241 };
242 pub const Pointer = struct {
243 base: Type,
244 mut: Mut,
245 vol: Vol,
246 size: Size,
247 alignment: u32,
248
249 pub const Mut = enum {
250 Mut,
251 Const,
252 };
253 pub const Vol = enum {
254 Non,
255 Volatile,
256 };
257 pub const Size = builtin.TypeInfo.Pointer.Size;
258
259 pub fn destroy(self: *Pointer, comp: *Compilation) void {
260 comp.a().destroy(self);
261 }
262
263 pub fn get(
264 comp: *Compilation,
265 elem_type: *Type,
266 mut: Mut,
267 vol: Vol,
268 size: Size,
269 alignment: u32,
270 ) *Pointer {
271 @panic("TODO get pointer");
272 }
273
274 pub fn getLlvmType(self: *Pointer, ofile: *ObjectFile) llvm.TypeRef {
275 @panic("TODO");
276 }
277 };
278
279 pub const Array = struct {
280 base: Type,
281
282 pub fn destroy(self: *Array, comp: *Compilation) void {
283 comp.a().destroy(self);
284 }
285
286 pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef {
287 @panic("TODO");
288 }
289 };
290
291 pub const ComptimeFloat = struct {
292 base: Type,
293
294 pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void {
295 comp.a().destroy(self);
296 }
297 };
298
299 pub const ComptimeInt = struct {
300 base: Type,
301
302 pub fn destroy(self: *ComptimeInt, comp: *Compilation) void {
303 comp.a().destroy(self);
304 }
305 };
306
307 pub const Undefined = struct {
308 base: Type,
309
310 pub fn destroy(self: *Undefined, comp: *Compilation) void {
311 comp.a().destroy(self);
312 }
313 };
314
315 pub const Null = struct {
316 base: Type,
317
318 pub fn destroy(self: *Null, comp: *Compilation) void {
319 comp.a().destroy(self);
320 }
321 };
322
323 pub const Optional = struct {
324 base: Type,
325
326 pub fn destroy(self: *Optional, comp: *Compilation) void {
327 comp.a().destroy(self);
328 }
329
330 pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef {
331 @panic("TODO");
332 }
333 };
334
335 pub const ErrorUnion = struct {
336 base: Type,
337
338 pub fn destroy(self: *ErrorUnion, comp: *Compilation) void {
339 comp.a().destroy(self);
340 }
341
342 pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef {
343 @panic("TODO");
344 }
345 };
346
347 pub const ErrorSet = struct {
348 base: Type,
349
350 pub fn destroy(self: *ErrorSet, comp: *Compilation) void {
351 comp.a().destroy(self);
352 }
353
354 pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef {
355 @panic("TODO");
356 }
357 };
358
359 pub const Enum = struct {
360 base: Type,
361
362 pub fn destroy(self: *Enum, comp: *Compilation) void {
363 comp.a().destroy(self);
364 }
365
366 pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef {
367 @panic("TODO");
368 }
369 };
370
371 pub const Union = struct {
372 base: Type,
373
374 pub fn destroy(self: *Union, comp: *Compilation) void {
375 comp.a().destroy(self);
376 }
377
378 pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef {
379 @panic("TODO");
380 }
381 };
382
383 pub const Namespace = struct {
384 base: Type,
385
386 pub fn destroy(self: *Namespace, comp: *Compilation) void {
387 comp.a().destroy(self);
388 }
389 };
390
391 pub const Block = struct {
392 base: Type,
393
394 pub fn destroy(self: *Block, comp: *Compilation) void {
395 comp.a().destroy(self);
396 }
397 };
398
399 pub const BoundFn = struct {
400 base: Type,
401
402 pub fn destroy(self: *BoundFn, comp: *Compilation) void {
403 comp.a().destroy(self);
404 }
405
406 pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef {
407 @panic("TODO");
408 }
409 };
410
411 pub const ArgTuple = struct {
412 base: Type,
413
414 pub fn destroy(self: *ArgTuple, comp: *Compilation) void {
415 comp.a().destroy(self);
416 }
417 };
418
419 pub const Opaque = struct {
420 base: Type,
421
422 pub fn destroy(self: *Opaque, comp: *Compilation) void {
423 comp.a().destroy(self);
424 }
425
426 pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef {
427 @panic("TODO");
428 }
429 };
430
431 pub const Promise = struct {
432 base: Type,
433
434 pub fn destroy(self: *Promise, comp: *Compilation) void {
435 comp.a().destroy(self);
436 }
437
438 pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef {
439 @panic("TODO");
440 }
441 };
442};
src-self-hosted/value.zig created+154
...@@ -0,0 +1,154 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Scope = @import("scope.zig").Scope;
4const Compilation = @import("compilation.zig").Compilation;
5
6/// Values are ref-counted, heap-allocated, and copy-on-write
7/// If there is only 1 ref then write need not copy
8pub const Value = struct {
9 id: Id,
10 typeof: *Type,
11 ref_count: std.atomic.Int(usize),
12
13 /// Thread-safe
14 pub fn ref(base: *Value) void {
15 _ = base.ref_count.incr();
16 }
17
18 /// Thread-safe
19 pub fn deref(base: *Value, comp: *Compilation) void {
20 if (base.ref_count.decr() == 1) {
21 base.typeof.base.deref(comp);
22 switch (base.id) {
23 Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp),
24 Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
25 Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp),
26 Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
27 Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
28 Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp),
29 }
30 }
31 }
32
33 pub fn getRef(base: *Value) *Value {
34 base.ref();
35 return base;
36 }
37
38 pub fn dump(base: *const Value) void {
39 std.debug.warn("{}", @tagName(base.id));
40 }
41
42 pub const Id = enum {
43 Type,
44 Fn,
45 Void,
46 Bool,
47 NoReturn,
48 Ptr,
49 };
50
51 pub const Type = @import("type.zig").Type;
52
53 pub const Fn = struct {
54 base: Value,
55
56 /// The main external name that is used in the .o file.
57 /// TODO https://github.com/ziglang/zig/issues/265
58 symbol_name: std.Buffer,
59
60 /// parent should be the top level decls or container decls
61 fndef_scope: *Scope.FnDef,
62
63 /// parent is scope for last parameter
64 child_scope: *Scope,
65
66 /// parent is child_scope
67 block_scope: *Scope.Block,
68
69 /// Creates a Fn value with 1 ref
70 /// Takes ownership of symbol_name
71 pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn {
72 const self = try comp.a().create(Fn{
73 .base = Value{
74 .id = Value.Id.Fn,
75 .typeof = &fn_type.base,
76 .ref_count = std.atomic.Int(usize).init(1),
77 },
78 .fndef_scope = fndef_scope,
79 .child_scope = &fndef_scope.base,
80 .block_scope = undefined,
81 .symbol_name = symbol_name,
82 });
83 fn_type.base.base.ref();
84 fndef_scope.fn_val = self;
85 fndef_scope.base.ref();
86 return self;
87 }
88
89 pub fn destroy(self: *Fn, comp: *Compilation) void {
90 self.fndef_scope.base.deref(comp);
91 self.symbol_name.deinit();
92 comp.a().destroy(self);
93 }
94 };
95
96 pub const Void = struct {
97 base: Value,
98
99 pub fn get(comp: *Compilation) *Void {
100 comp.void_value.base.ref();
101 return comp.void_value;
102 }
103
104 pub fn destroy(self: *Void, comp: *Compilation) void {
105 comp.a().destroy(self);
106 }
107 };
108
109 pub const Bool = struct {
110 base: Value,
111 x: bool,
112
113 pub fn get(comp: *Compilation, x: bool) *Bool {
114 if (x) {
115 comp.true_value.base.ref();
116 return comp.true_value;
117 } else {
118 comp.false_value.base.ref();
119 return comp.false_value;
120 }
121 }
122
123 pub fn destroy(self: *Bool, comp: *Compilation) void {
124 comp.a().destroy(self);
125 }
126 };
127
128 pub const NoReturn = struct {
129 base: Value,
130
131 pub fn get(comp: *Compilation) *NoReturn {
132 comp.noreturn_value.base.ref();
133 return comp.noreturn_value;
134 }
135
136 pub fn destroy(self: *NoReturn, comp: *Compilation) void {
137 comp.a().destroy(self);
138 }
139 };
140
141 pub const Ptr = struct {
142 base: Value,
143
144 pub const Mut = enum {
145 CompTimeConst,
146 CompTimeVar,
147 RunTime,
148 };
149
150 pub fn destroy(self: *Ptr, comp: *Compilation) void {
151 comp.a().destroy(self);
152 }
153 };
154};
src-self-hosted/visib.zig created+4
...@@ -0,0 +1,4 @@
1pub const Visib = enum {
2 Private,
3 Pub,
4};
src/all_types.hpp+5-6
...@@ -2003,12 +2003,6 @@ struct IrBasicBlock {...@@ -2003,12 +2003,6 @@ struct IrBasicBlock {
2003 IrInstruction *must_be_comptime_source_instr;2003 IrInstruction *must_be_comptime_source_instr;
2004};2004};
20052005
2006struct LVal {
2007 bool is_ptr;
2008 bool is_const;
2009 bool is_volatile;
2010};
2011
2012enum IrInstructionId {2006enum IrInstructionId {
2013 IrInstructionIdInvalid,2007 IrInstructionIdInvalid,
2014 IrInstructionIdBr,2008 IrInstructionIdBr,
...@@ -2970,6 +2964,11 @@ struct IrInstructionTypeName {...@@ -2970,6 +2964,11 @@ struct IrInstructionTypeName {
2970 IrInstruction *type_value;2964 IrInstruction *type_value;
2971};2965};
29722966
2967enum LVal {
2968 LValNone,
2969 LValPtr,
2970};
2971
2973struct IrInstructionDeclRef {2972struct IrInstructionDeclRef {
2974 IrInstruction base;2973 IrInstruction base;
29752974
src/analyze.cpp+20-3
...@@ -1430,10 +1430,10 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {...@@ -1430,10 +1430,10 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
1430 case TypeTableEntryIdBoundFn:1430 case TypeTableEntryIdBoundFn:
1431 case TypeTableEntryIdArgTuple:1431 case TypeTableEntryIdArgTuple:
1432 case TypeTableEntryIdPromise:1432 case TypeTableEntryIdPromise:
1433 case TypeTableEntryIdVoid:
1433 return false;1434 return false;
1434 case TypeTableEntryIdOpaque:1435 case TypeTableEntryIdOpaque:
1435 case TypeTableEntryIdUnreachable:1436 case TypeTableEntryIdUnreachable:
1436 case TypeTableEntryIdVoid:
1437 case TypeTableEntryIdBool:1437 case TypeTableEntryIdBool:
1438 return true;1438 return true;
1439 case TypeTableEntryIdInt:1439 case TypeTableEntryIdInt:
...@@ -1460,7 +1460,10 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {...@@ -1460,7 +1460,10 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
1460 case TypeTableEntryIdOptional:1460 case TypeTableEntryIdOptional:
1461 {1461 {
1462 TypeTableEntry *child_type = type_entry->data.maybe.child_type;1462 TypeTableEntry *child_type = type_entry->data.maybe.child_type;
1463 return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn;1463 if (child_type->id != TypeTableEntryIdPointer && child_type->id != TypeTableEntryIdFn) {
1464 return false;
1465 }
1466 return type_allowed_in_extern(g, child_type);
1464 }1467 }
1465 case TypeTableEntryIdEnum:1468 case TypeTableEntryIdEnum:
1466 return type_entry->data.enumeration.layout == ContainerLayoutExtern || type_entry->data.enumeration.layout == ContainerLayoutPacked;1469 return type_entry->data.enumeration.layout == ContainerLayoutExtern || type_entry->data.enumeration.layout == ContainerLayoutPacked;
...@@ -1637,7 +1640,10 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c...@@ -1637,7 +1640,10 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1637 fn_type_id.return_type = specified_return_type;1640 fn_type_id.return_type = specified_return_type;
1638 }1641 }
16391642
1640 if (!calling_convention_allows_zig_types(fn_type_id.cc) && !type_allowed_in_extern(g, fn_type_id.return_type)) {1643 if (!calling_convention_allows_zig_types(fn_type_id.cc) &&
1644 fn_type_id.return_type->id != TypeTableEntryIdVoid &&
1645 !type_allowed_in_extern(g, fn_type_id.return_type))
1646 {
1641 add_node_error(g, fn_proto->return_type,1647 add_node_error(g, fn_proto->return_type,
1642 buf_sprintf("return type '%s' not allowed in function with calling convention '%s'",1648 buf_sprintf("return type '%s' not allowed in function with calling convention '%s'",
1643 buf_ptr(&fn_type_id.return_type->name),1649 buf_ptr(&fn_type_id.return_type->name),
...@@ -1939,6 +1945,17 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {...@@ -1939,6 +1945,17 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
1939 break;1945 break;
1940 }1946 }
19411947
1948 if (struct_type->data.structure.layout == ContainerLayoutExtern) {
1949 if (!type_allowed_in_extern(g, field_type)) {
1950 AstNode *field_source_node = decl_node->data.container_decl.fields.at(i);
1951 add_node_error(g, field_source_node,
1952 buf_sprintf("extern structs cannot contain fields of type '%s'",
1953 buf_ptr(&field_type->name)));
1954 struct_type->data.structure.is_invalid = true;
1955 break;
1956 }
1957 }
1958
1942 if (!type_has_bits(field_type))1959 if (!type_has_bits(field_type))
1943 continue;1960 continue;
19441961
src/codegen.cpp+5-3
...@@ -2212,10 +2212,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,...@@ -2212,10 +2212,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
2212 return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");2212 return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
2213 } else if (type_entry->id == TypeTableEntryIdEnum ||2213 } else if (type_entry->id == TypeTableEntryIdEnum ||
2214 type_entry->id == TypeTableEntryIdErrorSet ||2214 type_entry->id == TypeTableEntryIdErrorSet ||
2215 type_entry->id == TypeTableEntryIdPointer ||
2216 type_entry->id == TypeTableEntryIdBool ||2215 type_entry->id == TypeTableEntryIdBool ||
2217 type_entry->id == TypeTableEntryIdPromise ||2216 get_codegen_ptr_type(type_entry) != nullptr)
2218 type_entry->id == TypeTableEntryIdFn)
2219 {2217 {
2220 LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);2218 LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
2221 return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");2219 return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
...@@ -3103,6 +3101,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3103,6 +3101,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3103 return nullptr;3101 return nullptr;
3104 } else if (first_arg_ret) {3102 } else if (first_arg_ret) {
3105 return instruction->tmp_ptr;3103 return instruction->tmp_ptr;
3104 } else if (handle_is_ptr(src_return_type)) {
3105 auto store_instr = LLVMBuildStore(g->builder, result, instruction->tmp_ptr);
3106 LLVMSetAlignment(store_instr, LLVMGetAlignment(instruction->tmp_ptr));
3107 return instruction->tmp_ptr;
3106 } else {3108 } else {
3107 return result;3109 return result;
3108 }3110 }
src/ir.cpp+53-96
...@@ -39,9 +39,6 @@ struct IrAnalyze {...@@ -39,9 +39,6 @@ struct IrAnalyze {
39 IrBasicBlock *const_predecessor_bb;39 IrBasicBlock *const_predecessor_bb;
40};40};
4141
42static const LVal LVAL_NONE = { false, false, false };
43static const LVal LVAL_PTR = { true, false, false };
44
45enum ConstCastResultId {42enum ConstCastResultId {
46 ConstCastResultIdOk,43 ConstCastResultIdOk,
47 ConstCastResultIdErrSet,44 ConstCastResultIdErrSet,
...@@ -249,8 +246,6 @@ static void ir_ref_bb(IrBasicBlock *bb) {...@@ -249,8 +246,6 @@ static void ir_ref_bb(IrBasicBlock *bb) {
249static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) {246static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) {
250 assert(instruction->id != IrInstructionIdInvalid);247 assert(instruction->id != IrInstructionIdInvalid);
251 instruction->ref_count += 1;248 instruction->ref_count += 1;
252 if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction))
253 ir_ref_bb(instruction->owner_bb);
254}249}
255250
256static void ir_ref_var(VariableTableEntry *var) {251static void ir_ref_var(VariableTableEntry *var) {
...@@ -3164,7 +3159,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -3164,7 +3159,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
3164 case ReturnKindError:3159 case ReturnKindError:
3165 {3160 {
3166 assert(expr_node);3161 assert(expr_node);
3167 IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR);3162 IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr);
3168 if (err_union_ptr == irb->codegen->invalid_instruction)3163 if (err_union_ptr == irb->codegen->invalid_instruction)
3169 return irb->codegen->invalid_instruction;3164 return irb->codegen->invalid_instruction;
3170 IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr);3165 IrInstruction *err_union_val = ir_build_load_ptr(irb, scope, node, err_union_ptr);
...@@ -3192,7 +3187,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -3192,7 +3187,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
31923187
3193 ir_set_cursor_at_end_and_append_block(irb, continue_block);3188 ir_set_cursor_at_end_and_append_block(irb, continue_block);
3194 IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false);3189 IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false);
3195 if (lval.is_ptr)3190 if (lval == LValPtr)
3196 return unwrapped_ptr;3191 return unwrapped_ptr;
3197 else3192 else
3198 return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);3193 return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
...@@ -3357,7 +3352,7 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *no...@@ -3357,7 +3352,7 @@ static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *no
3357}3352}
33583353
3359static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) {3354static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node) {
3360 IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LVAL_PTR);3355 IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr);
3361 IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);3356 IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
33623357
3363 if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction)3358 if (lvalue == irb->codegen->invalid_instruction || rvalue == irb->codegen->invalid_instruction)
...@@ -3368,7 +3363,7 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node)...@@ -3368,7 +3363,7 @@ static IrInstruction *ir_gen_assign(IrBuilder *irb, Scope *scope, AstNode *node)
3368}3363}
33693364
3370static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) {3365static IrInstruction *ir_gen_assign_op(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
3371 IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LVAL_PTR);3366 IrInstruction *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValPtr);
3372 if (lvalue == irb->codegen->invalid_instruction)3367 if (lvalue == irb->codegen->invalid_instruction)
3373 return lvalue;3368 return lvalue;
3374 IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue);3369 IrInstruction *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue);
...@@ -3470,7 +3465,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As...@@ -3470,7 +3465,7 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As
3470 AstNode *op1_node = node->data.bin_op_expr.op1;3465 AstNode *op1_node = node->data.bin_op_expr.op1;
3471 AstNode *op2_node = node->data.bin_op_expr.op2;3466 AstNode *op2_node = node->data.bin_op_expr.op2;
34723467
3473 IrInstruction *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LVAL_PTR);3468 IrInstruction *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr);
3474 if (maybe_ptr == irb->codegen->invalid_instruction)3469 if (maybe_ptr == irb->codegen->invalid_instruction)
3475 return irb->codegen->invalid_instruction;3470 return irb->codegen->invalid_instruction;
34763471
...@@ -3657,7 +3652,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -3657,7 +3652,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,
36573652
3658 Buf *variable_name = node->data.symbol_expr.symbol;3653 Buf *variable_name = node->data.symbol_expr.symbol;
36593654
3660 if (buf_eql_str(variable_name, "_") && lval.is_ptr) {3655 if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
3661 IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);3656 IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
3662 const_instruction->base.value.type = get_pointer_to_type(irb->codegen,3657 const_instruction->base.value.type = get_pointer_to_type(irb->codegen,
3663 irb->codegen->builtin_types.entry_void, false);3658 irb->codegen->builtin_types.entry_void, false);
...@@ -3669,8 +3664,8 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -3669,8 +3664,8 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,
3669 auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(variable_name);3664 auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(variable_name);
3670 if (primitive_table_entry) {3665 if (primitive_table_entry) {
3671 IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_table_entry->value);3666 IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_table_entry->value);
3672 if (lval.is_ptr) {3667 if (lval == LValPtr) {
3673 return ir_build_ref(irb, scope, node, value, lval.is_const, lval.is_volatile);3668 return ir_build_ref(irb, scope, node, value, false, false);
3674 } else {3669 } else {
3675 return value;3670 return value;
3676 }3671 }
...@@ -3679,7 +3674,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -3679,7 +3674,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,
3679 VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name);3674 VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name);
3680 if (var) {3675 if (var) {
3681 IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var);3676 IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var);
3682 if (lval.is_ptr)3677 if (lval == LValPtr)
3683 return var_ptr;3678 return var_ptr;
3684 else3679 else
3685 return ir_build_load_ptr(irb, scope, node, var_ptr);3680 return ir_build_load_ptr(irb, scope, node, var_ptr);
...@@ -3705,7 +3700,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode...@@ -3705,7 +3700,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode
3705 assert(node->type == NodeTypeArrayAccessExpr);3700 assert(node->type == NodeTypeArrayAccessExpr);
37063701
3707 AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr;3702 AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr;
3708 IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LVAL_PTR);3703 IrInstruction *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr);
3709 if (array_ref_instruction == irb->codegen->invalid_instruction)3704 if (array_ref_instruction == irb->codegen->invalid_instruction)
3710 return array_ref_instruction;3705 return array_ref_instruction;
37113706
...@@ -3716,7 +3711,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode...@@ -3716,7 +3711,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode
37163711
3717 IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction,3712 IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction,
3718 subscript_instruction, true, PtrLenSingle);3713 subscript_instruction, true, PtrLenSingle);
3719 if (lval.is_ptr)3714 if (lval == LValPtr)
3720 return ptr_instruction;3715 return ptr_instruction;
37213716
3722 return ir_build_load_ptr(irb, scope, node, ptr_instruction);3717 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
...@@ -3728,7 +3723,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode...@@ -3728,7 +3723,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode
3728 AstNode *container_ref_node = node->data.field_access_expr.struct_expr;3723 AstNode *container_ref_node = node->data.field_access_expr.struct_expr;
3729 Buf *field_name = node->data.field_access_expr.field_name;3724 Buf *field_name = node->data.field_access_expr.field_name;
37303725
3731 IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LVAL_PTR);3726 IrInstruction *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr);
3732 if (container_ref_instruction == irb->codegen->invalid_instruction)3727 if (container_ref_instruction == irb->codegen->invalid_instruction)
3733 return container_ref_instruction;3728 return container_ref_instruction;
37343729
...@@ -4386,7 +4381,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4386,7 +4381,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4386 case BuiltinFnIdField:4381 case BuiltinFnIdField:
4387 {4382 {
4388 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4383 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4389 IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LVAL_PTR);4384 IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr);
4390 if (arg0_value == irb->codegen->invalid_instruction)4385 if (arg0_value == irb->codegen->invalid_instruction)
4391 return arg0_value;4386 return arg0_value;
43924387
...@@ -4397,7 +4392,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4397,7 +4392,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
43974392
4398 IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value);4393 IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value);
43994394
4400 if (lval.is_ptr)4395 if (lval == LValPtr)
4401 return ptr_instruction;4396 return ptr_instruction;
44024397
4403 return ir_build_load_ptr(irb, scope, node, ptr_instruction);4398 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
...@@ -4928,18 +4923,18 @@ static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, Ast...@@ -4928,18 +4923,18 @@ static IrInstruction *ir_gen_prefix_op_id_lval(IrBuilder *irb, Scope *scope, Ast
4928}4923}
49294924
4930static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) {4925static IrInstruction *ir_gen_prefix_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrUnOp op_id) {
4931 return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LVAL_NONE);4926 return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone);
4932}4927}
49334928
4934static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval) {4929static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval) {
4935 if (!lval.is_ptr)4930 if (lval != LValPtr)
4936 return value;4931 return value;
4937 if (value == irb->codegen->invalid_instruction)4932 if (value == irb->codegen->invalid_instruction)
4938 return value;4933 return value;
49394934
4940 // We needed a pointer to a value, but we got a value. So we create4935 // We needed a pointer to a value, but we got a value. So we create
4941 // an instruction which just makes a const pointer of it.4936 // an instruction which just makes a const pointer of it.
4942 return ir_build_ref(irb, scope, value->source_node, value, lval.is_const, lval.is_volatile);4937 return ir_build_ref(irb, scope, value->source_node, value, false, false);
4943}4938}
49444939
4945static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) {4940static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) {
...@@ -5001,7 +4996,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode...@@ -5001,7 +4996,7 @@ static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode
5001static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node,4996static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node,
5002 LVal lval)4997 LVal lval)
5003{4998{
5004 IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR);4999 IrInstruction *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr);
5005 if (err_union_ptr == irb->codegen->invalid_instruction)5000 if (err_union_ptr == irb->codegen->invalid_instruction)
5006 return irb->codegen->invalid_instruction;5001 return irb->codegen->invalid_instruction;
50075002
...@@ -5009,7 +5004,7 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode...@@ -5009,7 +5004,7 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode
5009 if (payload_ptr == irb->codegen->invalid_instruction)5004 if (payload_ptr == irb->codegen->invalid_instruction)
5010 return irb->codegen->invalid_instruction;5005 return irb->codegen->invalid_instruction;
50115006
5012 if (lval.is_ptr)5007 if (lval == LValPtr)
5013 return payload_ptr;5008 return payload_ptr;
50145009
5015 return ir_build_load_ptr(irb, scope, source_node, payload_ptr);5010 return ir_build_load_ptr(irb, scope, source_node, payload_ptr);
...@@ -5046,7 +5041,7 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod...@@ -5046,7 +5041,7 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod
5046 return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval);5041 return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval);
5047 case PrefixOpAddrOf: {5042 case PrefixOpAddrOf: {
5048 AstNode *expr_node = node->data.prefix_op_expr.primary_expr;5043 AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
5049 return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR), lval);5044 return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr), lval);
5050 }5045 }
5051 }5046 }
5052 zig_unreachable();5047 zig_unreachable();
...@@ -5186,7 +5181,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n...@@ -5186,7 +5181,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
5186 } else {5181 } else {
5187 payload_scope = scope;5182 payload_scope = scope;
5188 }5183 }
5189 IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LVAL_PTR);5184 IrInstruction *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LValPtr);
5190 if (err_val_ptr == irb->codegen->invalid_instruction)5185 if (err_val_ptr == irb->codegen->invalid_instruction)
5191 return err_val_ptr;5186 return err_val_ptr;
5192 IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr);5187 IrInstruction *err_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, err_val_ptr);
...@@ -5269,7 +5264,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n...@@ -5269,7 +5264,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
5269 VariableTableEntry *payload_var = ir_create_var(irb, symbol_node, scope, var_symbol,5264 VariableTableEntry *payload_var = ir_create_var(irb, symbol_node, scope, var_symbol,
5270 true, false, false, is_comptime);5265 true, false, false, is_comptime);
5271 Scope *child_scope = payload_var->child_scope;5266 Scope *child_scope = payload_var->child_scope;
5272 IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LVAL_PTR);5267 IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, scope, LValPtr);
5273 if (maybe_val_ptr == irb->codegen->invalid_instruction)5268 if (maybe_val_ptr == irb->codegen->invalid_instruction)
5274 return maybe_val_ptr;5269 return maybe_val_ptr;
5275 IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr);5270 IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr);
...@@ -5413,7 +5408,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo...@@ -5413,7 +5408,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
5413 }5408 }
5414 assert(elem_node->type == NodeTypeSymbol);5409 assert(elem_node->type == NodeTypeSymbol);
54155410
5416 IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LVAL_PTR);5411 IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LValPtr);
5417 if (array_val_ptr == irb->codegen->invalid_instruction)5412 if (array_val_ptr == irb->codegen->invalid_instruction)
5418 return array_val_ptr;5413 return array_val_ptr;
54195414
...@@ -5700,7 +5695,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no...@@ -5700,7 +5695,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no
5700 AstNode *else_node = node->data.test_expr.else_node;5695 AstNode *else_node = node->data.test_expr.else_node;
5701 bool var_is_ptr = node->data.test_expr.var_is_ptr;5696 bool var_is_ptr = node->data.test_expr.var_is_ptr;
57025697
5703 IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR);5698 IrInstruction *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr);
5704 if (maybe_val_ptr == irb->codegen->invalid_instruction)5699 if (maybe_val_ptr == irb->codegen->invalid_instruction)
5705 return maybe_val_ptr;5700 return maybe_val_ptr;
57065701
...@@ -5778,7 +5773,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -5778,7 +5773,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *
5778 Buf *var_symbol = node->data.if_err_expr.var_symbol;5773 Buf *var_symbol = node->data.if_err_expr.var_symbol;
5779 Buf *err_symbol = node->data.if_err_expr.err_symbol;5774 Buf *err_symbol = node->data.if_err_expr.err_symbol;
57805775
5781 IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LVAL_PTR);5776 IrInstruction *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr);
5782 if (err_val_ptr == irb->codegen->invalid_instruction)5777 if (err_val_ptr == irb->codegen->invalid_instruction)
5783 return err_val_ptr;5778 return err_val_ptr;
57845779
...@@ -5904,7 +5899,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -5904,7 +5899,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
5904 assert(node->type == NodeTypeSwitchExpr);5899 assert(node->type == NodeTypeSwitchExpr);
59055900
5906 AstNode *target_node = node->data.switch_expr.expr;5901 AstNode *target_node = node->data.switch_expr.expr;
5907 IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LVAL_PTR);5902 IrInstruction *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr);
5908 if (target_value_ptr == irb->codegen->invalid_instruction)5903 if (target_value_ptr == irb->codegen->invalid_instruction)
5909 return target_value_ptr;5904 return target_value_ptr;
5910 IrInstruction *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr);5905 IrInstruction *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr);
...@@ -6277,7 +6272,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node)...@@ -6277,7 +6272,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node)
6277 AstNode *start_node = slice_expr->start;6272 AstNode *start_node = slice_expr->start;
6278 AstNode *end_node = slice_expr->end;6273 AstNode *end_node = slice_expr->end;
62796274
6280 IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LVAL_PTR);6275 IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr);
6281 if (ptr_value == irb->codegen->invalid_instruction)6276 if (ptr_value == irb->codegen->invalid_instruction)
6282 return irb->codegen->invalid_instruction;6277 return irb->codegen->invalid_instruction;
62836278
...@@ -6311,11 +6306,11 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN...@@ -6311,11 +6306,11 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN
6311 add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name)));6306 add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name)));
6312 return irb->codegen->invalid_instruction;6307 return irb->codegen->invalid_instruction;
6313 }6308 }
6314 return ir_gen_err_assert_ok(irb, parent_scope, node, op1_node, LVAL_NONE);6309 return ir_gen_err_assert_ok(irb, parent_scope, node, op1_node, LValNone);
6315 }6310 }
63166311
63176312
6318 IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LVAL_PTR);6313 IrInstruction *err_union_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr);
6319 if (err_union_ptr == irb->codegen->invalid_instruction)6314 if (err_union_ptr == irb->codegen->invalid_instruction)
6320 return irb->codegen->invalid_instruction;6315 return irb->codegen->invalid_instruction;
63216316
...@@ -6868,7 +6863,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -6868,7 +6863,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
6868 IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node);6863 IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node);
6869 if (ptr_instruction == irb->codegen->invalid_instruction)6864 if (ptr_instruction == irb->codegen->invalid_instruction)
6870 return ptr_instruction;6865 return ptr_instruction;
6871 if (lval.is_ptr)6866 if (lval == LValPtr)
6872 return ptr_instruction;6867 return ptr_instruction;
68736868
6874 return ir_build_load_ptr(irb, scope, node, ptr_instruction);6869 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
...@@ -6884,12 +6879,12 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -6884,12 +6879,12 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
6884 case NodeTypeUnwrapOptional: {6879 case NodeTypeUnwrapOptional: {
6885 AstNode *expr_node = node->data.unwrap_optional.expr;6880 AstNode *expr_node = node->data.unwrap_optional.expr;
68866881
6887 IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR);6882 IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr);
6888 if (maybe_ptr == irb->codegen->invalid_instruction)6883 if (maybe_ptr == irb->codegen->invalid_instruction)
6889 return irb->codegen->invalid_instruction;6884 return irb->codegen->invalid_instruction;
68906885
6891 IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true);6886 IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true);
6892 if (lval.is_ptr)6887 if (lval == LValPtr)
6893 return unwrapped_ptr;6888 return unwrapped_ptr;
68946889
6895 return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);6890 return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
...@@ -6959,7 +6954,7 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *sc...@@ -6959,7 +6954,7 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *sc
6959}6954}
69606955
6961static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) {6956static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) {
6962 return ir_gen_node_extra(irb, node, scope, LVAL_NONE);6957 return ir_gen_node_extra(irb, node, scope, LValNone);
6963}6958}
69646959
6965static void invalidate_exec(IrExecutable *exec) {6960static void invalidate_exec(IrExecutable *exec) {
...@@ -7089,7 +7084,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -7089,7 +7084,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
7089 irb->exec->coro_final_cleanup_block = ir_create_basic_block(irb, scope, "FinalCleanup");7084 irb->exec->coro_final_cleanup_block = ir_create_basic_block(irb, scope, "FinalCleanup");
7090 }7085 }
70917086
7092 IrInstruction *result = ir_gen_node_extra(irb, node, scope, LVAL_NONE);7087 IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone);
7093 assert(result);7088 assert(result);
7094 if (irb->exec->invalid)7089 if (irb->exec->invalid)
7095 return false;7090 return false;
...@@ -9242,26 +9237,9 @@ static TypeTableEntry *ir_finish_anal(IrAnalyze *ira, TypeTableEntry *result_typ...@@ -9242,26 +9237,9 @@ static TypeTableEntry *ir_finish_anal(IrAnalyze *ira, TypeTableEntry *result_typ
9242}9237}
92439238
9244static IrInstruction *ir_get_const(IrAnalyze *ira, IrInstruction *old_instruction) {9239static IrInstruction *ir_get_const(IrAnalyze *ira, IrInstruction *old_instruction) {
9245 IrInstruction *new_instruction;9240 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
9246 if (old_instruction->id == IrInstructionIdVarPtr) {9241 old_instruction->scope, old_instruction->source_node);
9247 IrInstructionVarPtr *old_var_ptr_instruction = (IrInstructionVarPtr *)old_instruction;9242 IrInstruction *new_instruction = &const_instruction->base;
9248 IrInstructionVarPtr *var_ptr_instruction = ir_create_instruction<IrInstructionVarPtr>(&ira->new_irb,
9249 old_instruction->scope, old_instruction->source_node);
9250 var_ptr_instruction->var = old_var_ptr_instruction->var;
9251 new_instruction = &var_ptr_instruction->base;
9252 } else if (old_instruction->id == IrInstructionIdFieldPtr) {
9253 IrInstructionFieldPtr *field_ptr_instruction = ir_create_instruction<IrInstructionFieldPtr>(&ira->new_irb,
9254 old_instruction->scope, old_instruction->source_node);
9255 new_instruction = &field_ptr_instruction->base;
9256 } else if (old_instruction->id == IrInstructionIdElemPtr) {
9257 IrInstructionElemPtr *elem_ptr_instruction = ir_create_instruction<IrInstructionElemPtr>(&ira->new_irb,
9258 old_instruction->scope, old_instruction->source_node);
9259 new_instruction = &elem_ptr_instruction->base;
9260 } else {
9261 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
9262 old_instruction->scope, old_instruction->source_node);
9263 new_instruction = &const_instruction->base;
9264 }
9265 new_instruction->value.special = ConstValSpecialStatic;9243 new_instruction->value.special = ConstValSpecialStatic;
9266 return new_instruction;9244 return new_instruction;
9267}9245}
...@@ -9615,23 +9593,6 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi...@@ -9615,23 +9593,6 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi
9615 if (type_is_invalid(value->value.type))9593 if (type_is_invalid(value->value.type))
9616 return ira->codegen->invalid_instruction;9594 return ira->codegen->invalid_instruction;
96179595
9618 if (value->id == IrInstructionIdLoadPtr) {
9619 IrInstructionLoadPtr *load_ptr_inst = (IrInstructionLoadPtr *) value;
9620
9621 if (load_ptr_inst->ptr->value.type->data.pointer.is_const) {
9622 return load_ptr_inst->ptr;
9623 }
9624
9625 type_ensure_zero_bits_known(ira->codegen, value->value.type);
9626 if (type_is_invalid(value->value.type)) {
9627 return ira->codegen->invalid_instruction;
9628 }
9629
9630 if (!type_has_bits(value->value.type)) {
9631 return load_ptr_inst->ptr;
9632 }
9633 }
9634
9635 if (instr_is_comptime(value)) {9596 if (instr_is_comptime(value)) {
9636 ConstExprValue *val = ir_resolve_const(ira, value, UndefOk);9597 ConstExprValue *val = ir_resolve_const(ira, value, UndefOk);
9637 if (!val)9598 if (!val)
...@@ -11150,7 +11111,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -11150,7 +11111,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
11150 if (type_is_invalid(resolved_type))11111 if (type_is_invalid(resolved_type))
11151 return resolved_type;11112 return resolved_type;
1115211113
1115311114 bool operator_allowed;
11154 switch (resolved_type->id) {11115 switch (resolved_type->id) {
11155 case TypeTableEntryIdInvalid:11116 case TypeTableEntryIdInvalid:
11156 zig_unreachable(); // handled above11117 zig_unreachable(); // handled above
...@@ -11159,6 +11120,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -11159,6 +11120,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
11159 case TypeTableEntryIdComptimeInt:11120 case TypeTableEntryIdComptimeInt:
11160 case TypeTableEntryIdInt:11121 case TypeTableEntryIdInt:
11161 case TypeTableEntryIdFloat:11122 case TypeTableEntryIdFloat:
11123 operator_allowed = true;
11162 break;11124 break;
1116311125
11164 case TypeTableEntryIdBool:11126 case TypeTableEntryIdBool:
...@@ -11173,19 +11135,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -11173,19 +11135,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
11173 case TypeTableEntryIdBoundFn:11135 case TypeTableEntryIdBoundFn:
11174 case TypeTableEntryIdArgTuple:11136 case TypeTableEntryIdArgTuple:
11175 case TypeTableEntryIdPromise:11137 case TypeTableEntryIdPromise:
11176 if (!is_equality_cmp) {
11177 ir_add_error_node(ira, source_node,
11178 buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
11179 return ira->codegen->builtin_types.entry_invalid;
11180 }
11181 break;
11182
11183 case TypeTableEntryIdEnum:11138 case TypeTableEntryIdEnum:
11184 if (!is_equality_cmp) {11139 operator_allowed = is_equality_cmp;
11185 ir_add_error_node(ira, source_node,
11186 buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
11187 return ira->codegen->builtin_types.entry_invalid;
11188 }
11189 break;11140 break;
1119011141
11191 case TypeTableEntryIdUnreachable:11142 case TypeTableEntryIdUnreachable:
...@@ -11193,12 +11144,18 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -11193,12 +11144,18 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
11193 case TypeTableEntryIdStruct:11144 case TypeTableEntryIdStruct:
11194 case TypeTableEntryIdUndefined:11145 case TypeTableEntryIdUndefined:
11195 case TypeTableEntryIdNull:11146 case TypeTableEntryIdNull:
11196 case TypeTableEntryIdOptional:
11197 case TypeTableEntryIdErrorUnion:11147 case TypeTableEntryIdErrorUnion:
11198 case TypeTableEntryIdUnion:11148 case TypeTableEntryIdUnion:
11199 ir_add_error_node(ira, source_node,11149 operator_allowed = false;
11200 buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));11150 break;
11201 return ira->codegen->builtin_types.entry_invalid;11151 case TypeTableEntryIdOptional:
11152 operator_allowed = is_equality_cmp && get_codegen_ptr_type(resolved_type) != nullptr;
11153 break;
11154 }
11155 if (!operator_allowed) {
11156 ir_add_error_node(ira, source_node,
11157 buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
11158 return ira->codegen->builtin_types.entry_invalid;
11202 }11159 }
1120311160
11204 IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type);11161 IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type);
...@@ -19752,7 +19709,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,...@@ -19752,7 +19709,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
19752 Tld *tld = instruction->tld;19709 Tld *tld = instruction->tld;
19753 LVal lval = instruction->lval;19710 LVal lval = instruction->lval;
1975419711
19755 resolve_top_level_decl(ira->codegen, tld, lval.is_ptr, instruction->base.source_node);19712 resolve_top_level_decl(ira->codegen, tld, lval == LValPtr, instruction->base.source_node);
19756 if (tld->resolution == TldResolutionInvalid)19713 if (tld->resolution == TldResolutionInvalid)
19757 return ira->codegen->builtin_types.entry_invalid;19714 return ira->codegen->builtin_types.entry_invalid;
1975819715
...@@ -19773,7 +19730,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,...@@ -19773,7 +19730,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
19773 add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node);19730 add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node);
19774 }19731 }
1977519732
19776 if (lval.is_ptr) {19733 if (lval == LValPtr) {
19777 ir_link_new_instruction(var_ptr, &instruction->base);19734 ir_link_new_instruction(var_ptr, &instruction->base);
19778 return var_ptr->value.type;19735 return var_ptr->value.type;
19779 } else {19736 } else {
...@@ -19794,7 +19751,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,...@@ -19794,7 +19751,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
1979419751
19795 IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope,19752 IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope,
19796 instruction->base.source_node, fn_entry);19753 instruction->base.source_node, fn_entry);
19797 if (lval.is_ptr) {19754 if (lval == LValPtr) {
19798 IrInstruction *ptr_instr = ir_get_ref(ira, &instruction->base, ref_instruction, true, false);19755 IrInstruction *ptr_instr = ir_get_ref(ira, &instruction->base, ref_instruction, true, false);
19799 ir_link_new_instruction(ptr_instr, &instruction->base);19756 ir_link_new_instruction(ptr_instr, &instruction->base);
19800 return ptr_instr->value.type;19757 return ptr_instr->value.type;
src/ir_print.cpp+2-4
...@@ -1005,10 +1005,8 @@ static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) {...@@ -1005,10 +1005,8 @@ static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) {
1005}1005}
10061006
1007static void ir_print_decl_ref(IrPrint *irp, IrInstructionDeclRef *instruction) {1007static void ir_print_decl_ref(IrPrint *irp, IrInstructionDeclRef *instruction) {
1008 const char *ptr_str = instruction->lval.is_ptr ? "ptr " : "";1008 const char *ptr_str = (instruction->lval == LValPtr) ? "ptr " : "";
1009 const char *const_str = instruction->lval.is_const ? "const " : "";1009 fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name));
1010 const char *volatile_str = instruction->lval.is_volatile ? "volatile " : "";
1011 fprintf(irp->f, "declref %s%s%s%s", const_str, volatile_str, ptr_str, buf_ptr(instruction->tld->name));
1012}1010}
10131011
1014static void ir_print_panic(IrPrint *irp, IrInstructionPanic *instruction) {1012static void ir_print_panic(IrPrint *irp, IrInstructionPanic *instruction) {
src/main.cpp+7-3
...@@ -891,15 +891,19 @@ int main(int argc, char **argv) {...@@ -891,15 +891,19 @@ int main(int argc, char **argv) {
891891
892 add_package(g, cur_pkg, g->root_package);892 add_package(g, cur_pkg, g->root_package);
893893
894 if (cmd == CmdBuild || cmd == CmdRun) {894 if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) {
895 codegen_set_emit_file_type(g, emit_file_type);
896
897 for (size_t i = 0; i < objects.length; i += 1) {895 for (size_t i = 0; i < objects.length; i += 1) {
898 codegen_add_object(g, buf_create_from_str(objects.at(i)));896 codegen_add_object(g, buf_create_from_str(objects.at(i)));
899 }897 }
900 for (size_t i = 0; i < asm_files.length; i += 1) {898 for (size_t i = 0; i < asm_files.length; i += 1) {
901 codegen_add_assembly(g, buf_create_from_str(asm_files.at(i)));899 codegen_add_assembly(g, buf_create_from_str(asm_files.at(i)));
902 }900 }
901 }
902
903
904 if (cmd == CmdBuild || cmd == CmdRun) {
905 codegen_set_emit_file_type(g, emit_file_type);
906
903 codegen_build(g);907 codegen_build(g);
904 codegen_link(g, out_file);908 codegen_link(g, out_file);
905 if (timing_info)909 if (timing_info)
std/array_list.zig+70-6
...@@ -41,8 +41,8 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {...@@ -41,8 +41,8 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
41 return self.items[0..self.len];41 return self.items[0..self.len];
42 }42 }
4343
44 pub fn at(self: Self, n: usize) T {44 pub fn at(self: Self, i: usize) T {
45 return self.toSliceConst()[n];45 return self.toSliceConst()[i];
46 }46 }
4747
48 /// Sets the value at index `i`, or returns `error.OutOfBounds` if48 /// Sets the value at index `i`, or returns `error.OutOfBounds` if
...@@ -85,7 +85,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {...@@ -85,7 +85,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
85 try self.ensureCapacity(self.len + 1);85 try self.ensureCapacity(self.len + 1);
86 self.len += 1;86 self.len += 1;
8787
88 mem.copy(T, self.items[n + 1 .. self.len], self.items[n .. self.len - 1]);88 mem.copyBackwards(T, self.items[n + 1 .. self.len], self.items[n .. self.len - 1]);
89 self.items[n] = item;89 self.items[n] = item;
90 }90 }
9191
...@@ -93,7 +93,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {...@@ -93,7 +93,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
93 try self.ensureCapacity(self.len + items.len);93 try self.ensureCapacity(self.len + items.len);
94 self.len += items.len;94 self.len += items.len;
9595
96 mem.copy(T, self.items[n + items.len .. self.len], self.items[n .. self.len - items.len]);96 mem.copyBackwards(T, self.items[n + items.len .. self.len], self.items[n .. self.len - items.len]);
97 mem.copy(T, self.items[n .. n + items.len], items);97 mem.copy(T, self.items[n .. n + items.len], items);
98 }98 }
9999
...@@ -102,6 +102,26 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {...@@ -102,6 +102,26 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
102 new_item_ptr.* = item;102 new_item_ptr.* = item;
103 }103 }
104104
105 /// Removes the element at the specified index and returns it.
106 /// The empty slot is filled from the end of the list.
107 pub fn swapRemove(self: *Self, i: usize) T {
108 if (self.len - 1 == i) return self.pop();
109
110 const slice = self.toSlice();
111 const old_item = slice[i];
112 slice[i] = self.pop();
113 return old_item;
114 }
115
116 pub fn removeOrError(self: *Self, n: usize) !T {
117 if (n >= self.len) return error.OutOfBounds;
118 if (self.len - 1 == n) return self.pop();
119
120 var old_item = self.at(n);
121 try self.setOrError(n, self.pop());
122 return old_item;
123 }
124
105 pub fn appendSlice(self: *Self, items: []align(A) const T) !void {125 pub fn appendSlice(self: *Self, items: []align(A) const T) !void {
106 try self.ensureCapacity(self.len + items.len);126 try self.ensureCapacity(self.len + items.len);
107 mem.copy(T, self.items[self.len..], items);127 mem.copy(T, self.items[self.len..], items);
...@@ -232,6 +252,33 @@ test "basic ArrayList test" {...@@ -232,6 +252,33 @@ test "basic ArrayList test" {
232 assert(list.pop() == 33);252 assert(list.pop() == 33);
233}253}
234254
255test "std.ArrayList.swapRemove" {
256 var list = ArrayList(i32).init(debug.global_allocator);
257 defer list.deinit();
258
259 try list.append(1);
260 try list.append(2);
261 try list.append(3);
262 try list.append(4);
263 try list.append(5);
264 try list.append(6);
265 try list.append(7);
266
267 //remove from middle
268 assert(list.swapRemove(3) == 4);
269 assert(list.at(3) == 7);
270 assert(list.len == 6);
271
272 //remove from end
273 assert(list.swapRemove(5) == 6);
274 assert(list.len == 5);
275
276 //remove from front
277 assert(list.swapRemove(0) == 1);
278 assert(list.at(0) == 5);
279 assert(list.len == 4);
280}
281
235test "iterator ArrayList test" {282test "iterator ArrayList test" {
236 var list = ArrayList(i32).init(debug.global_allocator);283 var list = ArrayList(i32).init(debug.global_allocator);
237 defer list.deinit();284 defer list.deinit();
...@@ -266,19 +313,36 @@ test "insert ArrayList test" {...@@ -266,19 +313,36 @@ test "insert ArrayList test" {
266 defer list.deinit();313 defer list.deinit();
267314
268 try list.append(1);315 try list.append(1);
316 try list.append(2);
317 try list.append(3);
269 try list.insert(0, 5);318 try list.insert(0, 5);
270 assert(list.items[0] == 5);319 assert(list.items[0] == 5);
271 assert(list.items[1] == 1);320 assert(list.items[1] == 1);
321 assert(list.items[2] == 2);
322 assert(list.items[3] == 3);
323}
324
325test "insertSlice ArrayList test" {
326 var list = ArrayList(i32).init(debug.global_allocator);
327 defer list.deinit();
272328
329 try list.append(1);
330 try list.append(2);
331 try list.append(3);
332 try list.append(4);
273 try list.insertSlice(1, []const i32{333 try list.insertSlice(1, []const i32{
274 9,334 9,
275 8,335 8,
276 });336 });
277 assert(list.items[0] == 5);337 assert(list.items[0] == 1);
278 assert(list.items[1] == 9);338 assert(list.items[1] == 9);
279 assert(list.items[2] == 8);339 assert(list.items[2] == 8);
340 assert(list.items[3] == 2);
341 assert(list.items[4] == 3);
342 assert(list.items[5] == 4);
280343
281 const items = []const i32{1};344 const items = []const i32{1};
282 try list.insertSlice(0, items[0..0]);345 try list.insertSlice(0, items[0..0]);
283 assert(list.items[0] == 5);346 assert(list.len == 6);
347 assert(list.items[0] == 1);
284}348}
std/atomic/index.zig+4-4
...@@ -1,9 +1,9 @@...@@ -1,9 +1,9 @@
1pub const Stack = @import("stack.zig").Stack;1pub const Stack = @import("stack.zig").Stack;
2pub const QueueMpsc = @import("queue_mpsc.zig").QueueMpsc;2pub const Queue = @import("queue.zig").Queue;
3pub const QueueMpmc = @import("queue_mpmc.zig").QueueMpmc;3pub const Int = @import("int.zig").Int;
44
5test "std.atomic" {5test "std.atomic" {
6 _ = @import("stack.zig");6 _ = @import("stack.zig");
7 _ = @import("queue_mpsc.zig");7 _ = @import("queue.zig");
8 _ = @import("queue_mpmc.zig");8 _ = @import("int.zig");
9}9}
std/atomic/int.zig created+29
...@@ -0,0 +1,29 @@
1const builtin = @import("builtin");
2const AtomicOrder = builtin.AtomicOrder;
3
4/// Thread-safe, lock-free integer
5pub fn Int(comptime T: type) type {
6 return struct {
7 unprotected_value: T,
8
9 pub const Self = this;
10
11 pub fn init(init_val: T) Self {
12 return Self{ .unprotected_value = init_val };
13 }
14
15 /// Returns previous value
16 pub fn incr(self: *Self) T {
17 return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
18 }
19
20 /// Returns previous value
21 pub fn decr(self: *Self) T {
22 return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
23 }
24
25 pub fn get(self: *Self) T {
26 return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst);
27 }
28 };
29}
std/atomic/queue.zig created+226
...@@ -0,0 +1,226 @@
1const builtin = @import("builtin");
2const AtomicOrder = builtin.AtomicOrder;
3const AtomicRmwOp = builtin.AtomicRmwOp;
4
5/// Many producer, many consumer, non-allocating, thread-safe.
6/// Uses a spinlock to protect get() and put().
7pub fn Queue(comptime T: type) type {
8 return struct {
9 head: ?*Node,
10 tail: ?*Node,
11 lock: u8,
12
13 pub const Self = this;
14
15 pub const Node = struct {
16 next: ?*Node,
17 data: T,
18 };
19
20 pub fn init() Self {
21 return Self{
22 .head = null,
23 .tail = null,
24 .lock = 0,
25 };
26 }
27
28 pub fn put(self: *Self, node: *Node) void {
29 node.next = null;
30
31 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
32 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
33
34 const opt_tail = self.tail;
35 self.tail = node;
36 if (opt_tail) |tail| {
37 tail.next = node;
38 } else {
39 assert(self.head == null);
40 self.head = node;
41 }
42 }
43
44 pub fn get(self: *Self) ?*Node {
45 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
46 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
47
48 const head = self.head orelse return null;
49 self.head = head.next;
50 if (head.next == null) self.tail = null;
51 return head;
52 }
53
54 pub fn isEmpty(self: *Self) bool {
55 return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null;
56 }
57
58 pub fn dump(self: *Self) void {
59 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
60 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
61
62 std.debug.warn("head: ");
63 dumpRecursive(self.head, 0);
64 std.debug.warn("tail: ");
65 dumpRecursive(self.tail, 0);
66 }
67
68 fn dumpRecursive(optional_node: ?*Node, indent: usize) void {
69 var stderr_file = std.io.getStdErr() catch return;
70 const stderr = &std.io.FileOutStream.init(&stderr_file).stream;
71 stderr.writeByteNTimes(' ', indent) catch return;
72 if (optional_node) |node| {
73 std.debug.warn("0x{x}={}\n", @ptrToInt(node), node.data);
74 dumpRecursive(node.next, indent + 1);
75 } else {
76 std.debug.warn("(null)\n");
77 }
78 }
79 };
80}
81
82const std = @import("../index.zig");
83const assert = std.debug.assert;
84
85const Context = struct {
86 allocator: *std.mem.Allocator,
87 queue: *Queue(i32),
88 put_sum: isize,
89 get_sum: isize,
90 get_count: usize,
91 puts_done: u8, // TODO make this a bool
92};
93
94// TODO add lazy evaluated build options and then put puts_per_thread behind
95// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor
96// CI we would use a less aggressive setting since at 1 core, while we still
97// want this test to pass, we need a smaller value since there is so much thrashing
98// we would also use a less aggressive setting when running in valgrind
99const puts_per_thread = 500;
100const put_thread_count = 3;
101
102test "std.atomic.Queue" {
103 var direct_allocator = std.heap.DirectAllocator.init();
104 defer direct_allocator.deinit();
105
106 var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
107 defer direct_allocator.allocator.free(plenty_of_memory);
108
109 var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
110 var a = &fixed_buffer_allocator.allocator;
111
112 var queue = Queue(i32).init();
113 var context = Context{
114 .allocator = a,
115 .queue = &queue,
116 .put_sum = 0,
117 .get_sum = 0,
118 .puts_done = 0,
119 .get_count = 0,
120 };
121
122 var putters: [put_thread_count]*std.os.Thread = undefined;
123 for (putters) |*t| {
124 t.* = try std.os.spawnThread(&context, startPuts);
125 }
126 var getters: [put_thread_count]*std.os.Thread = undefined;
127 for (getters) |*t| {
128 t.* = try std.os.spawnThread(&context, startGets);
129 }
130
131 for (putters) |t|
132 t.wait();
133 _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
134 for (getters) |t|
135 t.wait();
136
137 if (context.put_sum != context.get_sum) {
138 std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
139 }
140
141 if (context.get_count != puts_per_thread * put_thread_count) {
142 std.debug.panic(
143 "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}",
144 context.get_count,
145 u32(puts_per_thread),
146 u32(put_thread_count),
147 );
148 }
149}
150
151fn startPuts(ctx: *Context) u8 {
152 var put_count: usize = puts_per_thread;
153 var r = std.rand.DefaultPrng.init(0xdeadbeef);
154 while (put_count != 0) : (put_count -= 1) {
155 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
156 const x = @bitCast(i32, r.random.scalar(u32));
157 const node = ctx.allocator.create(Queue(i32).Node{
158 .next = undefined,
159 .data = x,
160 }) catch unreachable;
161 ctx.queue.put(node);
162 _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
163 }
164 return 0;
165}
166
167fn startGets(ctx: *Context) u8 {
168 while (true) {
169 const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1;
170
171 while (ctx.queue.get()) |node| {
172 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
173 _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
174 _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
175 }
176
177 if (last) return 0;
178 }
179}
180
181test "std.atomic.Queue single-threaded" {
182 var queue = Queue(i32).init();
183
184 var node_0 = Queue(i32).Node{
185 .data = 0,
186 .next = undefined,
187 };
188 queue.put(&node_0);
189
190 var node_1 = Queue(i32).Node{
191 .data = 1,
192 .next = undefined,
193 };
194 queue.put(&node_1);
195
196 assert(queue.get().?.data == 0);
197
198 var node_2 = Queue(i32).Node{
199 .data = 2,
200 .next = undefined,
201 };
202 queue.put(&node_2);
203
204 var node_3 = Queue(i32).Node{
205 .data = 3,
206 .next = undefined,
207 };
208 queue.put(&node_3);
209
210 assert(queue.get().?.data == 1);
211
212 assert(queue.get().?.data == 2);
213
214 var node_4 = Queue(i32).Node{
215 .data = 4,
216 .next = undefined,
217 };
218 queue.put(&node_4);
219
220 assert(queue.get().?.data == 3);
221 node_3.next = null;
222
223 assert(queue.get().?.data == 4);
224
225 assert(queue.get() == null);
226}
std/atomic/queue_mpmc.zig deleted-214
...@@ -1,214 +0,0 @@
1const builtin = @import("builtin");
2const AtomicOrder = builtin.AtomicOrder;
3const AtomicRmwOp = builtin.AtomicRmwOp;
4
5/// Many producer, many consumer, non-allocating, thread-safe, lock-free
6/// This implementation has a crippling limitation - it hangs onto node
7/// memory for 1 extra get() and 1 extra put() operation - when get() returns a node, that
8/// node must not be freed until both the next get() and the next put() completes.
9pub fn QueueMpmc(comptime T: type) type {
10 return struct {
11 head: *Node,
12 tail: *Node,
13 root: Node,
14
15 pub const Self = this;
16
17 pub const Node = struct {
18 next: ?*Node,
19 data: T,
20 };
21
22 /// TODO: well defined copy elision: https://github.com/ziglang/zig/issues/287
23 pub fn init(self: *Self) void {
24 self.root.next = null;
25 self.head = &self.root;
26 self.tail = &self.root;
27 }
28
29 pub fn put(self: *Self, node: *Node) void {
30 node.next = null;
31
32 const tail = @atomicRmw(*Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
33 _ = @atomicRmw(?*Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
34 }
35
36 /// node must not be freed until both the next get() and the next put() complete
37 pub fn get(self: *Self) ?*Node {
38 var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst);
39 while (true) {
40 const node = head.next orelse return null;
41 head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return node;
42 }
43 }
44
45 ///// This is a debug function that is not thread-safe.
46 pub fn dump(self: *Self) void {
47 std.debug.warn("head: ");
48 dumpRecursive(self.head, 0);
49 std.debug.warn("tail: ");
50 dumpRecursive(self.tail, 0);
51 }
52
53 fn dumpRecursive(optional_node: ?*Node, indent: usize) void {
54 var stderr_file = std.io.getStdErr() catch return;
55 const stderr = &std.io.FileOutStream.init(&stderr_file).stream;
56 stderr.writeByteNTimes(' ', indent) catch return;
57 if (optional_node) |node| {
58 std.debug.warn("0x{x}={}\n", @ptrToInt(node), node.data);
59 dumpRecursive(node.next, indent + 1);
60 } else {
61 std.debug.warn("(null)\n");
62 }
63 }
64 };
65}
66
67const std = @import("std");
68const assert = std.debug.assert;
69
70const Context = struct {
71 allocator: *std.mem.Allocator,
72 queue: *QueueMpmc(i32),
73 put_sum: isize,
74 get_sum: isize,
75 get_count: usize,
76 puts_done: u8, // TODO make this a bool
77};
78
79// TODO add lazy evaluated build options and then put puts_per_thread behind
80// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor
81// CI we would use a less aggressive setting since at 1 core, while we still
82// want this test to pass, we need a smaller value since there is so much thrashing
83// we would also use a less aggressive setting when running in valgrind
84const puts_per_thread = 500;
85const put_thread_count = 3;
86
87test "std.atomic.queue_mpmc" {
88 var direct_allocator = std.heap.DirectAllocator.init();
89 defer direct_allocator.deinit();
90
91 var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
92 defer direct_allocator.allocator.free(plenty_of_memory);
93
94 var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
95 var a = &fixed_buffer_allocator.allocator;
96
97 var queue: QueueMpmc(i32) = undefined;
98 queue.init();
99 var context = Context{
100 .allocator = a,
101 .queue = &queue,
102 .put_sum = 0,
103 .get_sum = 0,
104 .puts_done = 0,
105 .get_count = 0,
106 };
107
108 var putters: [put_thread_count]*std.os.Thread = undefined;
109 for (putters) |*t| {
110 t.* = try std.os.spawnThread(&context, startPuts);
111 }
112 var getters: [put_thread_count]*std.os.Thread = undefined;
113 for (getters) |*t| {
114 t.* = try std.os.spawnThread(&context, startGets);
115 }
116
117 for (putters) |t|
118 t.wait();
119 _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
120 for (getters) |t|
121 t.wait();
122
123 if (context.put_sum != context.get_sum) {
124 std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
125 }
126
127 if (context.get_count != puts_per_thread * put_thread_count) {
128 std.debug.panic(
129 "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}",
130 context.get_count,
131 u32(puts_per_thread),
132 u32(put_thread_count),
133 );
134 }
135}
136
137fn startPuts(ctx: *Context) u8 {
138 var put_count: usize = puts_per_thread;
139 var r = std.rand.DefaultPrng.init(0xdeadbeef);
140 while (put_count != 0) : (put_count -= 1) {
141 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
142 const x = @bitCast(i32, r.random.scalar(u32));
143 const node = ctx.allocator.create(QueueMpmc(i32).Node{
144 .next = undefined,
145 .data = x,
146 }) catch unreachable;
147 ctx.queue.put(node);
148 _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
149 }
150 return 0;
151}
152
153fn startGets(ctx: *Context) u8 {
154 while (true) {
155 const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1;
156
157 while (ctx.queue.get()) |node| {
158 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
159 _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
160 _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
161 }
162
163 if (last) return 0;
164 }
165}
166
167test "std.atomic.queue_mpmc single-threaded" {
168 var queue: QueueMpmc(i32) = undefined;
169 queue.init();
170
171 var node_0 = QueueMpmc(i32).Node{
172 .data = 0,
173 .next = undefined,
174 };
175 queue.put(&node_0);
176
177 var node_1 = QueueMpmc(i32).Node{
178 .data = 1,
179 .next = undefined,
180 };
181 queue.put(&node_1);
182
183 assert(queue.get().?.data == 0);
184
185 var node_2 = QueueMpmc(i32).Node{
186 .data = 2,
187 .next = undefined,
188 };
189 queue.put(&node_2);
190
191 var node_3 = QueueMpmc(i32).Node{
192 .data = 3,
193 .next = undefined,
194 };
195 queue.put(&node_3);
196
197 assert(queue.get().?.data == 1);
198
199 assert(queue.get().?.data == 2);
200
201 var node_4 = QueueMpmc(i32).Node{
202 .data = 4,
203 .next = undefined,
204 };
205 queue.put(&node_4);
206
207 assert(queue.get().?.data == 3);
208 // if we were to set node_3.next to null here, it would cause this test
209 // to fail. this demonstrates the limitation of hanging on to extra memory.
210
211 assert(queue.get().?.data == 4);
212
213 assert(queue.get() == null);
214}
std/atomic/queue_mpsc.zig deleted-185
...@@ -1,185 +0,0 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3const builtin = @import("builtin");
4const AtomicOrder = builtin.AtomicOrder;
5const AtomicRmwOp = builtin.AtomicRmwOp;
6
7/// Many producer, single consumer, non-allocating, thread-safe, lock-free
8pub fn QueueMpsc(comptime T: type) type {
9 return struct {
10 inboxes: [2]std.atomic.Stack(T),
11 outbox: std.atomic.Stack(T),
12 inbox_index: usize,
13
14 pub const Self = this;
15
16 pub const Node = std.atomic.Stack(T).Node;
17
18 /// Not thread-safe. The call to init() must complete before any other functions are called.
19 /// No deinitialization required.
20 pub fn init() Self {
21 return Self{
22 .inboxes = []std.atomic.Stack(T){
23 std.atomic.Stack(T).init(),
24 std.atomic.Stack(T).init(),
25 },
26 .outbox = std.atomic.Stack(T).init(),
27 .inbox_index = 0,
28 };
29 }
30
31 /// Fully thread-safe. put() may be called from any thread at any time.
32 pub fn put(self: *Self, node: *Node) void {
33 const inbox_index = @atomicLoad(usize, &self.inbox_index, AtomicOrder.SeqCst);
34 const inbox = &self.inboxes[inbox_index];
35 inbox.push(node);
36 }
37
38 /// Must be called by only 1 consumer at a time. Every call to get() and isEmpty() must complete before
39 /// the next call to get().
40 pub fn get(self: *Self) ?*Node {
41 if (self.outbox.pop()) |node| {
42 return node;
43 }
44 const prev_inbox_index = @atomicRmw(usize, &self.inbox_index, AtomicRmwOp.Xor, 0x1, AtomicOrder.SeqCst);
45 const prev_inbox = &self.inboxes[prev_inbox_index];
46 while (prev_inbox.pop()) |node| {
47 self.outbox.push(node);
48 }
49 return self.outbox.pop();
50 }
51
52 /// Must be called by only 1 consumer at a time. Every call to get() and isEmpty() must complete before
53 /// the next call to isEmpty().
54 pub fn isEmpty(self: *Self) bool {
55 if (!self.outbox.isEmpty()) return false;
56 const prev_inbox_index = @atomicRmw(usize, &self.inbox_index, AtomicRmwOp.Xor, 0x1, AtomicOrder.SeqCst);
57 const prev_inbox = &self.inboxes[prev_inbox_index];
58 while (prev_inbox.pop()) |node| {
59 self.outbox.push(node);
60 }
61 return self.outbox.isEmpty();
62 }
63
64 /// For debugging only. No API guarantees about what this does.
65 pub fn dump(self: *Self) void {
66 {
67 var it = self.outbox.root;
68 while (it) |node| {
69 std.debug.warn("0x{x} -> ", @ptrToInt(node));
70 it = node.next;
71 }
72 }
73 const inbox_index = self.inbox_index;
74 const inboxes = []*std.atomic.Stack(T){
75 &self.inboxes[self.inbox_index],
76 &self.inboxes[1 - self.inbox_index],
77 };
78 for (inboxes) |inbox| {
79 var it = inbox.root;
80 while (it) |node| {
81 std.debug.warn("0x{x} -> ", @ptrToInt(node));
82 it = node.next;
83 }
84 }
85
86 std.debug.warn("null\n");
87 }
88 };
89}
90
91const Context = struct {
92 allocator: *std.mem.Allocator,
93 queue: *QueueMpsc(i32),
94 put_sum: isize,
95 get_sum: isize,
96 get_count: usize,
97 puts_done: u8, // TODO make this a bool
98};
99
100// TODO add lazy evaluated build options and then put puts_per_thread behind
101// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor
102// CI we would use a less aggressive setting since at 1 core, while we still
103// want this test to pass, we need a smaller value since there is so much thrashing
104// we would also use a less aggressive setting when running in valgrind
105const puts_per_thread = 500;
106const put_thread_count = 3;
107
108test "std.atomic.queue_mpsc" {
109 var direct_allocator = std.heap.DirectAllocator.init();
110 defer direct_allocator.deinit();
111
112 var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
113 defer direct_allocator.allocator.free(plenty_of_memory);
114
115 var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
116 var a = &fixed_buffer_allocator.allocator;
117
118 var queue = QueueMpsc(i32).init();
119 var context = Context{
120 .allocator = a,
121 .queue = &queue,
122 .put_sum = 0,
123 .get_sum = 0,
124 .puts_done = 0,
125 .get_count = 0,
126 };
127
128 var putters: [put_thread_count]*std.os.Thread = undefined;
129 for (putters) |*t| {
130 t.* = try std.os.spawnThread(&context, startPuts);
131 }
132 var getters: [1]*std.os.Thread = undefined;
133 for (getters) |*t| {
134 t.* = try std.os.spawnThread(&context, startGets);
135 }
136
137 for (putters) |t|
138 t.wait();
139 _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
140 for (getters) |t|
141 t.wait();
142
143 if (context.put_sum != context.get_sum) {
144 std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
145 }
146
147 if (context.get_count != puts_per_thread * put_thread_count) {
148 std.debug.panic(
149 "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}",
150 context.get_count,
151 u32(puts_per_thread),
152 u32(put_thread_count),
153 );
154 }
155}
156
157fn startPuts(ctx: *Context) u8 {
158 var put_count: usize = puts_per_thread;
159 var r = std.rand.DefaultPrng.init(0xdeadbeef);
160 while (put_count != 0) : (put_count -= 1) {
161 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
162 const x = @bitCast(i32, r.random.scalar(u32));
163 const node = ctx.allocator.create(QueueMpsc(i32).Node{
164 .next = undefined,
165 .data = x,
166 }) catch unreachable;
167 ctx.queue.put(node);
168 _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
169 }
170 return 0;
171}
172
173fn startGets(ctx: *Context) u8 {
174 while (true) {
175 const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1;
176
177 while (ctx.queue.get()) |node| {
178 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
179 _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
180 _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
181 }
182
183 if (last) return 0;
184 }
185}
std/atomic/stack.zig+20-12
...@@ -1,10 +1,13 @@...@@ -1,10 +1,13 @@
1const assert = std.debug.assert;
1const builtin = @import("builtin");2const builtin = @import("builtin");
2const AtomicOrder = builtin.AtomicOrder;3const AtomicOrder = builtin.AtomicOrder;
34
4/// Many reader, many writer, non-allocating, thread-safe, lock-free5/// Many reader, many writer, non-allocating, thread-safe
6/// Uses a spinlock to protect push() and pop()
5pub fn Stack(comptime T: type) type {7pub fn Stack(comptime T: type) type {
6 return struct {8 return struct {
7 root: ?*Node,9 root: ?*Node,
10 lock: u8,
811
9 pub const Self = this;12 pub const Self = this;
1013
...@@ -14,7 +17,10 @@ pub fn Stack(comptime T: type) type {...@@ -14,7 +17,10 @@ pub fn Stack(comptime T: type) type {
14 };17 };
1518
16 pub fn init() Self {19 pub fn init() Self {
17 return Self{ .root = null };20 return Self{
21 .root = null,
22 .lock = 0,
23 };
18 }24 }
1925
20 /// push operation, but only if you are the first item in the stack. if you did not succeed in26 /// push operation, but only if you are the first item in the stack. if you did not succeed in
...@@ -25,18 +31,20 @@ pub fn Stack(comptime T: type) type {...@@ -25,18 +31,20 @@ pub fn Stack(comptime T: type) type {
25 }31 }
2632
27 pub fn push(self: *Self, node: *Node) void {33 pub fn push(self: *Self, node: *Node) void {
28 var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst);34 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
29 while (true) {35 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
30 node.next = root;36
31 root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse break;37 node.next = self.root;
32 }38 self.root = node;
33 }39 }
3440
35 pub fn pop(self: *Self) ?*Node {41 pub fn pop(self: *Self) ?*Node {
36 var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst);42 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
37 while (true) {43 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
38 root = @cmpxchgWeak(?*Node, &self.root, root, (root orelse return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return root;44
39 }45 const root = self.root orelse return null;
46 self.root = root.next;
47 return root;
40 }48 }
4149
42 pub fn isEmpty(self: *Self) bool {50 pub fn isEmpty(self: *Self) bool {
...@@ -45,7 +53,7 @@ pub fn Stack(comptime T: type) type {...@@ -45,7 +53,7 @@ pub fn Stack(comptime T: type) type {
45 };53 };
46}54}
4755
48const std = @import("std");56const std = @import("../index.zig");
49const Context = struct {57const Context = struct {
50 allocator: *std.mem.Allocator,58 allocator: *std.mem.Allocator,
51 stack: *Stack(i32),59 stack: *Stack(i32),
std/build.zig+22
...@@ -1596,6 +1596,8 @@ pub const TestStep = struct {...@@ -1596,6 +1596,8 @@ pub const TestStep = struct {
1596 target: Target,1596 target: Target,
1597 exec_cmd_args: ?[]const ?[]const u8,1597 exec_cmd_args: ?[]const ?[]const u8,
1598 include_dirs: ArrayList([]const u8),1598 include_dirs: ArrayList([]const u8),
1599 lib_paths: ArrayList([]const u8),
1600 object_files: ArrayList([]const u8),
15991601
1600 pub fn init(builder: *Builder, root_src: []const u8) TestStep {1602 pub fn init(builder: *Builder, root_src: []const u8) TestStep {
1601 const step_name = builder.fmt("test {}", root_src);1603 const step_name = builder.fmt("test {}", root_src);
...@@ -1611,9 +1613,15 @@ pub const TestStep = struct {...@@ -1611,9 +1613,15 @@ pub const TestStep = struct {
1611 .target = Target{ .Native = {} },1613 .target = Target{ .Native = {} },
1612 .exec_cmd_args = null,1614 .exec_cmd_args = null,
1613 .include_dirs = ArrayList([]const u8).init(builder.allocator),1615 .include_dirs = ArrayList([]const u8).init(builder.allocator),
1616 .lib_paths = ArrayList([]const u8).init(builder.allocator),
1617 .object_files = ArrayList([]const u8).init(builder.allocator),
1614 };1618 };
1615 }1619 }
16161620
1621 pub fn addLibPath(self: *TestStep, path: []const u8) void {
1622 self.lib_paths.append(path) catch unreachable;
1623 }
1624
1617 pub fn setVerbose(self: *TestStep, value: bool) void {1625 pub fn setVerbose(self: *TestStep, value: bool) void {
1618 self.verbose = value;1626 self.verbose = value;
1619 }1627 }
...@@ -1638,6 +1646,10 @@ pub const TestStep = struct {...@@ -1638,6 +1646,10 @@ pub const TestStep = struct {
1638 self.filter = text;1646 self.filter = text;
1639 }1647 }
16401648
1649 pub fn addObjectFile(self: *TestStep, path: []const u8) void {
1650 self.object_files.append(path) catch unreachable;
1651 }
1652
1641 pub fn setTarget(self: *TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void {1653 pub fn setTarget(self: *TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void {
1642 self.target = Target{1654 self.target = Target{
1643 .Cross = CrossTarget{1655 .Cross = CrossTarget{
...@@ -1699,6 +1711,11 @@ pub const TestStep = struct {...@@ -1699,6 +1711,11 @@ pub const TestStep = struct {
1699 try zig_args.append(self.name_prefix);1711 try zig_args.append(self.name_prefix);
1700 }1712 }
17011713
1714 for (self.object_files.toSliceConst()) |object_file| {
1715 try zig_args.append("--object");
1716 try zig_args.append(builder.pathFromRoot(object_file));
1717 }
1718
1702 {1719 {
1703 var it = self.link_libs.iterator();1720 var it = self.link_libs.iterator();
1704 while (true) {1721 while (true) {
...@@ -1734,6 +1751,11 @@ pub const TestStep = struct {...@@ -1734,6 +1751,11 @@ pub const TestStep = struct {
1734 try zig_args.append(rpath);1751 try zig_args.append(rpath);
1735 }1752 }
17361753
1754 for (self.lib_paths.toSliceConst()) |lib_path| {
1755 try zig_args.append("--library-path");
1756 try zig_args.append(lib_path);
1757 }
1758
1737 for (builder.lib_paths.toSliceConst()) |lib_path| {1759 for (builder.lib_paths.toSliceConst()) |lib_path| {
1738 try zig_args.append("--library-path");1760 try zig_args.append("--library-path");
1739 try zig_args.append(lib_path);1761 try zig_args.append(lib_path);
std/c/darwin.zig+1-1
...@@ -44,7 +44,7 @@ pub const timezone = extern struct {...@@ -44,7 +44,7 @@ pub const timezone = extern struct {
44 tz_dsttime: i32,44 tz_dsttime: i32,
45};45};
4646
47pub const mach_timebase_info_data = struct {47pub const mach_timebase_info_data = extern struct {
48 numer: u32,48 numer: u32,
49 denom: u32,49 denom: u32,
50};50};
std/event.zig+4
...@@ -3,6 +3,8 @@ pub const Loop = @import("event/loop.zig").Loop;...@@ -3,6 +3,8 @@ pub const Loop = @import("event/loop.zig").Loop;
3pub const Lock = @import("event/lock.zig").Lock;3pub const Lock = @import("event/lock.zig").Lock;
4pub const tcp = @import("event/tcp.zig");4pub const tcp = @import("event/tcp.zig");
5pub const Channel = @import("event/channel.zig").Channel;5pub const Channel = @import("event/channel.zig").Channel;
6pub const Group = @import("event/group.zig").Group;
7pub const Future = @import("event/future.zig").Future;
68
7test "import event tests" {9test "import event tests" {
8 _ = @import("event/locked.zig");10 _ = @import("event/locked.zig");
...@@ -10,4 +12,6 @@ test "import event tests" {...@@ -10,4 +12,6 @@ test "import event tests" {
10 _ = @import("event/lock.zig");12 _ = @import("event/lock.zig");
11 _ = @import("event/tcp.zig");13 _ = @import("event/tcp.zig");
12 _ = @import("event/channel.zig");14 _ = @import("event/channel.zig");
15 _ = @import("event/group.zig");
16 _ = @import("event/future.zig");
13}17}
std/event/channel.zig+6-6
...@@ -12,8 +12,8 @@ pub fn Channel(comptime T: type) type {...@@ -12,8 +12,8 @@ pub fn Channel(comptime T: type) type {
12 return struct {12 return struct {
13 loop: *Loop,13 loop: *Loop,
1414
15 getters: std.atomic.QueueMpsc(GetNode),15 getters: std.atomic.Queue(GetNode),
16 putters: std.atomic.QueueMpsc(PutNode),16 putters: std.atomic.Queue(PutNode),
17 get_count: usize,17 get_count: usize,
18 put_count: usize,18 put_count: usize,
19 dispatch_lock: u8, // TODO make this a bool19 dispatch_lock: u8, // TODO make this a bool
...@@ -46,8 +46,8 @@ pub fn Channel(comptime T: type) type {...@@ -46,8 +46,8 @@ pub fn Channel(comptime T: type) type {
46 .buffer_index = 0,46 .buffer_index = 0,
47 .dispatch_lock = 0,47 .dispatch_lock = 0,
48 .need_dispatch = 0,48 .need_dispatch = 0,
49 .getters = std.atomic.QueueMpsc(GetNode).init(),49 .getters = std.atomic.Queue(GetNode).init(),
50 .putters = std.atomic.QueueMpsc(PutNode).init(),50 .putters = std.atomic.Queue(PutNode).init(),
51 .get_count = 0,51 .get_count = 0,
52 .put_count = 0,52 .put_count = 0,
53 });53 });
...@@ -81,7 +81,7 @@ pub fn Channel(comptime T: type) type {...@@ -81,7 +81,7 @@ pub fn Channel(comptime T: type) type {
81 .next = undefined,81 .next = undefined,
82 .data = handle,82 .data = handle,
83 };83 };
84 var queue_node = std.atomic.QueueMpsc(PutNode).Node{84 var queue_node = std.atomic.Queue(PutNode).Node{
85 .data = PutNode{85 .data = PutNode{
86 .tick_node = &my_tick_node,86 .tick_node = &my_tick_node,
87 .data = data,87 .data = data,
...@@ -111,7 +111,7 @@ pub fn Channel(comptime T: type) type {...@@ -111,7 +111,7 @@ pub fn Channel(comptime T: type) type {
111 .next = undefined,111 .next = undefined,
112 .data = handle,112 .data = handle,
113 };113 };
114 var queue_node = std.atomic.QueueMpsc(GetNode).Node{114 var queue_node = std.atomic.Queue(GetNode).Node{
115 .data = GetNode{115 .data = GetNode{
116 .ptr = &result,116 .ptr = &result,
117 .tick_node = &my_tick_node,117 .tick_node = &my_tick_node,
std/event/future.zig created+97
...@@ -0,0 +1,97 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3const builtin = @import("builtin");
4const AtomicRmwOp = builtin.AtomicRmwOp;
5const AtomicOrder = builtin.AtomicOrder;
6const Lock = std.event.Lock;
7const Loop = std.event.Loop;
8
9/// This is a value that starts out unavailable, until a value is put().
10/// While it is unavailable, coroutines suspend when they try to get() it,
11/// and then are resumed when the value is put().
12/// At this point the value remains forever available, and another put() is not allowed.
13pub fn Future(comptime T: type) type {
14 return struct {
15 lock: Lock,
16 data: T,
17 available: u8, // TODO make this a bool
18
19 const Self = this;
20 const Queue = std.atomic.Queue(promise);
21
22 pub fn init(loop: *Loop) Self {
23 return Self{
24 .lock = Lock.initLocked(loop),
25 .available = 0,
26 .data = undefined,
27 };
28 }
29
30 /// Obtain the value. If it's not available, wait until it becomes
31 /// available.
32 /// Thread-safe.
33 pub async fn get(self: *Self) *T {
34 if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) {
35 return &self.data;
36 }
37 const held = await (async self.lock.acquire() catch unreachable);
38 held.release();
39
40 return &self.data;
41 }
42
43 /// Make the data become available. May be called only once.
44 /// Before calling this, modify the `data` property.
45 pub fn resolve(self: *Self) void {
46 const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
47 assert(prev == 0); // put() called twice
48 Lock.Held.release(Lock.Held{ .lock = &self.lock });
49 }
50 };
51}
52
53test "std.event.Future" {
54 var da = std.heap.DirectAllocator.init();
55 defer da.deinit();
56
57 const allocator = &da.allocator;
58
59 var loop: Loop = undefined;
60 try loop.initMultiThreaded(allocator);
61 defer loop.deinit();
62
63 const handle = try async<allocator> testFuture(&loop);
64 defer cancel handle;
65
66 loop.run();
67}
68
69async fn testFuture(loop: *Loop) void {
70 suspend |p| {
71 resume p;
72 }
73 var future = Future(i32).init(loop);
74
75 const a = async waitOnFuture(&future) catch @panic("memory");
76 const b = async waitOnFuture(&future) catch @panic("memory");
77 const c = async resolveFuture(&future) catch @panic("memory");
78
79 const result = (await a) + (await b);
80 cancel c;
81 assert(result == 12);
82}
83
84async fn waitOnFuture(future: *Future(i32)) i32 {
85 suspend |p| {
86 resume p;
87 }
88 return (await (async future.get() catch @panic("memory"))).*;
89}
90
91async fn resolveFuture(future: *Future(i32)) void {
92 suspend |p| {
93 resume p;
94 }
95 future.data = 6;
96 future.resolve();
97}
std/event/group.zig created+158
...@@ -0,0 +1,158 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const Lock = std.event.Lock;
4const Loop = std.event.Loop;
5const AtomicRmwOp = builtin.AtomicRmwOp;
6const AtomicOrder = builtin.AtomicOrder;
7const assert = std.debug.assert;
8
9/// ReturnType should be `void` or `E!void`
10pub fn Group(comptime ReturnType: type) type {
11 return struct {
12 coro_stack: Stack,
13 alloc_stack: Stack,
14 lock: Lock,
15
16 const Self = this;
17
18 const Error = switch (@typeInfo(ReturnType)) {
19 builtin.TypeId.ErrorUnion => |payload| payload.error_set,
20 else => void,
21 };
22 const Stack = std.atomic.Stack(promise->ReturnType);
23
24 pub fn init(loop: *Loop) Self {
25 return Self{
26 .coro_stack = Stack.init(),
27 .alloc_stack = Stack.init(),
28 .lock = Lock.init(loop),
29 };
30 }
31
32 /// Add a promise to the group. Thread-safe.
33 pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) {
34 const node = try self.lock.loop.allocator.create(Stack.Node{
35 .next = undefined,
36 .data = handle,
37 });
38 self.alloc_stack.push(node);
39 }
40
41 /// This is equivalent to an async call, but the async function is added to the group, instead
42 /// of returning a promise. func must be async and have return type void.
43 /// Thread-safe.
44 pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) {
45 const S = struct {
46 async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType {
47 // TODO this is a hack to make the memory following be inside the coro frame
48 suspend |p| {
49 var my_node: Stack.Node = undefined;
50 node.* = &my_node;
51 resume p;
52 }
53
54 // TODO this allocation elision should be guaranteed because we await it in
55 // this coro frame
56 return await (async func(args2) catch unreachable);
57 }
58 };
59 var node: *Stack.Node = undefined;
60 const handle = try async<self.lock.loop.allocator> S.asyncFunc(&node, args);
61 node.* = Stack.Node{
62 .next = undefined,
63 .data = handle,
64 };
65 self.coro_stack.push(node);
66 }
67
68 /// Wait for all the calls and promises of the group to complete.
69 /// Thread-safe.
70 pub async fn wait(self: *Self) ReturnType {
71 // TODO catch unreachable because the allocation can be grouped with
72 // the coro frame allocation
73 const held = await (async self.lock.acquire() catch unreachable);
74 defer held.release();
75
76 while (self.coro_stack.pop()) |node| {
77 if (Error == void) {
78 await node.data;
79 } else {
80 (await node.data) catch |err| {
81 self.cancelAll();
82 return err;
83 };
84 }
85 }
86 while (self.alloc_stack.pop()) |node| {
87 const handle = node.data;
88 self.lock.loop.allocator.destroy(node);
89 if (Error == void) {
90 await handle;
91 } else {
92 (await handle) catch |err| {
93 self.cancelAll();
94 return err;
95 };
96 }
97 }
98 }
99
100 /// Cancel all the outstanding promises. May only be called if wait was never called.
101 pub fn cancelAll(self: *Self) void {
102 while (self.coro_stack.pop()) |node| {
103 cancel node.data;
104 }
105 while (self.alloc_stack.pop()) |node| {
106 cancel node.data;
107 self.lock.loop.allocator.destroy(node);
108 }
109 }
110 };
111}
112
113test "std.event.Group" {
114 var da = std.heap.DirectAllocator.init();
115 defer da.deinit();
116
117 const allocator = &da.allocator;
118
119 var loop: Loop = undefined;
120 try loop.initMultiThreaded(allocator);
121 defer loop.deinit();
122
123 const handle = try async<allocator> testGroup(&loop);
124 defer cancel handle;
125
126 loop.run();
127}
128
129async fn testGroup(loop: *Loop) void {
130 var count: usize = 0;
131 var group = Group(void).init(loop);
132 group.add(async sleepALittle(&count) catch @panic("memory")) catch @panic("memory");
133 group.call(increaseByTen, &count) catch @panic("memory");
134 await (async group.wait() catch @panic("memory"));
135 assert(count == 11);
136
137 var another = Group(error!void).init(loop);
138 another.add(async somethingElse() catch @panic("memory")) catch @panic("memory");
139 another.call(doSomethingThatFails) catch @panic("memory");
140 std.debug.assertError(await (async another.wait() catch @panic("memory")), error.ItBroke);
141}
142
143async fn sleepALittle(count: *usize) void {
144 std.os.time.sleep(0, 1000000);
145 _ = @atomicRmw(usize, count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
146}
147
148async fn increaseByTen(count: *usize) void {
149 var i: usize = 0;
150 while (i < 10) : (i += 1) {
151 _ = @atomicRmw(usize, count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
152 }
153}
154
155async fn doSomethingThatFails() error!void {}
156async fn somethingElse() error!void {
157 return error.ItBroke;
158}
std/event/lock.zig+11-2
...@@ -15,7 +15,7 @@ pub const Lock = struct {...@@ -15,7 +15,7 @@ pub const Lock = struct {
15 queue: Queue,15 queue: Queue,
16 queue_empty_bit: u8, // TODO make this a bool16 queue_empty_bit: u8, // TODO make this a bool
1717
18 const Queue = std.atomic.QueueMpsc(promise);18 const Queue = std.atomic.Queue(promise);
1919
20 pub const Held = struct {20 pub const Held = struct {
21 lock: *Lock,21 lock: *Lock,
...@@ -73,6 +73,15 @@ pub const Lock = struct {...@@ -73,6 +73,15 @@ pub const Lock = struct {
73 };73 };
74 }74 }
7575
76 pub fn initLocked(loop: *Loop) Lock {
77 return Lock{
78 .loop = loop,
79 .shared_bit = 1,
80 .queue = Queue.init(),
81 .queue_empty_bit = 1,
82 };
83 }
84
76 /// Must be called when not locked. Not thread safe.85 /// Must be called when not locked. Not thread safe.
77 /// All calls to acquire() and release() must complete before calling deinit().86 /// All calls to acquire() and release() must complete before calling deinit().
78 pub fn deinit(self: *Lock) void {87 pub fn deinit(self: *Lock) void {
...@@ -81,7 +90,7 @@ pub const Lock = struct {...@@ -81,7 +90,7 @@ pub const Lock = struct {
81 }90 }
8291
83 pub async fn acquire(self: *Lock) Held {92 pub async fn acquire(self: *Lock) Held {
84 s: suspend |handle| {93 suspend |handle| {
85 // TODO explicitly put this memory in the coroutine frame #119494 // TODO explicitly put this memory in the coroutine frame #1194
86 var my_tick_node = Loop.NextTickNode{95 var my_tick_node = Loop.NextTickNode{
87 .data = handle,96 .data = handle,
std/event/loop.zig+18-4
...@@ -9,7 +9,7 @@ const AtomicOrder = builtin.AtomicOrder;...@@ -9,7 +9,7 @@ const AtomicOrder = builtin.AtomicOrder;
99
10pub const Loop = struct {10pub const Loop = struct {
11 allocator: *mem.Allocator,11 allocator: *mem.Allocator,
12 next_tick_queue: std.atomic.QueueMpsc(promise),12 next_tick_queue: std.atomic.Queue(promise),
13 os_data: OsData,13 os_data: OsData,
14 final_resume_node: ResumeNode,14 final_resume_node: ResumeNode,
15 dispatch_lock: u8, // TODO make this a bool15 dispatch_lock: u8, // TODO make this a bool
...@@ -21,7 +21,7 @@ pub const Loop = struct {...@@ -21,7 +21,7 @@ pub const Loop = struct {
21 available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd),21 available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd),
22 eventfd_resume_nodes: []std.atomic.Stack(ResumeNode.EventFd).Node,22 eventfd_resume_nodes: []std.atomic.Stack(ResumeNode.EventFd).Node,
2323
24 pub const NextTickNode = std.atomic.QueueMpsc(promise).Node;24 pub const NextTickNode = std.atomic.Queue(promise).Node;
2525
26 pub const ResumeNode = struct {26 pub const ResumeNode = struct {
27 id: Id,27 id: Id,
...@@ -77,7 +77,7 @@ pub const Loop = struct {...@@ -77,7 +77,7 @@ pub const Loop = struct {
77 .pending_event_count = 0,77 .pending_event_count = 0,
78 .allocator = allocator,78 .allocator = allocator,
79 .os_data = undefined,79 .os_data = undefined,
80 .next_tick_queue = std.atomic.QueueMpsc(promise).init(),80 .next_tick_queue = std.atomic.Queue(promise).init(),
81 .dispatch_lock = 1, // start locked so threads go directly into epoll wait81 .dispatch_lock = 1, // start locked so threads go directly into epoll wait
82 .extra_threads = undefined,82 .extra_threads = undefined,
83 .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(),83 .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(),
...@@ -101,7 +101,6 @@ pub const Loop = struct {...@@ -101,7 +101,6 @@ pub const Loop = struct {
101 errdefer self.deinitOsData();101 errdefer self.deinitOsData();
102 }102 }
103103
104 /// must call stop before deinit
105 pub fn deinit(self: *Loop) void {104 pub fn deinit(self: *Loop) void {
106 self.deinitOsData();105 self.deinitOsData();
107 self.allocator.free(self.extra_threads);106 self.allocator.free(self.extra_threads);
...@@ -382,6 +381,21 @@ pub const Loop = struct {...@@ -382,6 +381,21 @@ pub const Loop = struct {
382 return async<self.allocator> S.asyncFunc(self, &handle, args);381 return async<self.allocator> S.asyncFunc(self, &handle, args);
383 }382 }
384383
384 /// Awaiting a yield lets the event loop run, starting any unstarted async operations.
385 /// Note that async operations automatically start when a function yields for any other reason,
386 /// for example, when async I/O is performed. This function is intended to be used only when
387 /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O
388 /// is performed.
389 pub async fn yield(self: *Loop) void {
390 suspend |p| {
391 var my_tick_node = Loop.NextTickNode{
392 .next = undefined,
393 .data = p,
394 };
395 loop.onNextTick(&my_tick_node);
396 }
397 }
398
385 fn workerRun(self: *Loop) void {399 fn workerRun(self: *Loop) void {
386 start_over: while (true) {400 start_over: while (true) {
387 if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {401 if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
std/heap.zig+77-3
...@@ -302,8 +302,17 @@ pub const FixedBufferAllocator = struct {...@@ -302,8 +302,17 @@ pub const FixedBufferAllocator = struct {
302 }302 }
303303
304 fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {304 fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
305 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
306 assert(old_mem.len <= self.end_index);
305 if (new_size <= old_mem.len) {307 if (new_size <= old_mem.len) {
306 return old_mem[0..new_size];308 return old_mem[0..new_size];
309 } else if (old_mem.ptr == self.buffer.ptr + self.end_index - old_mem.len) {
310 const start_index = self.end_index - old_mem.len;
311 const new_end_index = start_index + new_size;
312 if (new_end_index > self.buffer.len) return error.OutOfMemory;
313 const result = self.buffer[start_index..new_end_index];
314 self.end_index = new_end_index;
315 return result;
307 } else {316 } else {
308 const result = try alloc(allocator, new_size, alignment);317 const result = try alloc(allocator, new_size, alignment);
309 mem.copy(u8, result, old_mem);318 mem.copy(u8, result, old_mem);
...@@ -442,6 +451,7 @@ test "DirectAllocator" {...@@ -442,6 +451,7 @@ test "DirectAllocator" {
442451
443 const allocator = &direct_allocator.allocator;452 const allocator = &direct_allocator.allocator;
444 try testAllocator(allocator);453 try testAllocator(allocator);
454 try testAllocatorAligned(allocator, 16);
445 try testAllocatorLargeAlignment(allocator);455 try testAllocatorLargeAlignment(allocator);
446}456}
447457
...@@ -453,6 +463,7 @@ test "ArenaAllocator" {...@@ -453,6 +463,7 @@ test "ArenaAllocator" {
453 defer arena_allocator.deinit();463 defer arena_allocator.deinit();
454464
455 try testAllocator(&arena_allocator.allocator);465 try testAllocator(&arena_allocator.allocator);
466 try testAllocatorAligned(&arena_allocator.allocator, 16);
456 try testAllocatorLargeAlignment(&arena_allocator.allocator);467 try testAllocatorLargeAlignment(&arena_allocator.allocator);
457}468}
458469
...@@ -461,35 +472,98 @@ test "FixedBufferAllocator" {...@@ -461,35 +472,98 @@ test "FixedBufferAllocator" {
461 var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);472 var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
462473
463 try testAllocator(&fixed_buffer_allocator.allocator);474 try testAllocator(&fixed_buffer_allocator.allocator);
475 try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);
464 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);476 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
465}477}
466478
479test "FixedBufferAllocator Reuse memory on realloc" {
480 var small_fixed_buffer: [10]u8 = undefined;
481 // check if we re-use the memory
482 {
483 var fixed_buffer_allocator = FixedBufferAllocator.init(small_fixed_buffer[0..]);
484
485 var slice0 = try fixed_buffer_allocator.allocator.alloc(u8, 5);
486 assert(slice0.len == 5);
487 var slice1 = try fixed_buffer_allocator.allocator.realloc(u8, slice0, 10);
488 assert(slice1.ptr == slice0.ptr);
489 assert(slice1.len == 10);
490 debug.assertError(fixed_buffer_allocator.allocator.realloc(u8, slice1, 11), error.OutOfMemory);
491 }
492 // check that we don't re-use the memory if it's not the most recent block
493 {
494 var fixed_buffer_allocator = FixedBufferAllocator.init(small_fixed_buffer[0..]);
495
496 var slice0 = try fixed_buffer_allocator.allocator.alloc(u8, 2);
497 slice0[0] = 1;
498 slice0[1] = 2;
499 var slice1 = try fixed_buffer_allocator.allocator.alloc(u8, 2);
500 var slice2 = try fixed_buffer_allocator.allocator.realloc(u8, slice0, 4);
501 assert(slice0.ptr != slice2.ptr);
502 assert(slice1.ptr != slice2.ptr);
503 assert(slice2[0] == 1);
504 assert(slice2[1] == 2);
505 }
506}
507
467test "ThreadSafeFixedBufferAllocator" {508test "ThreadSafeFixedBufferAllocator" {
468 var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);509 var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
469510
470 try testAllocator(&fixed_buffer_allocator.allocator);511 try testAllocator(&fixed_buffer_allocator.allocator);
512 try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);
471 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);513 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
472}514}
473515
474fn testAllocator(allocator: *mem.Allocator) !void {516fn testAllocator(allocator: *mem.Allocator) !void {
475 var slice = try allocator.alloc(*i32, 100);517 var slice = try allocator.alloc(*i32, 100);
476518 assert(slice.len == 100);
477 for (slice) |*item, i| {519 for (slice) |*item, i| {
478 item.* = try allocator.create(@intCast(i32, i));520 item.* = try allocator.create(@intCast(i32, i));
479 }521 }
480522
481 for (slice) |item, i| {523 slice = try allocator.realloc(*i32, slice, 20000);
524 assert(slice.len == 20000);
525
526 for (slice[0..100]) |item, i| {
527 assert(item.* == @intCast(i32, i));
482 allocator.destroy(item);528 allocator.destroy(item);
483 }529 }
484530
485 slice = try allocator.realloc(*i32, slice, 20000);
486 slice = try allocator.realloc(*i32, slice, 50);531 slice = try allocator.realloc(*i32, slice, 50);
532 assert(slice.len == 50);
487 slice = try allocator.realloc(*i32, slice, 25);533 slice = try allocator.realloc(*i32, slice, 25);
534 assert(slice.len == 25);
535 slice = try allocator.realloc(*i32, slice, 0);
536 assert(slice.len == 0);
488 slice = try allocator.realloc(*i32, slice, 10);537 slice = try allocator.realloc(*i32, slice, 10);
538 assert(slice.len == 10);
489539
490 allocator.free(slice);540 allocator.free(slice);
491}541}
492542
543fn testAllocatorAligned(allocator: *mem.Allocator, comptime alignment: u29) !void {
544 // initial
545 var slice = try allocator.alignedAlloc(u8, alignment, 10);
546 assert(slice.len == 10);
547 // grow
548 slice = try allocator.alignedRealloc(u8, alignment, slice, 100);
549 assert(slice.len == 100);
550 // shrink
551 slice = try allocator.alignedRealloc(u8, alignment, slice, 10);
552 assert(slice.len == 10);
553 // go to zero
554 slice = try allocator.alignedRealloc(u8, alignment, slice, 0);
555 assert(slice.len == 0);
556 // realloc from zero
557 slice = try allocator.alignedRealloc(u8, alignment, slice, 100);
558 assert(slice.len == 100);
559 // shrink with shrink
560 slice = allocator.alignedShrink(u8, alignment, slice, 10);
561 assert(slice.len == 10);
562 // shrink to zero
563 slice = allocator.alignedShrink(u8, alignment, slice, 0);
564 assert(slice.len == 0);
565}
566
493fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void {567fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void {
494 //Maybe a platform's page_size is actually the same as or568 //Maybe a platform's page_size is actually the same as or
495 // very near usize?569 // very near usize?
std/macho.zig+1-1
...@@ -42,7 +42,7 @@ pub const Symbol = struct {...@@ -42,7 +42,7 @@ pub const Symbol = struct {
42 name: []const u8,42 name: []const u8,
43 address: u64,43 address: u64,
4444
45 fn addressLessThan(lhs: *const Symbol, rhs: *const Symbol) bool {45 fn addressLessThan(lhs: Symbol, rhs: Symbol) bool {
46 return lhs.address < rhs.address;46 return lhs.address < rhs.address;
47 }47 }
48};48};
std/mem.zig+23-2
...@@ -23,7 +23,10 @@ pub const Allocator = struct {...@@ -23,7 +23,10 @@ pub const Allocator = struct {
23 /// * this function must return successfully.23 /// * this function must return successfully.
24 /// * alignment <= alignment of old_mem.ptr24 /// * alignment <= alignment of old_mem.ptr
25 ///25 ///
26 /// The returned newly allocated memory is undefined.26 /// When `reallocFn` returns,
27 /// `return_value[0..min(old_mem.len, new_byte_count)]` must be the same
28 /// as `old_mem` was when `reallocFn` is called. The bytes of
29 /// `return_value[old_mem.len..]` have undefined values.
27 /// `alignment` is guaranteed to be >= 130 /// `alignment` is guaranteed to be >= 1
28 /// `alignment` is guaranteed to be a power of 231 /// `alignment` is guaranteed to be a power of 2
29 reallocFn: fn (self: *Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,32 reallocFn: fn (self: *Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,
...@@ -71,7 +74,7 @@ pub const Allocator = struct {...@@ -71,7 +74,7 @@ pub const Allocator = struct {
7174
72 pub fn alignedRealloc(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T {75 pub fn alignedRealloc(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T {
73 if (old_mem.len == 0) {76 if (old_mem.len == 0) {
74 return self.alloc(T, n);77 return self.alignedAlloc(T, alignment, n);
75 }78 }
76 if (n == 0) {79 if (n == 0) {
77 self.free(old_mem);80 self.free(old_mem);
...@@ -125,6 +128,7 @@ pub const Allocator = struct {...@@ -125,6 +128,7 @@ pub const Allocator = struct {
125128
126/// Copy all of source into dest at position 0.129/// Copy all of source into dest at position 0.
127/// dest.len must be >= source.len.130/// dest.len must be >= source.len.
131/// dest.ptr must be <= src.ptr.
128pub fn copy(comptime T: type, dest: []T, source: []const T) void {132pub fn copy(comptime T: type, dest: []T, source: []const T) void {
129 // TODO instead of manually doing this check for the whole array133 // TODO instead of manually doing this check for the whole array
130 // and turning off runtime safety, the compiler should detect loops like134 // and turning off runtime safety, the compiler should detect loops like
...@@ -135,6 +139,23 @@ pub fn copy(comptime T: type, dest: []T, source: []const T) void {...@@ -135,6 +139,23 @@ pub fn copy(comptime T: type, dest: []T, source: []const T) void {
135 dest[i] = s;139 dest[i] = s;
136}140}
137141
142/// Copy all of source into dest at position 0.
143/// dest.len must be >= source.len.
144/// dest.ptr must be >= src.ptr.
145pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
146 // TODO instead of manually doing this check for the whole array
147 // and turning off runtime safety, the compiler should detect loops like
148 // this and automatically omit safety checks for loops
149 @setRuntimeSafety(false);
150 assert(dest.len >= source.len);
151 var i = source.len;
152 while(i > 0){
153 i -= 1;
154 dest[i] = source[i];
155 }
156}
157
158
138pub fn set(comptime T: type, dest: []T, value: T) void {159pub fn set(comptime T: type, dest: []T, value: T) void {
139 for (dest) |*d|160 for (dest) |*d|
140 d.* = value;161 d.* = value;
std/os/windows/index.zig-2
...@@ -59,7 +59,6 @@ pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(...@@ -59,7 +59,6 @@ pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
59 dwFlags: DWORD,59 dwFlags: DWORD,
60) BOOLEAN;60) BOOLEAN;
6161
62
63pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;62pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
6463
65pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;64pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
...@@ -134,7 +133,6 @@ pub extern "kernel32" stdcallcc fn MoveFileExA(...@@ -134,7 +133,6 @@ pub extern "kernel32" stdcallcc fn MoveFileExA(
134 dwFlags: DWORD,133 dwFlags: DWORD,
135) BOOL;134) BOOL;
136135
137
138pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;136pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
139137
140pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;138pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
std/os/windows/util.zig+3-8
...@@ -215,10 +215,7 @@ pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN3...@@ -215,10 +215,7 @@ pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN3
215 return true;215 return true;
216}216}
217217
218218pub const WindowsCreateIoCompletionPortError = error{Unexpected};
219pub const WindowsCreateIoCompletionPortError = error {
220 Unexpected,
221};
222219
223pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_completion_port: ?windows.HANDLE, completion_key: usize, concurrent_thread_count: windows.DWORD) !windows.HANDLE {220pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_completion_port: ?windows.HANDLE, completion_key: usize, concurrent_thread_count: windows.DWORD) !windows.HANDLE {
224 const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {221 const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
...@@ -230,9 +227,7 @@ pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_compl...@@ -230,9 +227,7 @@ pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_compl
230 return handle;227 return handle;
231}228}
232229
233pub const WindowsPostQueuedCompletionStatusError = error {230pub const WindowsPostQueuedCompletionStatusError = error{Unexpected};
234 Unexpected,
235};
236231
237pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void {232pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void {
238 if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {233 if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
...@@ -243,7 +238,7 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_...@@ -243,7 +238,7 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_
243 }238 }
244}239}
245240
246pub const WindowsWaitResult = error {241pub const WindowsWaitResult = error{
247 Normal,242 Normal,
248 Aborted,243 Aborted,
249};244};
std/sort.zig+86-239
...@@ -5,7 +5,7 @@ const math = std.math;...@@ -5,7 +5,7 @@ const math = std.math;
5const builtin = @import("builtin");5const builtin = @import("builtin");
66
7/// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required).7/// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required).
8pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) void {8pub fn insertionSort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) void {
9 {9 {
10 var i: usize = 1;10 var i: usize = 1;
11 while (i < items.len) : (i += 1) {11 while (i < items.len) : (i += 1) {
...@@ -30,7 +30,7 @@ const Range = struct {...@@ -30,7 +30,7 @@ const Range = struct {
30 };30 };
31 }31 }
3232
33 fn length(self: *const Range) usize {33 fn length(self: Range) usize {
34 return self.end - self.start;34 return self.end - self.start;
35 }35 }
36};36};
...@@ -108,7 +108,7 @@ const Pull = struct {...@@ -108,7 +108,7 @@ const Pull = struct {
108108
109/// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required).109/// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required).
110/// Currently implemented as block sort.110/// Currently implemented as block sort.
111pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) void {111pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) void {
112 // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c112 // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c
113 var cache: [512]T = undefined;113 var cache: [512]T = undefined;
114114
...@@ -131,16 +131,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *con...@@ -131,16 +131,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *con
131 // http://pages.ripco.net/~jgamble/nw.html131 // http://pages.ripco.net/~jgamble/nw.html
132 var iterator = Iterator.init(items.len, 4);132 var iterator = Iterator.init(items.len, 4);
133 while (!iterator.finished()) {133 while (!iterator.finished()) {
134 var order = []u8{134 var order = []u8{ 0, 1, 2, 3, 4, 5, 6, 7 };
135 0,
136 1,
137 2,
138 3,
139 4,
140 5,
141 6,
142 7,
143 };
144 const range = iterator.nextRange();135 const range = iterator.nextRange();
145136
146 const sliced_items = items[range.start..];137 const sliced_items = items[range.start..];
...@@ -741,7 +732,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *con...@@ -741,7 +732,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *con
741}732}
742733
743// merge operation without a buffer734// merge operation without a buffer
744fn mergeInPlace(comptime T: type, items: []T, A_arg: *const Range, B_arg: *const Range, lessThan: fn (*const T, *const T) bool) void {735fn mergeInPlace(comptime T: type, items: []T, A_arg: Range, B_arg: Range, lessThan: fn (T, T) bool) void {
745 if (A_arg.length() == 0 or B_arg.length() == 0) return;736 if (A_arg.length() == 0 or B_arg.length() == 0) return;
746737
747 // this just repeatedly binary searches into B and rotates A into position.738 // this just repeatedly binary searches into B and rotates A into position.
...@@ -762,8 +753,8 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: *const Range, B_arg: *const...@@ -762,8 +753,8 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: *const Range, B_arg: *const
762 // again, this is NOT a general-purpose solution – it only works well in this case!753 // again, this is NOT a general-purpose solution – it only works well in this case!
763 // kind of like how the O(n^2) insertion sort is used in some places754 // kind of like how the O(n^2) insertion sort is used in some places
764755
765 var A = A_arg.*;756 var A = A_arg;
766 var B = B_arg.*;757 var B = B_arg;
767758
768 while (true) {759 while (true) {
769 // find the first place in B where the first item in A needs to be inserted760 // find the first place in B where the first item in A needs to be inserted
...@@ -783,7 +774,7 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: *const Range, B_arg: *const...@@ -783,7 +774,7 @@ fn mergeInPlace(comptime T: type, items: []T, A_arg: *const Range, B_arg: *const
783}774}
784775
785// merge operation using an internal buffer776// merge operation using an internal buffer
786fn mergeInternal(comptime T: type, items: []T, A: *const Range, B: *const Range, lessThan: fn (*const T, *const T) bool, buffer: *const Range) void {777fn mergeInternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn (T, T) bool, buffer: Range) void {
787 // whenever we find a value to add to the final array, swap it with the value that's already in that spot778 // whenever we find a value to add to the final array, swap it with the value that's already in that spot
788 // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order779 // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order
789 var A_count: usize = 0;780 var A_count: usize = 0;
...@@ -819,7 +810,7 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s...@@ -819,7 +810,7 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s
819810
820// combine a linear search with a binary search to reduce the number of comparisons in situations811// combine a linear search with a binary search to reduce the number of comparisons in situations
821// where have some idea as to how many unique values there are and where the next value might be812// where have some idea as to how many unique values there are and where the next value might be
822fn findFirstForward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize {813fn findFirstForward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize {
823 if (range.length() == 0) return range.start;814 if (range.length() == 0) return range.start;
824 const skip = math.max(range.length() / unique, usize(1));815 const skip = math.max(range.length() / unique, usize(1));
825816
...@@ -833,7 +824,7 @@ fn findFirstForward(comptime T: type, items: []T, value: *const T, range: *const...@@ -833,7 +824,7 @@ fn findFirstForward(comptime T: type, items: []T, value: *const T, range: *const
833 return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan);824 return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan);
834}825}
835826
836fn findFirstBackward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize {827fn findFirstBackward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize {
837 if (range.length() == 0) return range.start;828 if (range.length() == 0) return range.start;
838 const skip = math.max(range.length() / unique, usize(1));829 const skip = math.max(range.length() / unique, usize(1));
839830
...@@ -847,7 +838,7 @@ fn findFirstBackward(comptime T: type, items: []T, value: *const T, range: *cons...@@ -847,7 +838,7 @@ fn findFirstBackward(comptime T: type, items: []T, value: *const T, range: *cons
847 return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan);838 return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan);
848}839}
849840
850fn findLastForward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize {841fn findLastForward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize {
851 if (range.length() == 0) return range.start;842 if (range.length() == 0) return range.start;
852 const skip = math.max(range.length() / unique, usize(1));843 const skip = math.max(range.length() / unique, usize(1));
853844
...@@ -861,7 +852,7 @@ fn findLastForward(comptime T: type, items: []T, value: *const T, range: *const...@@ -861,7 +852,7 @@ fn findLastForward(comptime T: type, items: []T, value: *const T, range: *const
861 return binaryLast(T, items, value, Range.init(index - skip, index), lessThan);852 return binaryLast(T, items, value, Range.init(index - skip, index), lessThan);
862}853}
863854
864fn findLastBackward(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool, unique: usize) usize {855fn findLastBackward(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool, unique: usize) usize {
865 if (range.length() == 0) return range.start;856 if (range.length() == 0) return range.start;
866 const skip = math.max(range.length() / unique, usize(1));857 const skip = math.max(range.length() / unique, usize(1));
867858
...@@ -875,7 +866,7 @@ fn findLastBackward(comptime T: type, items: []T, value: *const T, range: *const...@@ -875,7 +866,7 @@ fn findLastBackward(comptime T: type, items: []T, value: *const T, range: *const
875 return binaryLast(T, items, value, Range.init(index, index + skip), lessThan);866 return binaryLast(T, items, value, Range.init(index, index + skip), lessThan);
876}867}
877868
878fn binaryFirst(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool) usize {869fn binaryFirst(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool) usize {
879 var start = range.start;870 var start = range.start;
880 var end = range.end - 1;871 var end = range.end - 1;
881 if (range.start >= range.end) return range.end;872 if (range.start >= range.end) return range.end;
...@@ -893,7 +884,7 @@ fn binaryFirst(comptime T: type, items: []T, value: *const T, range: *const Rang...@@ -893,7 +884,7 @@ fn binaryFirst(comptime T: type, items: []T, value: *const T, range: *const Rang
893 return start;884 return start;
894}885}
895886
896fn binaryLast(comptime T: type, items: []T, value: *const T, range: *const Range, lessThan: fn (*const T, *const T) bool) usize {887fn binaryLast(comptime T: type, items: []T, value: T, range: Range, lessThan: fn (T, T) bool) usize {
897 var start = range.start;888 var start = range.start;
898 var end = range.end - 1;889 var end = range.end - 1;
899 if (range.start >= range.end) return range.end;890 if (range.start >= range.end) return range.end;
...@@ -911,7 +902,7 @@ fn binaryLast(comptime T: type, items: []T, value: *const T, range: *const Range...@@ -911,7 +902,7 @@ fn binaryLast(comptime T: type, items: []T, value: *const T, range: *const Range
911 return start;902 return start;
912}903}
913904
914fn mergeInto(comptime T: type, from: []T, A: *const Range, B: *const Range, lessThan: fn (*const T, *const T) bool, into: []T) void {905fn mergeInto(comptime T: type, from: []T, A: Range, B: Range, lessThan: fn (T, T) bool, into: []T) void {
915 var A_index: usize = A.start;906 var A_index: usize = A.start;
916 var B_index: usize = B.start;907 var B_index: usize = B.start;
917 const A_last = A.end;908 const A_last = A.end;
...@@ -941,7 +932,7 @@ fn mergeInto(comptime T: type, from: []T, A: *const Range, B: *const Range, less...@@ -941,7 +932,7 @@ fn mergeInto(comptime T: type, from: []T, A: *const Range, B: *const Range, less
941 }932 }
942}933}
943934
944fn mergeExternal(comptime T: type, items: []T, A: *const Range, B: *const Range, lessThan: fn (*const T, *const T) bool, cache: []T) void {935fn mergeExternal(comptime T: type, items: []T, A: Range, B: Range, lessThan: fn (T, T) bool, cache: []T) void {
945 // A fits into the cache, so use that instead of the internal buffer936 // A fits into the cache, so use that instead of the internal buffer
946 var A_index: usize = 0;937 var A_index: usize = 0;
947 var B_index: usize = B.start;938 var B_index: usize = B.start;
...@@ -969,27 +960,32 @@ fn mergeExternal(comptime T: type, items: []T, A: *const Range, B: *const Range,...@@ -969,27 +960,32 @@ fn mergeExternal(comptime T: type, items: []T, A: *const Range, B: *const Range,
969 mem.copy(T, items[insert_index..], cache[A_index..A_last]);960 mem.copy(T, items[insert_index..], cache[A_index..A_last]);
970}961}
971962
972fn swap(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool, order: *[8]u8, x: usize, y: usize) void {963fn swap(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool, order: *[8]u8, x: usize, y: usize) void {
973 if (lessThan(items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(items[x], items[y]))) {964 if (lessThan(items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(items[x], items[y]))) {
974 mem.swap(T, &items[x], &items[y]);965 mem.swap(T, &items[x], &items[y]);
975 mem.swap(u8, &(order.*)[x], &(order.*)[y]);966 mem.swap(u8, &(order.*)[x], &(order.*)[y]);
976 }967 }
977}968}
978969
979fn i32asc(lhs: *const i32, rhs: *const i32) bool {970// Use these to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`.
980 return lhs.* < rhs.*;971pub fn asc(comptime T: type) fn (T, T) bool {
981}972 const impl = struct {
973 fn inner(a: T, b: T) bool {
974 return a < b;
975 }
976 };
982977
983fn i32desc(lhs: *const i32, rhs: *const i32) bool {978 return impl.inner;
984 return rhs.* < lhs.*;
985}979}
986980
987fn u8asc(lhs: *const u8, rhs: *const u8) bool {981pub fn desc(comptime T: type) fn (T, T) bool {
988 return lhs.* < rhs.*;982 const impl = struct {
989}983 fn inner(a: T, b: T) bool {
984 return a > b;
985 }
986 };
990987
991fn u8desc(lhs: *const u8, rhs: *const u8) bool {988 return impl.inner;
992 return rhs.* < lhs.*;
993}989}
994990
995test "stable sort" {991test "stable sort" {
...@@ -998,119 +994,38 @@ test "stable sort" {...@@ -998,119 +994,38 @@ test "stable sort" {
998}994}
999fn testStableSort() void {995fn testStableSort() void {
1000 var expected = []IdAndValue{996 var expected = []IdAndValue{
1001 IdAndValue{997 IdAndValue{ .id = 0, .value = 0 },
1002 .id = 0,998 IdAndValue{ .id = 1, .value = 0 },
1003 .value = 0,999 IdAndValue{ .id = 2, .value = 0 },
1004 },1000 IdAndValue{ .id = 0, .value = 1 },
1005 IdAndValue{1001 IdAndValue{ .id = 1, .value = 1 },
1006 .id = 1,1002 IdAndValue{ .id = 2, .value = 1 },
1007 .value = 0,1003 IdAndValue{ .id = 0, .value = 2 },
1008 },1004 IdAndValue{ .id = 1, .value = 2 },
1009 IdAndValue{1005 IdAndValue{ .id = 2, .value = 2 },
1010 .id = 2,
1011 .value = 0,
1012 },
1013 IdAndValue{
1014 .id = 0,
1015 .value = 1,
1016 },
1017 IdAndValue{
1018 .id = 1,
1019 .value = 1,
1020 },
1021 IdAndValue{
1022 .id = 2,
1023 .value = 1,
1024 },
1025 IdAndValue{
1026 .id = 0,
1027 .value = 2,
1028 },
1029 IdAndValue{
1030 .id = 1,
1031 .value = 2,
1032 },
1033 IdAndValue{
1034 .id = 2,
1035 .value = 2,
1036 },
1037 };1006 };
1038 var cases = [][9]IdAndValue{1007 var cases = [][9]IdAndValue{
1039 []IdAndValue{1008 []IdAndValue{
1040 IdAndValue{1009 IdAndValue{ .id = 0, .value = 0 },
1041 .id = 0,1010 IdAndValue{ .id = 0, .value = 1 },
1042 .value = 0,1011 IdAndValue{ .id = 0, .value = 2 },
1043 },1012 IdAndValue{ .id = 1, .value = 0 },
1044 IdAndValue{1013 IdAndValue{ .id = 1, .value = 1 },
1045 .id = 0,1014 IdAndValue{ .id = 1, .value = 2 },
1046 .value = 1,1015 IdAndValue{ .id = 2, .value = 0 },
1047 },1016 IdAndValue{ .id = 2, .value = 1 },
1048 IdAndValue{1017 IdAndValue{ .id = 2, .value = 2 },
1049 .id = 0,
1050 .value = 2,
1051 },
1052 IdAndValue{
1053 .id = 1,
1054 .value = 0,
1055 },
1056 IdAndValue{
1057 .id = 1,
1058 .value = 1,
1059 },
1060 IdAndValue{
1061 .id = 1,
1062 .value = 2,
1063 },
1064 IdAndValue{
1065 .id = 2,
1066 .value = 0,
1067 },
1068 IdAndValue{
1069 .id = 2,
1070 .value = 1,
1071 },
1072 IdAndValue{
1073 .id = 2,
1074 .value = 2,
1075 },
1076 },1018 },
1077 []IdAndValue{1019 []IdAndValue{
1078 IdAndValue{1020 IdAndValue{ .id = 0, .value = 2 },
1079 .id = 0,1021 IdAndValue{ .id = 0, .value = 1 },
1080 .value = 2,1022 IdAndValue{ .id = 0, .value = 0 },
1081 },1023 IdAndValue{ .id = 1, .value = 2 },
1082 IdAndValue{1024 IdAndValue{ .id = 1, .value = 1 },
1083 .id = 0,1025 IdAndValue{ .id = 1, .value = 0 },
1084 .value = 1,1026 IdAndValue{ .id = 2, .value = 2 },
1085 },1027 IdAndValue{ .id = 2, .value = 1 },
1086 IdAndValue{1028 IdAndValue{ .id = 2, .value = 0 },
1087 .id = 0,
1088 .value = 0,
1089 },
1090 IdAndValue{
1091 .id = 1,
1092 .value = 2,
1093 },
1094 IdAndValue{
1095 .id = 1,
1096 .value = 1,
1097 },
1098 IdAndValue{
1099 .id = 1,
1100 .value = 0,
1101 },
1102 IdAndValue{
1103 .id = 2,
1104 .value = 2,
1105 },
1106 IdAndValue{
1107 .id = 2,
1108 .value = 1,
1109 },
1110 IdAndValue{
1111 .id = 2,
1112 .value = 0,
1113 },
1114 },1029 },
1115 };1030 };
1116 for (cases) |*case| {1031 for (cases) |*case| {
...@@ -1125,8 +1040,8 @@ const IdAndValue = struct {...@@ -1125,8 +1040,8 @@ const IdAndValue = struct {
1125 id: usize,1040 id: usize,
1126 value: i32,1041 value: i32,
1127};1042};
1128fn cmpByValue(a: *const IdAndValue, b: *const IdAndValue) bool {1043fn cmpByValue(a: IdAndValue, b: IdAndValue) bool {
1129 return i32asc(a.value, b.value);1044 return asc(i32)(a.value, b.value);
1130}1045}
11311046
1132test "std.sort" {1047test "std.sort" {
...@@ -1161,7 +1076,7 @@ test "std.sort" {...@@ -1161,7 +1076,7 @@ test "std.sort" {
1161 var buf: [8]u8 = undefined;1076 var buf: [8]u8 = undefined;
1162 const slice = buf[0..case[0].len];1077 const slice = buf[0..case[0].len];
1163 mem.copy(u8, slice, case[0]);1078 mem.copy(u8, slice, case[0]);
1164 sort(u8, slice, u8asc);1079 sort(u8, slice, asc(u8));
1165 assert(mem.eql(u8, slice, case[1]));1080 assert(mem.eql(u8, slice, case[1]));
1166 }1081 }
11671082
...@@ -1175,48 +1090,20 @@ test "std.sort" {...@@ -1175,48 +1090,20 @@ test "std.sort" {
1175 []i32{1},1090 []i32{1},
1176 },1091 },
1177 [][]const i32{1092 [][]const i32{
1178 []i32{1093 []i32{ 0, 1 },
1179 0,1094 []i32{ 0, 1 },
1180 1,
1181 },
1182 []i32{
1183 0,
1184 1,
1185 },
1186 },1095 },
1187 [][]const i32{1096 [][]const i32{
1188 []i32{1097 []i32{ 1, 0 },
1189 1,1098 []i32{ 0, 1 },
1190 0,
1191 },
1192 []i32{
1193 0,
1194 1,
1195 },
1196 },1099 },
1197 [][]const i32{1100 [][]const i32{
1198 []i32{1101 []i32{ 1, -1, 0 },
1199 1,1102 []i32{ -1, 0, 1 },
1200 -1,
1201 0,
1202 },
1203 []i32{
1204 -1,
1205 0,
1206 1,
1207 },
1208 },1103 },
1209 [][]const i32{1104 [][]const i32{
1210 []i32{1105 []i32{ 2, 1, 3 },
1211 2,1106 []i32{ 1, 2, 3 },
1212 1,
1213 3,
1214 },
1215 []i32{
1216 1,
1217 2,
1218 3,
1219 },
1220 },1107 },
1221 };1108 };
12221109
...@@ -1224,7 +1111,7 @@ test "std.sort" {...@@ -1224,7 +1111,7 @@ test "std.sort" {
1224 var buf: [8]i32 = undefined;1111 var buf: [8]i32 = undefined;
1225 const slice = buf[0..case[0].len];1112 const slice = buf[0..case[0].len];
1226 mem.copy(i32, slice, case[0]);1113 mem.copy(i32, slice, case[0]);
1227 sort(i32, slice, i32asc);1114 sort(i32, slice, asc(i32));
1228 assert(mem.eql(i32, slice, case[1]));1115 assert(mem.eql(i32, slice, case[1]));
1229 }1116 }
1230}1117}
...@@ -1240,48 +1127,20 @@ test "std.sort descending" {...@@ -1240,48 +1127,20 @@ test "std.sort descending" {
1240 []i32{1},1127 []i32{1},
1241 },1128 },
1242 [][]const i32{1129 [][]const i32{
1243 []i32{1130 []i32{ 0, 1 },
1244 0,1131 []i32{ 1, 0 },
1245 1,
1246 },
1247 []i32{
1248 1,
1249 0,
1250 },
1251 },1132 },
1252 [][]const i32{1133 [][]const i32{
1253 []i32{1134 []i32{ 1, 0 },
1254 1,1135 []i32{ 1, 0 },
1255 0,
1256 },
1257 []i32{
1258 1,
1259 0,
1260 },
1261 },1136 },
1262 [][]const i32{1137 [][]const i32{
1263 []i32{1138 []i32{ 1, -1, 0 },
1264 1,1139 []i32{ 1, 0, -1 },
1265 -1,
1266 0,
1267 },
1268 []i32{
1269 1,
1270 0,
1271 -1,
1272 },
1273 },1140 },
1274 [][]const i32{1141 [][]const i32{
1275 []i32{1142 []i32{ 2, 1, 3 },
1276 2,1143 []i32{ 3, 2, 1 },
1277 1,
1278 3,
1279 },
1280 []i32{
1281 3,
1282 2,
1283 1,
1284 },
1285 },1144 },
1286 };1145 };
12871146
...@@ -1289,28 +1148,16 @@ test "std.sort descending" {...@@ -1289,28 +1148,16 @@ test "std.sort descending" {
1289 var buf: [8]i32 = undefined;1148 var buf: [8]i32 = undefined;
1290 const slice = buf[0..case[0].len];1149 const slice = buf[0..case[0].len];
1291 mem.copy(i32, slice, case[0]);1150 mem.copy(i32, slice, case[0]);
1292 sort(i32, slice, i32desc);1151 sort(i32, slice, desc(i32));
1293 assert(mem.eql(i32, slice, case[1]));1152 assert(mem.eql(i32, slice, case[1]));
1294 }1153 }
1295}1154}
12961155
1297test "another sort case" {1156test "another sort case" {
1298 var arr = []i32{1157 var arr = []i32{ 5, 3, 1, 2, 4 };
1299 5,1158 sort(i32, arr[0..], asc(i32));
1300 3,1159
1301 1,1160 assert(mem.eql(i32, arr, []i32{ 1, 2, 3, 4, 5 }));
1302 2,
1303 4,
1304 };
1305 sort(i32, arr[0..], i32asc);
1306
1307 assert(mem.eql(i32, arr, []i32{
1308 1,
1309 2,
1310 3,
1311 4,
1312 5,
1313 }));
1314}1161}
13151162
1316test "sort fuzz testing" {1163test "sort fuzz testing" {
...@@ -1345,7 +1192,7 @@ fn fuzzTest(rng: *std.rand.Random) void {...@@ -1345,7 +1192,7 @@ fn fuzzTest(rng: *std.rand.Random) void {
1345 }1192 }
1346}1193}
13471194
1348pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) T {1195pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) T {
1349 var i: usize = 0;1196 var i: usize = 0;
1350 var smallest = items[0];1197 var smallest = items[0];
1351 for (items[1..]) |item| {1198 for (items[1..]) |item| {
...@@ -1356,7 +1203,7 @@ pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *cons...@@ -1356,7 +1203,7 @@ pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *cons
1356 return smallest;1203 return smallest;
1357}1204}
13581205
1359pub fn max(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *const T) bool) T {1206pub fn max(comptime T: type, items: []T, lessThan: fn (lhs: T, rhs: T) bool) T {
1360 var i: usize = 0;1207 var i: usize = 0;
1361 var biggest = items[0];1208 var biggest = items[0];
1362 for (items[1..]) |item| {1209 for (items[1..]) |item| {
std/zig/ast.zig-6
...@@ -970,14 +970,8 @@ pub const Node = struct {...@@ -970,14 +970,8 @@ pub const Node = struct {
970 pub const Defer = struct {970 pub const Defer = struct {
971 base: Node,971 base: Node,
972 defer_token: TokenIndex,972 defer_token: TokenIndex,
973 kind: Kind,
974 expr: *Node,973 expr: *Node,
975974
976 const Kind = enum {
977 Error,
978 Unconditional,
979 };
980
981 pub fn iterate(self: *Defer, index: usize) ?*Node {975 pub fn iterate(self: *Defer, index: usize) ?*Node {
982 var i = index;976 var i = index;
983977
std/zig/parse.zig-5
...@@ -1041,11 +1041,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -1041,11 +1041,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
1041 const node = try arena.create(ast.Node.Defer{1041 const node = try arena.create(ast.Node.Defer{
1042 .base = ast.Node{ .id = ast.Node.Id.Defer },1042 .base = ast.Node{ .id = ast.Node.Id.Defer },
1043 .defer_token = token_index,1043 .defer_token = token_index,
1044 .kind = switch (token_ptr.id) {
1045 Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
1046 Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
1047 else => unreachable,
1048 },
1049 .expr = undefined,1044 .expr = undefined,
1050 });1045 });
1051 const node_ptr = try block.statements.addOne();1046 const node_ptr = try block.statements.addOne();
test/behavior.zig+1
...@@ -9,6 +9,7 @@ comptime {...@@ -9,6 +9,7 @@ comptime {
9 _ = @import("cases/bitcast.zig");9 _ = @import("cases/bitcast.zig");
10 _ = @import("cases/bool.zig");10 _ = @import("cases/bool.zig");
11 _ = @import("cases/bugs/1111.zig");11 _ = @import("cases/bugs/1111.zig");
12 _ = @import("cases/bugs/1230.zig");
12 _ = @import("cases/bugs/394.zig");13 _ = @import("cases/bugs/394.zig");
13 _ = @import("cases/bugs/655.zig");14 _ = @import("cases/bugs/655.zig");
14 _ = @import("cases/bugs/656.zig");15 _ = @import("cases/bugs/656.zig");
test/cases/bugs/1230.zig created+14
...@@ -0,0 +1,14 @@
1const assert = @import("std").debug.assert;
2
3const S = extern struct {
4 x: i32,
5};
6
7extern fn ret_struct() S {
8 return S{ .x = 42 };
9}
10
11test "extern return small struct (bug 1230)" {
12 const s = ret_struct();
13 assert(s.x == 42);
14}
test/cases/optional.zig+21
...@@ -7,3 +7,24 @@ test "optional pointer to size zero struct" {...@@ -7,3 +7,24 @@ test "optional pointer to size zero struct" {
7 var o: ?*EmptyStruct = &e;7 var o: ?*EmptyStruct = &e;
8 assert(o != null);8 assert(o != null);
9}9}
10
11test "equality compare nullable pointers" {
12 testNullPtrsEql();
13 comptime testNullPtrsEql();
14}
15
16fn testNullPtrsEql() void {
17 var number: i32 = 1234;
18
19 var x: ?*i32 = null;
20 var y: ?*i32 = null;
21 assert(x == y);
22 y = &number;
23 assert(x != y);
24 assert(x != &number);
25 assert(&number != x);
26 x = &number;
27 assert(x == y);
28 assert(x == &number);
29 assert(&number == x);
30}
test/compile_errors.zig+27
...@@ -1,6 +1,33 @@...@@ -1,6 +1,33 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompileErrorContext) void {3pub fn addCases(cases: *tests.CompileErrorContext) void {
4 cases.add(
5 "optional pointer to void in extern struct",
6 \\const Foo = extern struct {
7 \\ x: ?*const void,
8 \\};
9 \\const Bar = extern struct {
10 \\ foo: Foo,
11 \\ y: i32,
12 \\};
13 \\export fn entry(bar: *Bar) void {}
14 ,
15 ".tmp_source.zig:2:5: error: extern structs cannot contain fields of type '?*const void'",
16 );
17
18 cases.add(
19 "use of comptime-known undefined function value",
20 \\const Cmd = struct {
21 \\ exec: fn () void,
22 \\};
23 \\export fn entry() void {
24 \\ const command = Cmd{ .exec = undefined };
25 \\ command.exec();
26 \\}
27 ,
28 ".tmp_source.zig:6:12: error: use of undefined value",
29 );
30
4 cases.add(31 cases.add(
5 "use of comptime-known undefined function value",32 "use of comptime-known undefined function value",
6 \\const Cmd = struct {33 \\const Cmd = struct {
test/stage2/compile_errors.zig created+12
...@@ -0,0 +1,12 @@
1const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
2
3pub 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}
test/tests.zig+14-33
...@@ -47,12 +47,13 @@ const test_targets = []TestTarget{...@@ -47,12 +47,13 @@ const test_targets = []TestTarget{
4747
48const max_stdout_size = 1 * 1024 * 1024; // 1 MB48const max_stdout_size = 1 * 1024 * 1024; // 1 MB
4949
50pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {50pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
51 const cases = b.allocator.create(CompareOutputContext{51 const cases = b.allocator.create(CompareOutputContext{
52 .b = b,52 .b = b,
53 .step = b.step("test-compare-output", "Run the compare output tests"),53 .step = b.step("test-compare-output", "Run the compare output tests"),
54 .test_index = 0,54 .test_index = 0,
55 .test_filter = test_filter,55 .test_filter = test_filter,
56 .modes = modes,
56 }) catch unreachable;57 }) catch unreachable;
5758
58 compare_output.addCases(cases);59 compare_output.addCases(cases);
...@@ -60,12 +61,13 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8) *build...@@ -60,12 +61,13 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8) *build
60 return cases.step;61 return cases.step;
61}62}
6263
63pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {64pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
64 const cases = b.allocator.create(CompareOutputContext{65 const cases = b.allocator.create(CompareOutputContext{
65 .b = b,66 .b = b,
66 .step = b.step("test-runtime-safety", "Run the runtime safety tests"),67 .step = b.step("test-runtime-safety", "Run the runtime safety tests"),
67 .test_index = 0,68 .test_index = 0,
68 .test_filter = test_filter,69 .test_filter = test_filter,
70 .modes = modes,
69 }) catch unreachable;71 }) catch unreachable;
7072
71 runtime_safety.addCases(cases);73 runtime_safety.addCases(cases);
...@@ -73,12 +75,13 @@ pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8) *build...@@ -73,12 +75,13 @@ pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8) *build
73 return cases.step;75 return cases.step;
74}76}
7577
76pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {78pub fn addCompileErrorTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
77 const cases = b.allocator.create(CompileErrorContext{79 const cases = b.allocator.create(CompileErrorContext{
78 .b = b,80 .b = b,
79 .step = b.step("test-compile-errors", "Run the compile error tests"),81 .step = b.step("test-compile-errors", "Run the compile error tests"),
80 .test_index = 0,82 .test_index = 0,
81 .test_filter = test_filter,83 .test_filter = test_filter,
84 .modes = modes,
82 }) catch unreachable;85 }) catch unreachable;
8386
84 compile_errors.addCases(cases);87 compile_errors.addCases(cases);
...@@ -99,12 +102,13 @@ pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build....@@ -99,12 +102,13 @@ pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build.
99 return cases.step;102 return cases.step;
100}103}
101104
102pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {105pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
103 const cases = b.allocator.create(CompareOutputContext{106 const cases = b.allocator.create(CompareOutputContext{
104 .b = b,107 .b = b,
105 .step = b.step("test-asm-link", "Run the assemble and link tests"),108 .step = b.step("test-asm-link", "Run the assemble and link tests"),
106 .test_index = 0,109 .test_index = 0,
107 .test_filter = test_filter,110 .test_filter = test_filter,
111 .modes = modes,
108 }) catch unreachable;112 }) catch unreachable;
109113
110 assemble_and_link.addCases(cases);114 assemble_and_link.addCases(cases);
...@@ -138,16 +142,11 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {...@@ -138,16 +142,11 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
138 return cases.step;142 return cases.step;
139}143}
140144
141pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []const u8, name: []const u8, desc: []const u8, with_lldb: bool) *build.Step {145pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []const u8, name: []const u8, desc: []const u8, modes: []const Mode) *build.Step {
142 const step = b.step(b.fmt("test-{}", name), desc);146 const step = b.step(b.fmt("test-{}", name), desc);
143 for (test_targets) |test_target| {147 for (test_targets) |test_target| {
144 const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch);148 const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch);
145 for ([]Mode{149 for (modes) |mode| {
146 Mode.Debug,
147 Mode.ReleaseSafe,
148 Mode.ReleaseFast,
149 Mode.ReleaseSmall,
150 }) |mode| {
151 for ([]bool{150 for ([]bool{
152 false,151 false,
153 true,152 true,
...@@ -166,18 +165,6 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons...@@ -166,18 +165,6 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons
166 if (link_libc) {165 if (link_libc) {
167 these_tests.linkSystemLibrary("c");166 these_tests.linkSystemLibrary("c");
168 }167 }
169 if (with_lldb) {
170 these_tests.setExecCmd([]?[]const u8{
171 "lldb",
172 null,
173 "-o",
174 "run",
175 "-o",
176 "bt",
177 "-o",
178 "exit",
179 });
180 }
181 step.dependOn(&these_tests.step);168 step.dependOn(&these_tests.step);
182 }169 }
183 }170 }
...@@ -190,6 +177,7 @@ pub const CompareOutputContext = struct {...@@ -190,6 +177,7 @@ pub const CompareOutputContext = struct {
190 step: *build.Step,177 step: *build.Step,
191 test_index: usize,178 test_index: usize,
192 test_filter: ?[]const u8,179 test_filter: ?[]const u8,
180 modes: []const Mode,
193181
194 const Special = enum {182 const Special = enum {
195 None,183 None,
...@@ -440,12 +428,7 @@ pub const CompareOutputContext = struct {...@@ -440,12 +428,7 @@ pub const CompareOutputContext = struct {
440 self.step.dependOn(&run_and_cmp_output.step);428 self.step.dependOn(&run_and_cmp_output.step);
441 },429 },
442 Special.None => {430 Special.None => {
443 for ([]Mode{431 for (self.modes) |mode| {
444 Mode.Debug,
445 Mode.ReleaseSafe,
446 Mode.ReleaseFast,
447 Mode.ReleaseSmall,
448 }) |mode| {
449 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", "compare-output", case.name, @tagName(mode)) catch unreachable;432 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", "compare-output", case.name, @tagName(mode)) catch unreachable;
450 if (self.test_filter) |filter| {433 if (self.test_filter) |filter| {
451 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;434 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
...@@ -500,6 +483,7 @@ pub const CompileErrorContext = struct {...@@ -500,6 +483,7 @@ pub const CompileErrorContext = struct {
500 step: *build.Step,483 step: *build.Step,
501 test_index: usize,484 test_index: usize,
502 test_filter: ?[]const u8,485 test_filter: ?[]const u8,
486 modes: []const Mode,
503487
504 const TestCase = struct {488 const TestCase = struct {
505 name: []const u8,489 name: []const u8,
...@@ -690,10 +674,7 @@ pub const CompileErrorContext = struct {...@@ -690,10 +674,7 @@ pub const CompileErrorContext = struct {
690 pub fn addCase(self: *CompileErrorContext, case: *const TestCase) void {674 pub fn addCase(self: *CompileErrorContext, case: *const TestCase) void {
691 const b = self.b;675 const b = self.b;
692676
693 for ([]Mode{677 for (self.modes) |mode| {
694 Mode.Debug,
695 Mode.ReleaseFast,
696 }) |mode| {
697 const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {} ({})", case.name, @tagName(mode)) catch unreachable;678 const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {} ({})", case.name, @tagName(mode)) catch unreachable;
698 if (self.test_filter) |filter| {679 if (self.test_filter) |filter| {
699 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;680 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;