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
431431set(ZIG_STD_FILES
432432 "array_list.zig"
433433 "atomic/index.zig"
434 "atomic/queue_mpmc.zig"
435 "atomic/queue_mpsc.zig"
434 "atomic/int.zig"
435 "atomic/queue.zig"
436436 "atomic/stack.zig"
437437 "base64.zig"
438438 "buf_map.zig"
......@@ -459,6 +459,8 @@ set(ZIG_STD_FILES
459459 "empty.zig"
460460 "event.zig"
461461 "event/channel.zig"
462 "event/future.zig"
463 "event/group.zig"
462464 "event/lock.zig"
463465 "event/locked.zig"
464466 "event/loop.zig"
build.zig+105-72
......@@ -35,73 +35,31 @@ pub fn build(b: *Builder) !void {
3535 "BUILD_INFO",
3636 });
3737 var index: usize = 0;
38 const cmake_binary_dir = nextValue(&index, build_info);
39 const cxx_compiler = nextValue(&index, build_info);
40 const llvm_config_exe = nextValue(&index, build_info);
41 const lld_include_dir = nextValue(&index, build_info);
42 const lld_libraries = nextValue(&index, build_info);
43 const std_files = nextValue(&index, build_info);
44 const c_header_files = nextValue(&index, build_info);
45 const dia_guids_lib = nextValue(&index, build_info);
38 var ctx = Context{
39 .cmake_binary_dir = nextValue(&index, build_info),
40 .cxx_compiler = nextValue(&index, build_info),
41 .llvm_config_exe = nextValue(&index, build_info),
42 .lld_include_dir = nextValue(&index, build_info),
43 .lld_libraries = nextValue(&index, build_info),
44 .std_files = 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
4954 var exe = b.addExecutable("zig", "src-self-hosted/main.zig");
5055 exe.setBuildMode(mode);
5156
52 // This is for finding /lib/libz.a on alpine linux.
53 // TODO turn this into -Dextra-lib-path=/lib option
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");
57 try configureStage2(b, test_stage2, ctx);
58 try configureStage2(b, exe, ctx);
10259
10360 b.default_step.dependOn(&exe.step);
10461
62 const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false;
10563 const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false;
10664 if (!skip_self_hosted) {
10765 test_step.dependOn(&exe.step);
......@@ -110,30 +68,40 @@ pub fn build(b: *Builder) !void {
11068 exe.setVerboseLink(verbose_link_exe);
11169
11270 b.installArtifact(exe);
113 installStdLib(b, std_files);
114 installCHeaders(b, c_header_files);
71 installStdLib(b, ctx.std_files);
72 installCHeaders(b, ctx.c_header_files);
11573
11674 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));
12895 test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
129 test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
130 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
131 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter));
96 test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
97 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
98 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
13299 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
133100 test_step.dependOn(tests.addGenHTests(b, test_filter));
101 test_step.dependOn(docs_step);
134102}
135103
136fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) void {
104fn dependOnLib(lib_exe_obj: var, dep: *const LibraryDep) void {
137105 for (dep.libdirs.toSliceConst()) |lib_dir| {
138106 lib_exe_obj.addLibPath(lib_dir);
139107 }
......@@ -148,7 +116,7 @@ fn dependOnLib(lib_exe_obj: *std.build.LibExeObjStep, dep: *const LibraryDep) vo
148116 }
149117}
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 {
152120 const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib";
153121 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);
154122}
......@@ -254,3 +222,68 @@ fn nextValue(index: *usize, build_info: []const u8) []const u8 {
254222 }
255223 }
256224}
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" {
22392239 // On an OS other than fuchsia, block is not even analyzed,
22402240 // so this compile error is not triggered.
22412241 // On fuchsia this compile error would be triggered.
2242 @compileError("windows not supported");
2242 @compileError("fuchsia not supported");
22432243 },
22442244 else => {},
22452245 }
......@@ -2303,13 +2303,13 @@ test "while continue" {
23032303 {#code_begin|test|while#}
23042304const assert = @import("std").debug.assert;
23052305
2306test "while loop continuation expression" {
2306test "while loop continue expression" {
23072307 var i: usize = 0;
23082308 while (i < 10) : (i += 1) {}
23092309 assert(i == 10);
23102310}
23112311
2312test "while loop continuation expression, more complicated" {
2312test "while loop continue expression, more complicated" {
23132313 var i1: usize = 1;
23142314 var j1: usize = 1;
23152315 while (i1 * j1 < 2000) : ({ i1 *= 2; j1 *= 3; }) {
......@@ -7118,10 +7118,16 @@ Environments:
71187118 opencl</code></pre>
71197119 <p>
71207120 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 supports
7122 Linux x86_64. Not all standard library code requires operating system abstractions, however,
7121 abstractions, and thus takes additional work to support more platforms.
7122 Not all standard library code requires operating system abstractions, however,
71237123 so things such as generic data structures work an all above platforms.
71247124 </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>
71257131 {#header_close#}
71267132 {#header_open|Style Guide#}
71277133 <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 {
1111 On,
1212};
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
1426pub const Msg = struct {
1527 path: []const u8,
1628 text: []u8,
17 first_token: TokenIndex,
18 last_token: TokenIndex,
29 span: Span,
1930 tree: *ast.Tree,
2031};
2132
......@@ -39,8 +50,10 @@ pub fn createFromParseError(
3950 .tree = tree,
4051 .path = path,
4152 .text = text_buf.toOwnedSlice(),
42 .first_token = loc_token,
43 .last_token = loc_token,
53 .span = Span{
54 .first = loc_token,
55 .last = loc_token,
56 },
4457 });
4558 errdefer allocator.destroy(msg);
4659
......@@ -48,8 +61,8 @@ pub fn createFromParseError(
4861}
4962
5063pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void {
51 const first_token = msg.tree.tokens.at(msg.first_token);
52 const last_token = msg.tree.tokens.at(msg.last_token);
64 const first_token = msg.tree.tokens.at(msg.span.first);
65 const last_token = msg.tree.tokens.at(msg.span.last);
5366 const start_loc = msg.tree.tokenLocationPtr(0, first_token);
5467 const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token);
5568 if (!color_on) {
src-self-hosted/introspect.zig+5
......@@ -53,3 +53,8 @@ pub fn resolveZigLibDir(allocator: *mem.Allocator) ![]u8 {
5353 return error.ZigLibDirNotFound;
5454 };
5555}
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 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Compilation = @import("compilation.zig").Compilation;
14const 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
347pub const Instruction = struct {
448 id: Id,
549 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
7155 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,
26156 Return,
27 Cast,
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,
157 Const,
54158 Ref,
55 MinValue,
56 MaxValue,
57 CompileErr,
58 CompileLog,
59 ErrName,
60 EmbedFile,
61 Cmpxchg,
62 Fence,
63 Truncate,
64 IntType,
65 BoolNot,
66 Memset,
67 Memcpy,
68 Slice,
69 MemberCount,
70 MemberType,
71 MemberName,
72 Breakpoint,
73 ReturnAddress,
74 FrameAddress,
75 AlignOf,
76 OverflowOp,
77 TestErr,
78 UnwrapErrCode,
79 UnwrapErrPayload,
80 ErrWrapCode,
81 ErrWrapPayload,
82 FnProto,
83 TestComptime,
84 PtrCast,
85 BitCast,
86 WidenOrShorten,
87 IntToPtr,
88 PtrToInt,
89 IntToEnum,
90 IntToErr,
91 ErrToInt,
92 CheckSwitchProngs,
93 CheckStatementIsVoid,
94 TypeName,
95 CanImplicitCast,
96 DeclRef,
97 Panic,
98 TagName,
99 TagType,
100 FieldParentPtr,
101 OffsetOf,
102 TypeId,
103 SetEvalBranchQuota,
104 PtrTypeOf,
105 AlignCast,
106 OpaqueType,
107 SetAlignStack,
108 ArgType,
109 Export,
159 DeclVar,
160 CheckVoidStmt,
161 Phi,
162 Br,
163 AddImplicitReturnType,
164 };
165
166 pub const Const = struct {
167 base: Instruction,
168 params: Params,
169
170 const Params = struct {};
171
172 // Use Builder.buildConst* methods, or, after building a Const instruction,
173 // manually set the ir_val field.
174 const ir_val_init = IrVal.Init.Unknown;
175
176 pub fn dump(self: *const Const) void {
177 self.base.val.KnownValue.dump();
178 }
179
180 pub fn hasSideEffects(self: *const Const) bool {
181 return false;
182 }
183
184 pub fn analyze(self: *const Const, ira: *Analyze) !*Instruction {
185 const new_inst = try ira.irb.build(Const, self.base.scope, self.base.span, Params{});
186 new_inst.val = IrVal{ .KnownValue = self.base.val.KnownValue.getRef() };
187 return new_inst;
188 }
189 };
190
191 pub const Return = struct {
192 base: Instruction,
193 params: Params,
194
195 const Params = struct {
196 return_value: *Instruction,
197 };
198
199 const ir_val_init = IrVal.Init.NoReturn;
200
201 pub fn dump(self: *const Return) void {
202 std.debug.warn("#{}", self.params.return_value.debug_id);
203 }
204
205 pub fn hasSideEffects(self: *const Return) bool {
206 return true;
207 }
208
209 pub fn analyze(self: *const Return, ira: *Analyze) !*Instruction {
210 const value = try self.params.return_value.getAsParam();
211 const casted_value = try ira.implicitCast(value, ira.explicit_return_type);
212
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 }
110356 };
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,
111386};
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");
22const c = @import("c.zig");
33const assert = @import("std").debug.assert;
44
5pub const ValueRef = removeNullability(c.LLVMValueRef);
6pub const ModuleRef = removeNullability(c.LLVMModuleRef);
7pub const ContextRef = removeNullability(c.LLVMContextRef);
85pub 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
1027fn removeNullability(comptime T: type) type {
1128 comptime assert(@typeId(T) == builtin.TypeId.Optional);
src-self-hosted/main.zig+114-74
......@@ -14,7 +14,8 @@ const c = @import("c.zig");
1414const introspect = @import("introspect.zig");
1515const Args = arg.Args;
1616const Flag = arg.Flag;
17const Module = @import("module.zig").Module;
17const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
18const Compilation = @import("compilation.zig").Compilation;
1819const Target = @import("target.zig").Target;
1920const errmsg = @import("errmsg.zig");
2021
......@@ -257,7 +258,7 @@ const args_build_generic = []Flag{
257258 Flag.Arg1("--ver-patch"),
258259};
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 {
261262 var flags = try Args.parse(allocator, args_build_generic, args);
262263 defer flags.deinit();
263264
......@@ -299,14 +300,14 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
299300 const emit_type = blk: {
300301 if (flags.single("emit")) |emit_flag| {
301302 if (mem.eql(u8, emit_flag, "asm")) {
302 break :blk Module.Emit.Assembly;
303 break :blk Compilation.Emit.Assembly;
303304 } else if (mem.eql(u8, emit_flag, "bin")) {
304 break :blk Module.Emit.Binary;
305 break :blk Compilation.Emit.Binary;
305306 } else if (mem.eql(u8, emit_flag, "llvm-ir")) {
306 break :blk Module.Emit.LlvmIr;
307 break :blk Compilation.Emit.LlvmIr;
307308 } else unreachable;
308309 } else {
309 break :blk Module.Emit.Binary;
310 break :blk Compilation.Emit.Binary;
310311 }
311312 };
312313
......@@ -369,7 +370,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
369370 os.exit(1);
370371 }
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) {
373374 try stderr.write("When building an object file, --object arguments are invalid\n");
374375 os.exit(1);
375376 }
......@@ -386,9 +387,13 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
386387
387388 var loop: event.Loop = undefined;
388389 try loop.initMultiThreaded(allocator);
390 defer loop.deinit();
389391
390 var module = try Module.create(
391 &loop,
392 var event_loop_local = EventLoopLocal.init(&loop);
393 defer event_loop_local.deinit();
394
395 var comp = try Compilation.create(
396 &event_loop_local,
392397 root_name,
393398 root_source_file,
394399 Target.Native,
......@@ -397,16 +402,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
397402 zig_lib_dir,
398403 full_cache_dir,
399404 );
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);
403 module.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);
407 comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
408 comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") 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");
409 module.each_lib_rpath = flags.present("each-lib-rpath");
413 comp.linker_script = flags.single("linker-script");
414 comp.each_lib_rpath = flags.present("each-lib-rpath");
410415
411416 var clang_argv_buf = ArrayList([]const u8).init(allocator);
412417 defer clang_argv_buf.deinit();
......@@ -417,51 +422,51 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
417422 try clang_argv_buf.append(mllvm);
418423 }
419424
420 module.llvm_argv = mllvm_flags;
421 module.clang_argv = clang_argv_buf.toSliceConst();
425 comp.llvm_argv = mllvm_flags;
426 comp.clang_argv = clang_argv_buf.toSliceConst();
422427
423 module.strip = flags.present("strip");
424 module.is_static = flags.present("static");
428 comp.strip = flags.present("strip");
429 comp.is_static = flags.present("static");
425430
426431 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;
428433 }
429434 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;
431436 }
432437 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;
434439 }
435440 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;
437442 }
438443 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;
440445 }
441446 if (flags.single("dynamic-linker")) |dynamic_linker| {
442 module.dynamic_linker = dynamic_linker;
447 comp.dynamic_linker = dynamic_linker;
443448 }
444449
445 module.verbose_tokenize = flags.present("verbose-tokenize");
446 module.verbose_ast_tree = flags.present("verbose-ast-tree");
447 module.verbose_ast_fmt = flags.present("verbose-ast-fmt");
448 module.verbose_link = flags.present("verbose-link");
449 module.verbose_ir = flags.present("verbose-ir");
450 module.verbose_llvm_ir = flags.present("verbose-llvm-ir");
451 module.verbose_cimport = flags.present("verbose-cimport");
450 comp.verbose_tokenize = flags.present("verbose-tokenize");
451 comp.verbose_ast_tree = flags.present("verbose-ast-tree");
452 comp.verbose_ast_fmt = flags.present("verbose-ast-fmt");
453 comp.verbose_link = flags.present("verbose-link");
454 comp.verbose_ir = flags.present("verbose-ir");
455 comp.verbose_llvm_ir = flags.present("verbose-llvm-ir");
456 comp.verbose_cimport = flags.present("verbose-cimport");
452457
453 module.err_color = color;
454 module.lib_dirs = flags.many("library-path");
455 module.darwin_frameworks = flags.many("framework");
456 module.rpath_list = flags.many("rpath");
458 comp.err_color = color;
459 comp.lib_dirs = flags.many("library-path");
460 comp.darwin_frameworks = flags.many("framework");
461 comp.rpath_list = flags.many("rpath");
457462
458463 if (flags.single("output-h")) |output_h| {
459 module.out_h_path = output_h;
464 comp.out_h_path = output_h;
460465 }
461466
462 module.windows_subsystem_windows = flags.present("mwindows");
463 module.windows_subsystem_console = flags.present("mconsole");
464 module.linker_rdynamic = flags.present("rdynamic");
467 comp.windows_subsystem_windows = flags.present("mwindows");
468 comp.windows_subsystem_console = flags.present("mconsole");
469 comp.linker_rdynamic = flags.present("rdynamic");
465470
466471 if (flags.single("mmacosx-version-min") != null and flags.single("mios-version-min") != null) {
467472 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
469474 }
470475
471476 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 };
473478 }
474479 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 };
476481 }
477482
478 module.emit_file_type = emit_type;
479 module.link_objects = link_objects;
480 module.assembly_files = assembly_files;
481 module.link_out_file = flags.single("out-file");
483 comp.emit_file_type = emit_type;
484 comp.link_objects = link_objects;
485 comp.assembly_files = assembly_files;
486 comp.link_out_file = flags.single("out-file");
482487
483 try module.build();
484 const process_build_events_handle = try async<loop.allocator> processBuildEvents(module, true);
488 try comp.build();
489 const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);
485490 defer cancel process_build_events_handle;
486491 loop.run();
487492}
488493
489async fn processBuildEvents(module: *Module, watch: bool) void {
490 while (watch) {
491 // TODO directly awaiting async should guarantee memory allocation elision
492 const build_event = await (async module.events.get() catch unreachable);
494async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
495 // TODO directly awaiting async should guarantee memory allocation elision
496 const build_event = await (async comp.events.get() catch unreachable);
493497
494 switch (build_event) {
495 Module.Event.Ok => {
496 std.debug.warn("Build succeeded\n");
497 return;
498 },
499 Module.Event.Error => |err| {
500 std.debug.warn("build failed: {}\n", @errorName(err));
501 @panic("TODO error return trace");
502 },
503 Module.Event.Fail => |errs| {
504 @panic("TODO print compile error messages");
505 },
506 }
498 switch (build_event) {
499 Compilation.Event.Ok => {
500 std.debug.warn("Build succeeded\n");
501 return;
502 },
503 Compilation.Event.Error => |err| {
504 std.debug.warn("build failed: {}\n", @errorName(err));
505 os.exit(1);
506 },
507 Compilation.Event.Fail => |msgs| {
508 for (msgs) |msg| {
509 errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1);
510 }
511 },
507512 }
508513}
509514
510515fn cmdBuildExe(allocator: *Allocator, args: []const []const u8) !void {
511 return buildOutputType(allocator, args, Module.Kind.Exe);
516 return buildOutputType(allocator, args, Compilation.Kind.Exe);
512517}
513518
514519fn cmdBuildLib(allocator: *Allocator, args: []const []const u8) !void {
515 return buildOutputType(allocator, args, Module.Kind.Lib);
520 return buildOutputType(allocator, args, Compilation.Kind.Lib);
516521}
517522
518523fn cmdBuildObj(allocator: *Allocator, args: []const []const u8) !void {
519 return buildOutputType(allocator, args, Module.Kind.Obj);
524 return buildOutputType(allocator, args, Compilation.Kind.Obj);
520525}
521526
522527const usage_fmt =
......@@ -527,6 +532,7 @@ const usage_fmt =
527532 \\Options:
528533 \\ --help Print this help and exit
529534 \\ --color [auto|off|on] Enable or disable colored error messages
535 \\ --stdin Format code from stdin
530536 \\
531537 \\
532538;
......@@ -538,6 +544,7 @@ const args_fmt_spec = []Flag{
538544 "off",
539545 "on",
540546 }),
547 Flag.Bool("--stdin"),
541548};
542549
543550const Fmt = struct {
......@@ -579,11 +586,6 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
579586 os.exit(0);
580587 }
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
587589 const color = blk: {
588590 if (flags.single("color")) |color_flag| {
589591 if (mem.eql(u8, color_flag, "auto")) {
......@@ -598,6 +600,44 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
598600 }
599601 };
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
601641 var fmt = Fmt{
602642 .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator),
603643 .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 @@
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
110pub const Scope = struct {
211 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
550 pub const Id = enum {
651 Decls,
752 Block,
8 Defer,
9 DeferExpr,
10 VarDecl,
11 CImport,
12 Loop,
1353 FnDef,
1454 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 }
15233 };
16234};
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 {
20032003 IrInstruction *must_be_comptime_source_instr;
20042004};
20052005
2006struct LVal {
2007 bool is_ptr;
2008 bool is_const;
2009 bool is_volatile;
2010};
2011
20122006enum IrInstructionId {
20132007 IrInstructionIdInvalid,
20142008 IrInstructionIdBr,
......@@ -2970,6 +2964,11 @@ struct IrInstructionTypeName {
29702964 IrInstruction *type_value;
29712965};
29722966
2967enum LVal {
2968 LValNone,
2969 LValPtr,
2970};
2971
29732972struct IrInstructionDeclRef {
29742973 IrInstruction base;
29752974
src/analyze.cpp+20-3
......@@ -1430,10 +1430,10 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
14301430 case TypeTableEntryIdBoundFn:
14311431 case TypeTableEntryIdArgTuple:
14321432 case TypeTableEntryIdPromise:
1433 case TypeTableEntryIdVoid:
14331434 return false;
14341435 case TypeTableEntryIdOpaque:
14351436 case TypeTableEntryIdUnreachable:
1436 case TypeTableEntryIdVoid:
14371437 case TypeTableEntryIdBool:
14381438 return true;
14391439 case TypeTableEntryIdInt:
......@@ -1460,7 +1460,10 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
14601460 case TypeTableEntryIdOptional:
14611461 {
14621462 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);
14641467 }
14651468 case TypeTableEntryIdEnum:
14661469 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
16371640 fn_type_id.return_type = specified_return_type;
16381641 }
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 {
16411647 add_node_error(g, fn_proto->return_type,
16421648 buf_sprintf("return type '%s' not allowed in function with calling convention '%s'",
16431649 buf_ptr(&fn_type_id.return_type->name),
......@@ -1939,6 +1945,17 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
19391945 break;
19401946 }
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
19421959 if (!type_has_bits(field_type))
19431960 continue;
19441961
src/codegen.cpp+5-3
......@@ -2212,10 +2212,8 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
22122212 return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
22132213 } else if (type_entry->id == TypeTableEntryIdEnum ||
22142214 type_entry->id == TypeTableEntryIdErrorSet ||
2215 type_entry->id == TypeTableEntryIdPointer ||
22162215 type_entry->id == TypeTableEntryIdBool ||
2217 type_entry->id == TypeTableEntryIdPromise ||
2218 type_entry->id == TypeTableEntryIdFn)
2216 get_codegen_ptr_type(type_entry) != nullptr)
22192217 {
22202218 LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false);
22212219 return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, "");
......@@ -3103,6 +3101,10 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
31033101 return nullptr;
31043102 } else if (first_arg_ret) {
31053103 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;
31063108 } else {
31073109 return result;
31083110 }
src/ir.cpp+53-96
......@@ -39,9 +39,6 @@ struct IrAnalyze {
3939 IrBasicBlock *const_predecessor_bb;
4040};
4141
42static const LVal LVAL_NONE = { false, false, false };
43static const LVal LVAL_PTR = { true, false, false };
44
4542enum ConstCastResultId {
4643 ConstCastResultIdOk,
4744 ConstCastResultIdErrSet,
......@@ -249,8 +246,6 @@ static void ir_ref_bb(IrBasicBlock *bb) {
249246static void ir_ref_instruction(IrInstruction *instruction, IrBasicBlock *cur_bb) {
250247 assert(instruction->id != IrInstructionIdInvalid);
251248 instruction->ref_count += 1;
252 if (instruction->owner_bb != cur_bb && !instr_is_comptime(instruction))
253 ir_ref_bb(instruction->owner_bb);
254249}
255250
256251static void ir_ref_var(VariableTableEntry *var) {
......@@ -3164,7 +3159,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
31643159 case ReturnKindError:
31653160 {
31663161 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);
31683163 if (err_union_ptr == irb->codegen->invalid_instruction)
31693164 return irb->codegen->invalid_instruction;
31703165 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,
31923187
31933188 ir_set_cursor_at_end_and_append_block(irb, continue_block);
31943189 IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false);
3195 if (lval.is_ptr)
3190 if (lval == LValPtr)
31963191 return unwrapped_ptr;
31973192 else
31983193 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
33573352}
33583353
33593354static 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);
33613356 IrInstruction *rvalue = ir_gen_node(irb, node->data.bin_op_expr.op2, scope);
33623357
33633358 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)
33683363}
33693364
33703365static 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);
33723367 if (lvalue == irb->codegen->invalid_instruction)
33733368 return lvalue;
33743369 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
34703465 AstNode *op1_node = node->data.bin_op_expr.op1;
34713466 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);
34743469 if (maybe_ptr == irb->codegen->invalid_instruction)
34753470 return irb->codegen->invalid_instruction;
34763471
......@@ -3657,7 +3652,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,
36573652
36583653 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) {
36613656 IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
36623657 const_instruction->base.value.type = get_pointer_to_type(irb->codegen,
36633658 irb->codegen->builtin_types.entry_void, false);
......@@ -3669,8 +3664,8 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,
36693664 auto primitive_table_entry = irb->codegen->primitive_type_table.maybe_get(variable_name);
36703665 if (primitive_table_entry) {
36713666 IrInstruction *value = ir_build_const_type(irb, scope, node, primitive_table_entry->value);
3672 if (lval.is_ptr) {
3673 return ir_build_ref(irb, scope, node, value, lval.is_const, lval.is_volatile);
3667 if (lval == LValPtr) {
3668 return ir_build_ref(irb, scope, node, value, false, false);
36743669 } else {
36753670 return value;
36763671 }
......@@ -3679,7 +3674,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,
36793674 VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name);
36803675 if (var) {
36813676 IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var);
3682 if (lval.is_ptr)
3677 if (lval == LValPtr)
36833678 return var_ptr;
36843679 else
36853680 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
37053700 assert(node->type == NodeTypeArrayAccessExpr);
37063701
37073702 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);
37093704 if (array_ref_instruction == irb->codegen->invalid_instruction)
37103705 return array_ref_instruction;
37113706
......@@ -3716,7 +3711,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode
37163711
37173712 IrInstruction *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction,
37183713 subscript_instruction, true, PtrLenSingle);
3719 if (lval.is_ptr)
3714 if (lval == LValPtr)
37203715 return ptr_instruction;
37213716
37223717 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
37283723 AstNode *container_ref_node = node->data.field_access_expr.struct_expr;
37293724 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);
37323727 if (container_ref_instruction == irb->codegen->invalid_instruction)
37333728 return container_ref_instruction;
37343729
......@@ -4386,7 +4381,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
43864381 case BuiltinFnIdField:
43874382 {
43884383 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);
43904385 if (arg0_value == irb->codegen->invalid_instruction)
43914386 return arg0_value;
43924387
......@@ -4397,7 +4392,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
43974392
43984393 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)
44014396 return ptr_instruction;
44024397
44034398 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
49284923}
49294924
49304925static 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);
49324927}
49334928
49344929static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval) {
4935 if (!lval.is_ptr)
4930 if (lval != LValPtr)
49364931 return value;
49374932 if (value == irb->codegen->invalid_instruction)
49384933 return value;
49394934
49404935 // We needed a pointer to a value, but we got a value. So we create
49414936 // 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);
49434938}
49444939
49454940static 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
50014996static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode *source_node, AstNode *expr_node,
50024997 LVal lval)
50034998{
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);
50055000 if (err_union_ptr == irb->codegen->invalid_instruction)
50065001 return irb->codegen->invalid_instruction;
50075002
......@@ -5009,7 +5004,7 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode
50095004 if (payload_ptr == irb->codegen->invalid_instruction)
50105005 return irb->codegen->invalid_instruction;
50115006
5012 if (lval.is_ptr)
5007 if (lval == LValPtr)
50135008 return payload_ptr;
50145009
50155010 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
50465041 return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval);
50475042 case PrefixOpAddrOf: {
50485043 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);
50505045 }
50515046 }
50525047 zig_unreachable();
......@@ -5186,7 +5181,7 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
51865181 } else {
51875182 payload_scope = scope;
51885183 }
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);
51905185 if (err_val_ptr == irb->codegen->invalid_instruction)
51915186 return err_val_ptr;
51925187 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
52695264 VariableTableEntry *payload_var = ir_create_var(irb, symbol_node, scope, var_symbol,
52705265 true, false, false, is_comptime);
52715266 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);
52735268 if (maybe_val_ptr == irb->codegen->invalid_instruction)
52745269 return maybe_val_ptr;
52755270 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
54135408 }
54145409 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);
54175412 if (array_val_ptr == irb->codegen->invalid_instruction)
54185413 return array_val_ptr;
54195414
......@@ -5700,7 +5695,7 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no
57005695 AstNode *else_node = node->data.test_expr.else_node;
57015696 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);
57045699 if (maybe_val_ptr == irb->codegen->invalid_instruction)
57055700 return maybe_val_ptr;
57065701
......@@ -5778,7 +5773,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *
57785773 Buf *var_symbol = node->data.if_err_expr.var_symbol;
57795774 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);
57825777 if (err_val_ptr == irb->codegen->invalid_instruction)
57835778 return err_val_ptr;
57845779
......@@ -5904,7 +5899,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
59045899 assert(node->type == NodeTypeSwitchExpr);
59055900
59065901 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);
59085903 if (target_value_ptr == irb->codegen->invalid_instruction)
59095904 return target_value_ptr;
59105905 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)
62776272 AstNode *start_node = slice_expr->start;
62786273 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);
62816276 if (ptr_value == irb->codegen->invalid_instruction)
62826277 return irb->codegen->invalid_instruction;
62836278
......@@ -6311,11 +6306,11 @@ static IrInstruction *ir_gen_err_ok_or(IrBuilder *irb, Scope *parent_scope, AstN
63116306 add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name)));
63126307 return irb->codegen->invalid_instruction;
63136308 }
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);
63156310 }
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);
63196314 if (err_union_ptr == irb->codegen->invalid_instruction)
63206315 return irb->codegen->invalid_instruction;
63216316
......@@ -6868,7 +6863,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
68686863 IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node);
68696864 if (ptr_instruction == irb->codegen->invalid_instruction)
68706865 return ptr_instruction;
6871 if (lval.is_ptr)
6866 if (lval == LValPtr)
68726867 return ptr_instruction;
68736868
68746869 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
68846879 case NodeTypeUnwrapOptional: {
68856880 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);
68886883 if (maybe_ptr == irb->codegen->invalid_instruction)
68896884 return irb->codegen->invalid_instruction;
68906885
68916886 IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true);
6892 if (lval.is_ptr)
6887 if (lval == LValPtr)
68936888 return unwrapped_ptr;
68946889
68956890 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
69596954}
69606955
69616956static 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);
69636958}
69646959
69656960static void invalidate_exec(IrExecutable *exec) {
......@@ -7089,7 +7084,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
70897084 irb->exec->coro_final_cleanup_block = ir_create_basic_block(irb, scope, "FinalCleanup");
70907085 }
70917086
7092 IrInstruction *result = ir_gen_node_extra(irb, node, scope, LVAL_NONE);
7087 IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValNone);
70937088 assert(result);
70947089 if (irb->exec->invalid)
70957090 return false;
......@@ -9242,26 +9237,9 @@ static TypeTableEntry *ir_finish_anal(IrAnalyze *ira, TypeTableEntry *result_typ
92429237}
92439238
92449239static IrInstruction *ir_get_const(IrAnalyze *ira, IrInstruction *old_instruction) {
9245 IrInstruction *new_instruction;
9246 if (old_instruction->id == IrInstructionIdVarPtr) {
9247 IrInstructionVarPtr *old_var_ptr_instruction = (IrInstructionVarPtr *)old_instruction;
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 }
9240 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
9241 old_instruction->scope, old_instruction->source_node);
9242 IrInstruction *new_instruction = &const_instruction->base;
92659243 new_instruction->value.special = ConstValSpecialStatic;
92669244 return new_instruction;
92679245}
......@@ -9615,23 +9593,6 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi
96159593 if (type_is_invalid(value->value.type))
96169594 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
96359596 if (instr_is_comptime(value)) {
96369597 ConstExprValue *val = ir_resolve_const(ira, value, UndefOk);
96379598 if (!val)
......@@ -11150,7 +11111,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
1115011111 if (type_is_invalid(resolved_type))
1115111112 return resolved_type;
1115211113
11153
11114 bool operator_allowed;
1115411115 switch (resolved_type->id) {
1115511116 case TypeTableEntryIdInvalid:
1115611117 zig_unreachable(); // handled above
......@@ -11159,6 +11120,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
1115911120 case TypeTableEntryIdComptimeInt:
1116011121 case TypeTableEntryIdInt:
1116111122 case TypeTableEntryIdFloat:
11123 operator_allowed = true;
1116211124 break;
1116311125
1116411126 case TypeTableEntryIdBool:
......@@ -11173,19 +11135,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
1117311135 case TypeTableEntryIdBoundFn:
1117411136 case TypeTableEntryIdArgTuple:
1117511137 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
1118311138 case TypeTableEntryIdEnum:
11184 if (!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 }
11139 operator_allowed = is_equality_cmp;
1118911140 break;
1119011141
1119111142 case TypeTableEntryIdUnreachable:
......@@ -11193,12 +11144,18 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
1119311144 case TypeTableEntryIdStruct:
1119411145 case TypeTableEntryIdUndefined:
1119511146 case TypeTableEntryIdNull:
11196 case TypeTableEntryIdOptional:
1119711147 case TypeTableEntryIdErrorUnion:
1119811148 case TypeTableEntryIdUnion:
11199 ir_add_error_node(ira, source_node,
11200 buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name)));
11201 return ira->codegen->builtin_types.entry_invalid;
11149 operator_allowed = false;
11150 break;
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;
1120211159 }
1120311160
1120411161 IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type);
......@@ -19752,7 +19709,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
1975219709 Tld *tld = instruction->tld;
1975319710 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);
1975619713 if (tld->resolution == TldResolutionInvalid)
1975719714 return ira->codegen->builtin_types.entry_invalid;
1975819715
......@@ -19773,7 +19730,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
1977319730 add_link_lib_symbol(ira, tld_var->extern_lib_name, &var->name, instruction->base.source_node);
1977419731 }
1977519732
19776 if (lval.is_ptr) {
19733 if (lval == LValPtr) {
1977719734 ir_link_new_instruction(var_ptr, &instruction->base);
1977819735 return var_ptr->value.type;
1977919736 } else {
......@@ -19794,7 +19751,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira,
1979419751
1979519752 IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope,
1979619753 instruction->base.source_node, fn_entry);
19797 if (lval.is_ptr) {
19754 if (lval == LValPtr) {
1979819755 IrInstruction *ptr_instr = ir_get_ref(ira, &instruction->base, ref_instruction, true, false);
1979919756 ir_link_new_instruction(ptr_instr, &instruction->base);
1980019757 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) {
10051005}
10061006
10071007static void ir_print_decl_ref(IrPrint *irp, IrInstructionDeclRef *instruction) {
1008 const char *ptr_str = instruction->lval.is_ptr ? "ptr " : "";
1009 const char *const_str = instruction->lval.is_const ? "const " : "";
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));
1008 const char *ptr_str = (instruction->lval == LValPtr) ? "ptr " : "";
1009 fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name));
10121010}
10131011
10141012static void ir_print_panic(IrPrint *irp, IrInstructionPanic *instruction) {
src/main.cpp+7-3
......@@ -891,15 +891,19 @@ int main(int argc, char **argv) {
891891
892892 add_package(g, cur_pkg, g->root_package);
893893
894 if (cmd == CmdBuild || cmd == CmdRun) {
895 codegen_set_emit_file_type(g, emit_file_type);
896
894 if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) {
897895 for (size_t i = 0; i < objects.length; i += 1) {
898896 codegen_add_object(g, buf_create_from_str(objects.at(i)));
899897 }
900898 for (size_t i = 0; i < asm_files.length; i += 1) {
901899 codegen_add_assembly(g, buf_create_from_str(asm_files.at(i)));
902900 }
901 }
902
903
904 if (cmd == CmdBuild || cmd == CmdRun) {
905 codegen_set_emit_file_type(g, emit_file_type);
906
903907 codegen_build(g);
904908 codegen_link(g, out_file);
905909 if (timing_info)
std/array_list.zig+70-6
......@@ -41,8 +41,8 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
4141 return self.items[0..self.len];
4242 }
4343
44 pub fn at(self: Self, n: usize) T {
45 return self.toSliceConst()[n];
44 pub fn at(self: Self, i: usize) T {
45 return self.toSliceConst()[i];
4646 }
4747
4848 /// 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 {
8585 try self.ensureCapacity(self.len + 1);
8686 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]);
8989 self.items[n] = item;
9090 }
9191
......@@ -93,7 +93,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
9393 try self.ensureCapacity(self.len + items.len);
9494 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]);
9797 mem.copy(T, self.items[n .. n + items.len], items);
9898 }
9999
......@@ -102,6 +102,26 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
102102 new_item_ptr.* = item;
103103 }
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
105125 pub fn appendSlice(self: *Self, items: []align(A) const T) !void {
106126 try self.ensureCapacity(self.len + items.len);
107127 mem.copy(T, self.items[self.len..], items);
......@@ -232,6 +252,33 @@ test "basic ArrayList test" {
232252 assert(list.pop() == 33);
233253}
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
235282test "iterator ArrayList test" {
236283 var list = ArrayList(i32).init(debug.global_allocator);
237284 defer list.deinit();
......@@ -266,19 +313,36 @@ test "insert ArrayList test" {
266313 defer list.deinit();
267314
268315 try list.append(1);
316 try list.append(2);
317 try list.append(3);
269318 try list.insert(0, 5);
270319 assert(list.items[0] == 5);
271320 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);
273333 try list.insertSlice(1, []const i32{
274334 9,
275335 8,
276336 });
277 assert(list.items[0] == 5);
337 assert(list.items[0] == 1);
278338 assert(list.items[1] == 9);
279339 assert(list.items[2] == 8);
340 assert(list.items[3] == 2);
341 assert(list.items[4] == 3);
342 assert(list.items[5] == 4);
280343
281344 const items = []const i32{1};
282345 try list.insertSlice(0, items[0..0]);
283 assert(list.items[0] == 5);
346 assert(list.len == 6);
347 assert(list.items[0] == 1);
284348}
std/atomic/index.zig+4-4
......@@ -1,9 +1,9 @@
11pub const Stack = @import("stack.zig").Stack;
2pub const QueueMpsc = @import("queue_mpsc.zig").QueueMpsc;
3pub const QueueMpmc = @import("queue_mpmc.zig").QueueMpmc;
2pub const Queue = @import("queue.zig").Queue;
3pub const Int = @import("int.zig").Int;
44
55test "std.atomic" {
66 _ = @import("stack.zig");
7 _ = @import("queue_mpsc.zig");
8 _ = @import("queue_mpmc.zig");
7 _ = @import("queue.zig");
8 _ = @import("int.zig");
99}
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 @@
1const assert = std.debug.assert;
12const builtin = @import("builtin");
23const AtomicOrder = builtin.AtomicOrder;
34
4/// Many reader, many writer, non-allocating, thread-safe, lock-free
5/// Many reader, many writer, non-allocating, thread-safe
6/// Uses a spinlock to protect push() and pop()
57pub fn Stack(comptime T: type) type {
68 return struct {
79 root: ?*Node,
10 lock: u8,
811
912 pub const Self = this;
1013
......@@ -14,7 +17,10 @@ pub fn Stack(comptime T: type) type {
1417 };
1518
1619 pub fn init() Self {
17 return Self{ .root = null };
20 return Self{
21 .root = null,
22 .lock = 0,
23 };
1824 }
1925
2026 /// 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 {
2531 }
2632
2733 pub fn push(self: *Self, node: *Node) void {
28 var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst);
29 while (true) {
30 node.next = root;
31 root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse break;
32 }
34 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
35 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
36
37 node.next = self.root;
38 self.root = node;
3339 }
3440
3541 pub fn pop(self: *Self) ?*Node {
36 var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst);
37 while (true) {
38 root = @cmpxchgWeak(?*Node, &self.root, root, (root orelse return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return root;
39 }
42 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
43 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
44
45 const root = self.root orelse return null;
46 self.root = root.next;
47 return root;
4048 }
4149
4250 pub fn isEmpty(self: *Self) bool {
......@@ -45,7 +53,7 @@ pub fn Stack(comptime T: type) type {
4553 };
4654}
4755
48const std = @import("std");
56const std = @import("../index.zig");
4957const Context = struct {
5058 allocator: *std.mem.Allocator,
5159 stack: *Stack(i32),
std/build.zig+22
......@@ -1596,6 +1596,8 @@ pub const TestStep = struct {
15961596 target: Target,
15971597 exec_cmd_args: ?[]const ?[]const u8,
15981598 include_dirs: ArrayList([]const u8),
1599 lib_paths: ArrayList([]const u8),
1600 object_files: ArrayList([]const u8),
15991601
16001602 pub fn init(builder: *Builder, root_src: []const u8) TestStep {
16011603 const step_name = builder.fmt("test {}", root_src);
......@@ -1611,9 +1613,15 @@ pub const TestStep = struct {
16111613 .target = Target{ .Native = {} },
16121614 .exec_cmd_args = null,
16131615 .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),
16141618 };
16151619 }
16161620
1621 pub fn addLibPath(self: *TestStep, path: []const u8) void {
1622 self.lib_paths.append(path) catch unreachable;
1623 }
1624
16171625 pub fn setVerbose(self: *TestStep, value: bool) void {
16181626 self.verbose = value;
16191627 }
......@@ -1638,6 +1646,10 @@ pub const TestStep = struct {
16381646 self.filter = text;
16391647 }
16401648
1649 pub fn addObjectFile(self: *TestStep, path: []const u8) void {
1650 self.object_files.append(path) catch unreachable;
1651 }
1652
16411653 pub fn setTarget(self: *TestStep, target_arch: builtin.Arch, target_os: builtin.Os, target_environ: builtin.Environ) void {
16421654 self.target = Target{
16431655 .Cross = CrossTarget{
......@@ -1699,6 +1711,11 @@ pub const TestStep = struct {
16991711 try zig_args.append(self.name_prefix);
17001712 }
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
17021719 {
17031720 var it = self.link_libs.iterator();
17041721 while (true) {
......@@ -1734,6 +1751,11 @@ pub const TestStep = struct {
17341751 try zig_args.append(rpath);
17351752 }
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
17371759 for (builder.lib_paths.toSliceConst()) |lib_path| {
17381760 try zig_args.append("--library-path");
17391761 try zig_args.append(lib_path);
std/c/darwin.zig+1-1
......@@ -44,7 +44,7 @@ pub const timezone = extern struct {
4444 tz_dsttime: i32,
4545};
4646
47pub const mach_timebase_info_data = struct {
47pub const mach_timebase_info_data = extern struct {
4848 numer: u32,
4949 denom: u32,
5050};
std/event.zig+4
......@@ -3,6 +3,8 @@ pub const Loop = @import("event/loop.zig").Loop;
33pub const Lock = @import("event/lock.zig").Lock;
44pub const tcp = @import("event/tcp.zig");
55pub const Channel = @import("event/channel.zig").Channel;
6pub const Group = @import("event/group.zig").Group;
7pub const Future = @import("event/future.zig").Future;
68
79test "import event tests" {
810 _ = @import("event/locked.zig");
......@@ -10,4 +12,6 @@ test "import event tests" {
1012 _ = @import("event/lock.zig");
1113 _ = @import("event/tcp.zig");
1214 _ = @import("event/channel.zig");
15 _ = @import("event/group.zig");
16 _ = @import("event/future.zig");
1317}
std/event/channel.zig+6-6
......@@ -12,8 +12,8 @@ pub fn Channel(comptime T: type) type {
1212 return struct {
1313 loop: *Loop,
1414
15 getters: std.atomic.QueueMpsc(GetNode),
16 putters: std.atomic.QueueMpsc(PutNode),
15 getters: std.atomic.Queue(GetNode),
16 putters: std.atomic.Queue(PutNode),
1717 get_count: usize,
1818 put_count: usize,
1919 dispatch_lock: u8, // TODO make this a bool
......@@ -46,8 +46,8 @@ pub fn Channel(comptime T: type) type {
4646 .buffer_index = 0,
4747 .dispatch_lock = 0,
4848 .need_dispatch = 0,
49 .getters = std.atomic.QueueMpsc(GetNode).init(),
50 .putters = std.atomic.QueueMpsc(PutNode).init(),
49 .getters = std.atomic.Queue(GetNode).init(),
50 .putters = std.atomic.Queue(PutNode).init(),
5151 .get_count = 0,
5252 .put_count = 0,
5353 });
......@@ -81,7 +81,7 @@ pub fn Channel(comptime T: type) type {
8181 .next = undefined,
8282 .data = handle,
8383 };
84 var queue_node = std.atomic.QueueMpsc(PutNode).Node{
84 var queue_node = std.atomic.Queue(PutNode).Node{
8585 .data = PutNode{
8686 .tick_node = &my_tick_node,
8787 .data = data,
......@@ -111,7 +111,7 @@ pub fn Channel(comptime T: type) type {
111111 .next = undefined,
112112 .data = handle,
113113 };
114 var queue_node = std.atomic.QueueMpsc(GetNode).Node{
114 var queue_node = std.atomic.Queue(GetNode).Node{
115115 .data = GetNode{
116116 .ptr = &result,
117117 .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 {
1515 queue: Queue,
1616 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
2020 pub const Held = struct {
2121 lock: *Lock,
......@@ -73,6 +73,15 @@ pub const Lock = struct {
7373 };
7474 }
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
7685 /// Must be called when not locked. Not thread safe.
7786 /// All calls to acquire() and release() must complete before calling deinit().
7887 pub fn deinit(self: *Lock) void {
......@@ -81,7 +90,7 @@ pub const Lock = struct {
8190 }
8291
8392 pub async fn acquire(self: *Lock) Held {
84 s: suspend |handle| {
93 suspend |handle| {
8594 // TODO explicitly put this memory in the coroutine frame #1194
8695 var my_tick_node = Loop.NextTickNode{
8796 .data = handle,
std/event/loop.zig+18-4
......@@ -9,7 +9,7 @@ const AtomicOrder = builtin.AtomicOrder;
99
1010pub const Loop = struct {
1111 allocator: *mem.Allocator,
12 next_tick_queue: std.atomic.QueueMpsc(promise),
12 next_tick_queue: std.atomic.Queue(promise),
1313 os_data: OsData,
1414 final_resume_node: ResumeNode,
1515 dispatch_lock: u8, // TODO make this a bool
......@@ -21,7 +21,7 @@ pub const Loop = struct {
2121 available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd),
2222 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
2626 pub const ResumeNode = struct {
2727 id: Id,
......@@ -77,7 +77,7 @@ pub const Loop = struct {
7777 .pending_event_count = 0,
7878 .allocator = allocator,
7979 .os_data = undefined,
80 .next_tick_queue = std.atomic.QueueMpsc(promise).init(),
80 .next_tick_queue = std.atomic.Queue(promise).init(),
8181 .dispatch_lock = 1, // start locked so threads go directly into epoll wait
8282 .extra_threads = undefined,
8383 .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(),
......@@ -101,7 +101,6 @@ pub const Loop = struct {
101101 errdefer self.deinitOsData();
102102 }
103103
104 /// must call stop before deinit
105104 pub fn deinit(self: *Loop) void {
106105 self.deinitOsData();
107106 self.allocator.free(self.extra_threads);
......@@ -382,6 +381,21 @@ pub const Loop = struct {
382381 return async<self.allocator> S.asyncFunc(self, &handle, args);
383382 }
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
385399 fn workerRun(self: *Loop) void {
386400 start_over: while (true) {
387401 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 {
302302 }
303303
304304 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);
305307 if (new_size <= old_mem.len) {
306308 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;
307316 } else {
308317 const result = try alloc(allocator, new_size, alignment);
309318 mem.copy(u8, result, old_mem);
......@@ -442,6 +451,7 @@ test "DirectAllocator" {
442451
443452 const allocator = &direct_allocator.allocator;
444453 try testAllocator(allocator);
454 try testAllocatorAligned(allocator, 16);
445455 try testAllocatorLargeAlignment(allocator);
446456}
447457
......@@ -453,6 +463,7 @@ test "ArenaAllocator" {
453463 defer arena_allocator.deinit();
454464
455465 try testAllocator(&arena_allocator.allocator);
466 try testAllocatorAligned(&arena_allocator.allocator, 16);
456467 try testAllocatorLargeAlignment(&arena_allocator.allocator);
457468}
458469
......@@ -461,35 +472,98 @@ test "FixedBufferAllocator" {
461472 var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
462473
463474 try testAllocator(&fixed_buffer_allocator.allocator);
475 try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);
464476 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
465477}
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
467508test "ThreadSafeFixedBufferAllocator" {
468509 var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
469510
470511 try testAllocator(&fixed_buffer_allocator.allocator);
512 try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);
471513 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
472514}
473515
474516fn testAllocator(allocator: *mem.Allocator) !void {
475517 var slice = try allocator.alloc(*i32, 100);
476
518 assert(slice.len == 100);
477519 for (slice) |*item, i| {
478520 item.* = try allocator.create(@intCast(i32, i));
479521 }
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));
482528 allocator.destroy(item);
483529 }
484530
485 slice = try allocator.realloc(*i32, slice, 20000);
486531 slice = try allocator.realloc(*i32, slice, 50);
532 assert(slice.len == 50);
487533 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);
488537 slice = try allocator.realloc(*i32, slice, 10);
538 assert(slice.len == 10);
489539
490540 allocator.free(slice);
491541}
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
493567fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!void {
494568 //Maybe a platform's page_size is actually the same as or
495569 // very near usize?
std/macho.zig+1-1
......@@ -42,7 +42,7 @@ pub const Symbol = struct {
4242 name: []const u8,
4343 address: u64,
4444
45 fn addressLessThan(lhs: *const Symbol, rhs: *const Symbol) bool {
45 fn addressLessThan(lhs: Symbol, rhs: Symbol) bool {
4646 return lhs.address < rhs.address;
4747 }
4848};
std/mem.zig+23-2
......@@ -23,7 +23,10 @@ pub const Allocator = struct {
2323 /// * this function must return successfully.
2424 /// * alignment <= alignment of old_mem.ptr
2525 ///
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.
2730 /// `alignment` is guaranteed to be >= 1
2831 /// `alignment` is guaranteed to be a power of 2
2932 reallocFn: fn (self: *Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,
......@@ -71,7 +74,7 @@ pub const Allocator = struct {
7174
7275 pub fn alignedRealloc(self: *Allocator, comptime T: type, comptime alignment: u29, old_mem: []align(alignment) T, n: usize) ![]align(alignment) T {
7376 if (old_mem.len == 0) {
74 return self.alloc(T, n);
77 return self.alignedAlloc(T, alignment, n);
7578 }
7679 if (n == 0) {
7780 self.free(old_mem);
......@@ -125,6 +128,7 @@ pub const Allocator = struct {
125128
126129/// Copy all of source into dest at position 0.
127130/// dest.len must be >= source.len.
131/// dest.ptr must be <= src.ptr.
128132pub fn copy(comptime T: type, dest: []T, source: []const T) void {
129133 // TODO instead of manually doing this check for the whole array
130134 // 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 {
135139 dest[i] = s;
136140}
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
138159pub fn set(comptime T: type, dest: []T, value: T) void {
139160 for (dest) |*d|
140161 d.* = value;
std/os/windows/index.zig-2
......@@ -59,7 +59,6 @@ pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
5959 dwFlags: DWORD,
6060) BOOLEAN;
6161
62
6362pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
6463
6564pub 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(
134133 dwFlags: DWORD,
135134) BOOL;
136135
137
138136pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
139137
140138pub 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
215215 return true;
216216}
217217
218
219pub const WindowsCreateIoCompletionPortError = error {
220 Unexpected,
221};
218pub const WindowsCreateIoCompletionPortError = error{Unexpected};
222219
223220pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_completion_port: ?windows.HANDLE, completion_key: usize, concurrent_thread_count: windows.DWORD) !windows.HANDLE {
224221 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
230227 return handle;
231228}
232229
233pub const WindowsPostQueuedCompletionStatusError = error {
234 Unexpected,
235};
230pub const WindowsPostQueuedCompletionStatusError = error{Unexpected};
236231
237232pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void {
238233 if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
......@@ -243,7 +238,7 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_
243238 }
244239}
245240
246pub const WindowsWaitResult = error {
241pub const WindowsWaitResult = error{
247242 Normal,
248243 Aborted,
249244};
std/sort.zig+86-239
......@@ -5,7 +5,7 @@ const math = std.math;
55const builtin = @import("builtin");
66
77/// 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 {
99 {
1010 var i: usize = 1;
1111 while (i < items.len) : (i += 1) {
......@@ -30,7 +30,7 @@ const Range = struct {
3030 };
3131 }
3232
33 fn length(self: *const Range) usize {
33 fn length(self: Range) usize {
3434 return self.end - self.start;
3535 }
3636};
......@@ -108,7 +108,7 @@ const Pull = struct {
108108
109109/// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required).
110110/// 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 {
112112 // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c
113113 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
131131 // http://pages.ripco.net/~jgamble/nw.html
132132 var iterator = Iterator.init(items.len, 4);
133133 while (!iterator.finished()) {
134 var order = []u8{
135 0,
136 1,
137 2,
138 3,
139 4,
140 5,
141 6,
142 7,
143 };
134 var order = []u8{ 0, 1, 2, 3, 4, 5, 6, 7 };
144135 const range = iterator.nextRange();
145136
146137 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
741732}
742733
743734// 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 {
745736 if (A_arg.length() == 0 or B_arg.length() == 0) return;
746737
747738 // 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
762753 // again, this is NOT a general-purpose solution – it only works well in this case!
763754 // kind of like how the O(n^2) insertion sort is used in some places
764755
765 var A = A_arg.*;
766 var B = B_arg.*;
756 var A = A_arg;
757 var B = B_arg;
767758
768759 while (true) {
769760 // 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
783774}
784775
785776// 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 {
787778 // whenever we find a value to add to the final array, swap it with the value that's already in that spot
788779 // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order
789780 var A_count: usize = 0;
......@@ -819,7 +810,7 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s
819810
820811// combine a linear search with a binary search to reduce the number of comparisons in situations
821812// 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 {
823814 if (range.length() == 0) return range.start;
824815 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
833824 return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan);
834825}
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 {
837828 if (range.length() == 0) return range.start;
838829 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
847838 return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan);
848839}
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 {
851842 if (range.length() == 0) return range.start;
852843 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
861852 return binaryLast(T, items, value, Range.init(index - skip, index), lessThan);
862853}
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 {
865856 if (range.length() == 0) return range.start;
866857 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
875866 return binaryLast(T, items, value, Range.init(index, index + skip), lessThan);
876867}
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 {
879870 var start = range.start;
880871 var end = range.end - 1;
881872 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
893884 return start;
894885}
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 {
897888 var start = range.start;
898889 var end = range.end - 1;
899890 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
911902 return start;
912903}
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 {
915906 var A_index: usize = A.start;
916907 var B_index: usize = B.start;
917908 const A_last = A.end;
......@@ -941,7 +932,7 @@ fn mergeInto(comptime T: type, from: []T, A: *const Range, B: *const Range, less
941932 }
942933}
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 {
945936 // A fits into the cache, so use that instead of the internal buffer
946937 var A_index: usize = 0;
947938 var B_index: usize = B.start;
......@@ -969,27 +960,32 @@ fn mergeExternal(comptime T: type, items: []T, A: *const Range, B: *const Range,
969960 mem.copy(T, items[insert_index..], cache[A_index..A_last]);
970961}
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 {
973964 if (lessThan(items[y], items[x]) or ((order.*)[x] > (order.*)[y] and !lessThan(items[x], items[y]))) {
974965 mem.swap(T, &items[x], &items[y]);
975966 mem.swap(u8, &(order.*)[x], &(order.*)[y]);
976967 }
977968}
978969
979fn i32asc(lhs: *const i32, rhs: *const i32) bool {
980 return lhs.* < rhs.*;
981}
970// Use these to generate a comparator function for a given type. e.g. `sort(u8, slice, asc(u8))`.
971pub fn asc(comptime T: type) fn (T, T) bool {
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 {
984 return rhs.* < lhs.*;
978 return impl.inner;
985979}
986980
987fn u8asc(lhs: *const u8, rhs: *const u8) bool {
988 return lhs.* < rhs.*;
989}
981pub fn desc(comptime T: type) fn (T, T) bool {
982 const impl = struct {
983 fn inner(a: T, b: T) bool {
984 return a > b;
985 }
986 };
990987
991fn u8desc(lhs: *const u8, rhs: *const u8) bool {
992 return rhs.* < lhs.*;
988 return impl.inner;
993989}
994990
995991test "stable sort" {
......@@ -998,119 +994,38 @@ test "stable sort" {
998994}
999995fn testStableSort() void {
1000996 var expected = []IdAndValue{
1001 IdAndValue{
1002 .id = 0,
1003 .value = 0,
1004 },
1005 IdAndValue{
1006 .id = 1,
1007 .value = 0,
1008 },
1009 IdAndValue{
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 },
997 IdAndValue{ .id = 0, .value = 0 },
998 IdAndValue{ .id = 1, .value = 0 },
999 IdAndValue{ .id = 2, .value = 0 },
1000 IdAndValue{ .id = 0, .value = 1 },
1001 IdAndValue{ .id = 1, .value = 1 },
1002 IdAndValue{ .id = 2, .value = 1 },
1003 IdAndValue{ .id = 0, .value = 2 },
1004 IdAndValue{ .id = 1, .value = 2 },
1005 IdAndValue{ .id = 2, .value = 2 },
10371006 };
10381007 var cases = [][9]IdAndValue{
10391008 []IdAndValue{
1040 IdAndValue{
1041 .id = 0,
1042 .value = 0,
1043 },
1044 IdAndValue{
1045 .id = 0,
1046 .value = 1,
1047 },
1048 IdAndValue{
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 },
1009 IdAndValue{ .id = 0, .value = 0 },
1010 IdAndValue{ .id = 0, .value = 1 },
1011 IdAndValue{ .id = 0, .value = 2 },
1012 IdAndValue{ .id = 1, .value = 0 },
1013 IdAndValue{ .id = 1, .value = 1 },
1014 IdAndValue{ .id = 1, .value = 2 },
1015 IdAndValue{ .id = 2, .value = 0 },
1016 IdAndValue{ .id = 2, .value = 1 },
1017 IdAndValue{ .id = 2, .value = 2 },
10761018 },
10771019 []IdAndValue{
1078 IdAndValue{
1079 .id = 0,
1080 .value = 2,
1081 },
1082 IdAndValue{
1083 .id = 0,
1084 .value = 1,
1085 },
1086 IdAndValue{
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 },
1020 IdAndValue{ .id = 0, .value = 2 },
1021 IdAndValue{ .id = 0, .value = 1 },
1022 IdAndValue{ .id = 0, .value = 0 },
1023 IdAndValue{ .id = 1, .value = 2 },
1024 IdAndValue{ .id = 1, .value = 1 },
1025 IdAndValue{ .id = 1, .value = 0 },
1026 IdAndValue{ .id = 2, .value = 2 },
1027 IdAndValue{ .id = 2, .value = 1 },
1028 IdAndValue{ .id = 2, .value = 0 },
11141029 },
11151030 };
11161031 for (cases) |*case| {
......@@ -1125,8 +1040,8 @@ const IdAndValue = struct {
11251040 id: usize,
11261041 value: i32,
11271042};
1128fn cmpByValue(a: *const IdAndValue, b: *const IdAndValue) bool {
1129 return i32asc(a.value, b.value);
1043fn cmpByValue(a: IdAndValue, b: IdAndValue) bool {
1044 return asc(i32)(a.value, b.value);
11301045}
11311046
11321047test "std.sort" {
......@@ -1161,7 +1076,7 @@ test "std.sort" {
11611076 var buf: [8]u8 = undefined;
11621077 const slice = buf[0..case[0].len];
11631078 mem.copy(u8, slice, case[0]);
1164 sort(u8, slice, u8asc);
1079 sort(u8, slice, asc(u8));
11651080 assert(mem.eql(u8, slice, case[1]));
11661081 }
11671082
......@@ -1175,48 +1090,20 @@ test "std.sort" {
11751090 []i32{1},
11761091 },
11771092 [][]const i32{
1178 []i32{
1179 0,
1180 1,
1181 },
1182 []i32{
1183 0,
1184 1,
1185 },
1093 []i32{ 0, 1 },
1094 []i32{ 0, 1 },
11861095 },
11871096 [][]const i32{
1188 []i32{
1189 1,
1190 0,
1191 },
1192 []i32{
1193 0,
1194 1,
1195 },
1097 []i32{ 1, 0 },
1098 []i32{ 0, 1 },
11961099 },
11971100 [][]const i32{
1198 []i32{
1199 1,
1200 -1,
1201 0,
1202 },
1203 []i32{
1204 -1,
1205 0,
1206 1,
1207 },
1101 []i32{ 1, -1, 0 },
1102 []i32{ -1, 0, 1 },
12081103 },
12091104 [][]const i32{
1210 []i32{
1211 2,
1212 1,
1213 3,
1214 },
1215 []i32{
1216 1,
1217 2,
1218 3,
1219 },
1105 []i32{ 2, 1, 3 },
1106 []i32{ 1, 2, 3 },
12201107 },
12211108 };
12221109
......@@ -1224,7 +1111,7 @@ test "std.sort" {
12241111 var buf: [8]i32 = undefined;
12251112 const slice = buf[0..case[0].len];
12261113 mem.copy(i32, slice, case[0]);
1227 sort(i32, slice, i32asc);
1114 sort(i32, slice, asc(i32));
12281115 assert(mem.eql(i32, slice, case[1]));
12291116 }
12301117}
......@@ -1240,48 +1127,20 @@ test "std.sort descending" {
12401127 []i32{1},
12411128 },
12421129 [][]const i32{
1243 []i32{
1244 0,
1245 1,
1246 },
1247 []i32{
1248 1,
1249 0,
1250 },
1130 []i32{ 0, 1 },
1131 []i32{ 1, 0 },
12511132 },
12521133 [][]const i32{
1253 []i32{
1254 1,
1255 0,
1256 },
1257 []i32{
1258 1,
1259 0,
1260 },
1134 []i32{ 1, 0 },
1135 []i32{ 1, 0 },
12611136 },
12621137 [][]const i32{
1263 []i32{
1264 1,
1265 -1,
1266 0,
1267 },
1268 []i32{
1269 1,
1270 0,
1271 -1,
1272 },
1138 []i32{ 1, -1, 0 },
1139 []i32{ 1, 0, -1 },
12731140 },
12741141 [][]const i32{
1275 []i32{
1276 2,
1277 1,
1278 3,
1279 },
1280 []i32{
1281 3,
1282 2,
1283 1,
1284 },
1142 []i32{ 2, 1, 3 },
1143 []i32{ 3, 2, 1 },
12851144 },
12861145 };
12871146
......@@ -1289,28 +1148,16 @@ test "std.sort descending" {
12891148 var buf: [8]i32 = undefined;
12901149 const slice = buf[0..case[0].len];
12911150 mem.copy(i32, slice, case[0]);
1292 sort(i32, slice, i32desc);
1151 sort(i32, slice, desc(i32));
12931152 assert(mem.eql(i32, slice, case[1]));
12941153 }
12951154}
12961155
12971156test "another sort case" {
1298 var arr = []i32{
1299 5,
1300 3,
1301 1,
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 }));
1157 var arr = []i32{ 5, 3, 1, 2, 4 };
1158 sort(i32, arr[0..], asc(i32));
1159
1160 assert(mem.eql(i32, arr, []i32{ 1, 2, 3, 4, 5 }));
13141161}
13151162
13161163test "sort fuzz testing" {
......@@ -1345,7 +1192,7 @@ fn fuzzTest(rng: *std.rand.Random) void {
13451192 }
13461193}
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 {
13491196 var i: usize = 0;
13501197 var smallest = items[0];
13511198 for (items[1..]) |item| {
......@@ -1356,7 +1203,7 @@ pub fn min(comptime T: type, items: []T, lessThan: fn (lhs: *const T, rhs: *cons
13561203 return smallest;
13571204}
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 {
13601207 var i: usize = 0;
13611208 var biggest = items[0];
13621209 for (items[1..]) |item| {
std/zig/ast.zig-6
......@@ -970,14 +970,8 @@ pub const Node = struct {
970970 pub const Defer = struct {
971971 base: Node,
972972 defer_token: TokenIndex,
973 kind: Kind,
974973 expr: *Node,
975974
976 const Kind = enum {
977 Error,
978 Unconditional,
979 };
980
981975 pub fn iterate(self: *Defer, index: usize) ?*Node {
982976 var i = index;
983977
std/zig/parse.zig-5
......@@ -1041,11 +1041,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
10411041 const node = try arena.create(ast.Node.Defer{
10421042 .base = ast.Node{ .id = ast.Node.Id.Defer },
10431043 .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 },
10491044 .expr = undefined,
10501045 });
10511046 const node_ptr = try block.statements.addOne();
test/behavior.zig+1
......@@ -9,6 +9,7 @@ comptime {
99 _ = @import("cases/bitcast.zig");
1010 _ = @import("cases/bool.zig");
1111 _ = @import("cases/bugs/1111.zig");
12 _ = @import("cases/bugs/1230.zig");
1213 _ = @import("cases/bugs/394.zig");
1314 _ = @import("cases/bugs/655.zig");
1415 _ = @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" {
77 var o: ?*EmptyStruct = &e;
88 assert(o != null);
99}
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 @@
11const tests = @import("tests.zig");
22
33pub 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
431 cases.add(
532 "use of comptime-known undefined function value",
633 \\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{
4747
4848const 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 {
5151 const cases = b.allocator.create(CompareOutputContext{
5252 .b = b,
5353 .step = b.step("test-compare-output", "Run the compare output tests"),
5454 .test_index = 0,
5555 .test_filter = test_filter,
56 .modes = modes,
5657 }) catch unreachable;
5758
5859 compare_output.addCases(cases);
......@@ -60,12 +61,13 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8) *build
6061 return cases.step;
6162}
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 {
6465 const cases = b.allocator.create(CompareOutputContext{
6566 .b = b,
6667 .step = b.step("test-runtime-safety", "Run the runtime safety tests"),
6768 .test_index = 0,
6869 .test_filter = test_filter,
70 .modes = modes,
6971 }) catch unreachable;
7072
7173 runtime_safety.addCases(cases);
......@@ -73,12 +75,13 @@ pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8) *build
7375 return cases.step;
7476}
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 {
7779 const cases = b.allocator.create(CompileErrorContext{
7880 .b = b,
7981 .step = b.step("test-compile-errors", "Run the compile error tests"),
8082 .test_index = 0,
8183 .test_filter = test_filter,
84 .modes = modes,
8285 }) catch unreachable;
8386
8487 compile_errors.addCases(cases);
......@@ -99,12 +102,13 @@ pub fn addBuildExampleTests(b: *build.Builder, test_filter: ?[]const u8) *build.
99102 return cases.step;
100103}
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 {
103106 const cases = b.allocator.create(CompareOutputContext{
104107 .b = b,
105108 .step = b.step("test-asm-link", "Run the assemble and link tests"),
106109 .test_index = 0,
107110 .test_filter = test_filter,
111 .modes = modes,
108112 }) catch unreachable;
109113
110114 assemble_and_link.addCases(cases);
......@@ -138,16 +142,11 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
138142 return cases.step;
139143}
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 {
142146 const step = b.step(b.fmt("test-{}", name), desc);
143147 for (test_targets) |test_target| {
144148 const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch);
145 for ([]Mode{
146 Mode.Debug,
147 Mode.ReleaseSafe,
148 Mode.ReleaseFast,
149 Mode.ReleaseSmall,
150 }) |mode| {
149 for (modes) |mode| {
151150 for ([]bool{
152151 false,
153152 true,
......@@ -166,18 +165,6 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons
166165 if (link_libc) {
167166 these_tests.linkSystemLibrary("c");
168167 }
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 }
181168 step.dependOn(&these_tests.step);
182169 }
183170 }
......@@ -190,6 +177,7 @@ pub const CompareOutputContext = struct {
190177 step: *build.Step,
191178 test_index: usize,
192179 test_filter: ?[]const u8,
180 modes: []const Mode,
193181
194182 const Special = enum {
195183 None,
......@@ -440,12 +428,7 @@ pub const CompareOutputContext = struct {
440428 self.step.dependOn(&run_and_cmp_output.step);
441429 },
442430 Special.None => {
443 for ([]Mode{
444 Mode.Debug,
445 Mode.ReleaseSafe,
446 Mode.ReleaseFast,
447 Mode.ReleaseSmall,
448 }) |mode| {
431 for (self.modes) |mode| {
449432 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", "compare-output", case.name, @tagName(mode)) catch unreachable;
450433 if (self.test_filter) |filter| {
451434 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
......@@ -500,6 +483,7 @@ pub const CompileErrorContext = struct {
500483 step: *build.Step,
501484 test_index: usize,
502485 test_filter: ?[]const u8,
486 modes: []const Mode,
503487
504488 const TestCase = struct {
505489 name: []const u8,
......@@ -690,10 +674,7 @@ pub const CompileErrorContext = struct {
690674 pub fn addCase(self: *CompileErrorContext, case: *const TestCase) void {
691675 const b = self.b;
692676
693 for ([]Mode{
694 Mode.Debug,
695 Mode.ReleaseFast,
696 }) |mode| {
677 for (self.modes) |mode| {
697678 const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {} ({})", case.name, @tagName(mode)) catch unreachable;
698679 if (self.test_filter) |filter| {
699680 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;