authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-25 21:57:28-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-25 21:57:28-04:00
log7109035b78ee05302bbdaadc52013b430a030b69
treeae6d7202dc75f2c799f5fbcad72ccf8b02a954a4
parent6cf248ec0824c746fc796905144c8077ccab99cf
parent526338b00fbe1cac19f64832176af3bdf2108a56

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


91 files changed, 7310 insertions(+), 2405 deletions(-)

CMakeLists.txt+4
......@@ -463,11 +463,14 @@ set(ZIG_STD_FILES
463463 "empty.zig"
464464 "event.zig"
465465 "event/channel.zig"
466 "event/fs.zig"
466467 "event/future.zig"
467468 "event/group.zig"
468469 "event/lock.zig"
469470 "event/locked.zig"
470471 "event/loop.zig"
472 "event/rwlock.zig"
473 "event/rwlocked.zig"
471474 "event/tcp.zig"
472475 "fmt/errol/enum3.zig"
473476 "fmt/errol/index.zig"
......@@ -556,6 +559,7 @@ set(ZIG_STD_FILES
556559 "math/tanh.zig"
557560 "math/trunc.zig"
558561 "mem.zig"
562 "mutex.zig"
559563 "net.zig"
560564 "os/child_process.zig"
561565 "os/darwin.zig"
build.zig+1-1
......@@ -19,7 +19,7 @@ pub fn build(b: *Builder) !void {
1919 var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{
2020 docgen_exe.getOutputPath(),
2121 rel_zig_exe,
22 "doc/langref.html.in",
22 "doc" ++ os.path.sep_str ++ "langref.html.in",
2323 os.path.join(b.allocator, b.cache_root, "langref.html") catch unreachable,
2424 });
2525 docgen_cmd.step.dependOn(&docgen_exe.step);
cmake/Findllvm.cmake+1-1
......@@ -8,7 +8,7 @@
88# LLVM_LIBDIRS
99
1010find_program(LLVM_CONFIG_EXE
11 NAMES llvm-config-7.0 llvm-config
11 NAMES llvm-config llvm-config-7.0
1212 PATHS
1313 "/mingw64/bin"
1414 "/c/msys64/mingw64/bin"
doc/docgen.zig+5-5
......@@ -34,10 +34,10 @@ pub fn main() !void {
3434 const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg"));
3535 defer allocator.free(out_file_name);
3636
37 var in_file = try os.File.openRead(allocator, in_file_name);
37 var in_file = try os.File.openRead(in_file_name);
3838 defer in_file.close();
3939
40 var out_file = try os.File.openWrite(allocator, out_file_name);
40 var out_file = try os.File.openWrite(out_file_name);
4141 defer out_file.close();
4242
4343 var file_in_stream = io.FileInStream.init(&in_file);
......@@ -370,9 +370,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
370370 .n = header_stack_size,
371371 },
372372 });
373 if (try urls.put(urlized, tag_token)) |other_tag_token| {
373 if (try urls.put(urlized, tag_token)) |entry| {
374374 parseError(tokenizer, tag_token, "duplicate header url: #{}", urlized) catch {};
375 parseError(tokenizer, other_tag_token, "other tag here") catch {};
375 parseError(tokenizer, entry.value, "other tag here") catch {};
376376 return error.ParseError;
377377 }
378378 if (last_action == Action.Open) {
......@@ -738,7 +738,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
738738 try out.print("<pre><code class=\"zig\">{}</code></pre>", escaped_source);
739739 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
740740 const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);
741 try io.writeFile(allocator, tmp_source_file_name, trimmed_raw_source);
741 try io.writeFile(tmp_source_file_name, trimmed_raw_source);
742742
743743 switch (code.id) {
744744 Code.Id.Exe => |expected_outcome| {
doc/langref.html.in+151-75
......@@ -247,66 +247,6 @@ pub fn main() void {
247247 Description
248248 </th>
249249 </tr>
250 <tr>
251 <td><code>i2</code></td>
252 <td><code>(none)</code></td>
253 <td>signed 2-bit integer</td>
254 </tr>
255 <tr>
256 <td><code>u2</code></td>
257 <td><code>(none)</code></td>
258 <td>unsigned 2-bit integer</td>
259 </tr>
260 <tr>
261 <td><code>i3</code></td>
262 <td><code>(none)</code></td>
263 <td>signed 3-bit integer</td>
264 </tr>
265 <tr>
266 <td><code>u3</code></td>
267 <td><code>(none)</code></td>
268 <td>unsigned 3-bit integer</td>
269 </tr>
270 <tr>
271 <td><code>i4</code></td>
272 <td><code>(none)</code></td>
273 <td>signed 4-bit integer</td>
274 </tr>
275 <tr>
276 <td><code>u4</code></td>
277 <td><code>(none)</code></td>
278 <td>unsigned 4-bit integer</td>
279 </tr>
280 <tr>
281 <td><code>i5</code></td>
282 <td><code>(none)</code></td>
283 <td>signed 5-bit integer</td>
284 </tr>
285 <tr>
286 <td><code>u5</code></td>
287 <td><code>(none)</code></td>
288 <td>unsigned 5-bit integer</td>
289 </tr>
290 <tr>
291 <td><code>i6</code></td>
292 <td><code>(none)</code></td>
293 <td>signed 6-bit integer</td>
294 </tr>
295 <tr>
296 <td><code>u6</code></td>
297 <td><code>(none)</code></td>
298 <td>unsigned 6-bit integer</td>
299 </tr>
300 <tr>
301 <td><code>i7</code></td>
302 <td><code>(none)</code></td>
303 <td>signed 7-bit integer</td>
304 </tr>
305 <tr>
306 <td><code>u7</code></td>
307 <td><code>(none)</code></td>
308 <td>unsigned 7-bit integer</td>
309 </tr>
310250 <tr>
311251 <td><code>i8</code></td>
312252 <td><code>int8_t</code></td>
......@@ -476,6 +416,11 @@ pub fn main() void {
476416 </tr>
477417 </table>
478418 </div>
419 <p>
420 In addition to the integer types above, arbitrary bit-width integers can be referenced by using
421 an identifier of <code>i</code> or </code>u</code> followed by digits. For example, the identifier
422 <code>i7</code> refers to a signed 7-bit integer.
423 </p>
479424 {#see_also|Integers|Floats|void|Errors#}
480425 {#header_close#}
481426 {#header_open|Primitive Values#}
......@@ -744,19 +689,19 @@ const yet_another_hex_float = 0x103.70P-5;
744689 {#code_end#}
745690 {#header_close#}
746691 {#header_open|Floating Point Operations#}
747 <p>By default floating point operations use <code>Optimized</code> mode,
748 but you can switch to <code>Strict</code> mode on a per-block basis:</p>
692 <p>By default floating point operations use <code>Strict</code> mode,
693 but you can switch to <code>Optimized</code> mode on a per-block basis:</p>
749694 {#code_begin|obj|foo#}
750695 {#code_release_fast#}
751696const builtin = @import("builtin");
752697const big = f64(1 << 40);
753698
754699export fn foo_strict(x: f64) f64 {
755 @setFloatMode(this, builtin.FloatMode.Strict);
756700 return x + big - big;
757701}
758702
759703export fn foo_optimized(x: f64) f64 {
704 @setFloatMode(this, builtin.FloatMode.Optimized);
760705 return x + big - big;
761706}
762707 {#code_end#}
......@@ -809,6 +754,8 @@ a += b</code></pre></td>
809754 <td>Addition.
810755 <ul>
811756 <li>Can cause {#link|overflow|Default Operations#} for integers.</li>
757 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
758 <li>See also {#link|@addWithOverflow#}.</li>
812759 </ul>
813760 </td>
814761 <td>
......@@ -826,6 +773,8 @@ a +%= b</code></pre></td>
826773 <td>Wrapping Addition.
827774 <ul>
828775 <li>Guaranteed to have twos-complement wrapping behavior.</li>
776 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
777 <li>See also {#link|@addWithOverflow#}.</li>
829778 </ul>
830779 </td>
831780 <td>
......@@ -844,6 +793,8 @@ a -= b</code></pre></td>
844793 <td>Subtraction.
845794 <ul>
846795 <li>Can cause {#link|overflow|Default Operations#} for integers.</li>
796 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
797 <li>See also {#link|@subWithOverflow#}.</li>
847798 </ul>
848799 </td>
849800 <td>
......@@ -861,6 +812,8 @@ a -%= b</code></pre></td>
861812 <td>Wrapping Subtraction.
862813 <ul>
863814 <li>Guaranteed to have twos-complement wrapping behavior.</li>
815 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
816 <li>See also {#link|@subWithOverflow#}.</li>
864817 </ul>
865818 </td>
866819 <td>
......@@ -914,6 +867,8 @@ a *= b</code></pre></td>
914867 <td>Multiplication.
915868 <ul>
916869 <li>Can cause {#link|overflow|Default Operations#} for integers.</li>
870 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
871 <li>See also {#link|@mulWithOverflow#}.</li>
917872 </ul>
918873 </td>
919874 <td>
......@@ -931,6 +886,8 @@ a *%= b</code></pre></td>
931886 <td>Wrapping Multiplication.
932887 <ul>
933888 <li>Guaranteed to have twos-complement wrapping behavior.</li>
889 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
890 <li>See also {#link|@mulWithOverflow#}.</li>
934891 </ul>
935892 </td>
936893 <td>
......@@ -956,6 +913,7 @@ a /= b</code></pre></td>
956913 {#link|@divFloor#}, or
957914 {#link|@divExact#} instead of <code>/</code>.
958915 </li>
916 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
959917 </ul>
960918 </td>
961919 <td>
......@@ -979,6 +937,7 @@ a %= b</code></pre></td>
979937 {#link|@rem#} or
980938 {#link|@mod#} instead of <code>%</code>.
981939 </li>
940 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
982941 </ul>
983942 </td>
984943 <td>
......@@ -995,6 +954,7 @@ a &lt;&lt;= b</code></pre></td>
995954 </td>
996955 <td>Bit Shift Left.
997956 <ul>
957 <li><code>b</code> must be {#link|comptime-known|comptime#} or have a type with log2 number of bits as <code>a</code>.</li>
998958 <li>See also {#link|@shlExact#}.</li>
999959 <li>See also {#link|@shlWithOverflow#}.</li>
1000960 </ul>
......@@ -1013,6 +973,7 @@ a &gt;&gt;= b</code></pre></td>
1013973 </td>
1014974 <td>Bit Shift Right.
1015975 <ul>
976 <li><code>b</code> must be {#link|comptime-known|comptime#} or have a type with log2 number of bits as <code>a</code>.</li>
1016977 <li>See also {#link|@shrExact#}.</li>
1017978 </ul>
1018979 </td>
......@@ -1029,6 +990,9 @@ a &amp;= b</code></pre></td>
1029990 </ul>
1030991 </td>
1031992 <td>Bitwise AND.
993 <ul>
994 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
995 </ul>
1032996 </td>
1033997 <td>
1034998 <pre><code class="zig">0b011 &amp; 0b101 == 0b001</code></pre>
......@@ -1043,6 +1007,9 @@ a |= b</code></pre></td>
10431007 </ul>
10441008 </td>
10451009 <td>Bitwise OR.
1010 <ul>
1011 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
1012 </ul>
10461013 </td>
10471014 <td>
10481015 <pre><code class="zig">0b010 | 0b100 == 0b110</code></pre>
......@@ -1057,6 +1024,9 @@ a ^= b</code></pre></td>
10571024 </ul>
10581025 </td>
10591026 <td>Bitwise XOR.
1027 <ul>
1028 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
1029 </ul>
10601030 </td>
10611031 <td>
10621032 <pre><code class="zig">0b011 ^ 0b101 == 0b110</code></pre>
......@@ -1186,6 +1156,7 @@ unwrapped == 1234</code></pre>
11861156 </td>
11871157 <td>
11881158 Returns <code>true</code> if a and b are equal, otherwise returns <code>false</code>.
1159 Invokes {#link|Peer Type Resolution#} for the operands.
11891160 </td>
11901161 <td>
11911162 <pre><code class="zig">(1 == 1) == true</code></pre>
......@@ -1218,6 +1189,7 @@ value == null</code></pre>
12181189 </td>
12191190 <td>
12201191 Returns <code>false</code> if a and b are equal, otherwise returns <code>true</code>.
1192 Invokes {#link|Peer Type Resolution#} for the operands.
12211193 </td>
12221194 <td>
12231195 <pre><code class="zig">(1 != 1) == false</code></pre>
......@@ -1233,6 +1205,7 @@ value == null</code></pre>
12331205 </td>
12341206 <td>
12351207 Returns <code>true</code> if a is greater than b, otherwise returns <code>false</code>.
1208 Invokes {#link|Peer Type Resolution#} for the operands.
12361209 </td>
12371210 <td>
12381211 <pre><code class="zig">(2 &gt; 1) == true</code></pre>
......@@ -1248,6 +1221,7 @@ value == null</code></pre>
12481221 </td>
12491222 <td>
12501223 Returns <code>true</code> if a is greater than or equal to b, otherwise returns <code>false</code>.
1224 Invokes {#link|Peer Type Resolution#} for the operands.
12511225 </td>
12521226 <td>
12531227 <pre><code class="zig">(2 &gt;= 1) == true</code></pre>
......@@ -1263,6 +1237,7 @@ value == null</code></pre>
12631237 </td>
12641238 <td>
12651239 Returns <code>true</code> if a is less than b, otherwise returns <code>false</code>.
1240 Invokes {#link|Peer Type Resolution#} for the operands.
12661241 </td>
12671242 <td>
12681243 <pre><code class="zig">(1 &lt; 2) == true</code></pre>
......@@ -1278,6 +1253,7 @@ value == null</code></pre>
12781253 </td>
12791254 <td>
12801255 Returns <code>true</code> if a is less than or equal to b, otherwise returns <code>false</code>.
1256 Invokes {#link|Peer Type Resolution#} for the operands.
12811257 </td>
12821258 <td>
12831259 <pre><code class="zig">(1 &lt;= 2) == true</code></pre>
......@@ -3807,6 +3783,7 @@ test "float widening" {
38073783 <p>TODO: [N]T to ?[]const T</p>
38083784 <p>TODO: *[N]T to []T</p>
38093785 <p>TODO: *[N]T to [*]T</p>
3786 <p>TODO: *[N]T to ?[*]T</p>
38103787 <p>TODO: *T to *[1]T</p>
38113788 <p>TODO: [N]T to E![]const T</p>
38123789 {#header_close#}
......@@ -3877,7 +3854,106 @@ test "float widening" {
38773854 {#header_close#}
38783855
38793856 {#header_open|Peer Type Resolution#}
3880 <p>TODO</p>
3857 <p>Peer Type Resolution occurs in these places:</p>
3858 <ul>
3859 <li>{#link|switch#} expressions</li>
3860 <li>{#link|if#} expressions</li>
3861 <li>{#link|while#} expressions</li>
3862 <li>{#link|for#} expressions</li>
3863 <li>Multiple break statements in a block</li>
3864 <li>Some {#link|binary operations|Table of Operators#}</li>
3865 </ul>
3866 <p>
3867 This kind of type resolution chooses a type that all peer types can implicitly cast into. Here are
3868 some examples:
3869 </p>
3870 {#code_begin|test#}
3871const std = @import("std");
3872const assert = std.debug.assert;
3873const mem = std.mem;
3874
3875test "peer resolve int widening" {
3876 var a: i8 = 12;
3877 var b: i16 = 34;
3878 var c = a + b;
3879 assert(c == 46);
3880 assert(@typeOf(c) == i16);
3881}
3882
3883test "peer resolve arrays of different size to const slice" {
3884 assert(mem.eql(u8, boolToStr(true), "true"));
3885 assert(mem.eql(u8, boolToStr(false), "false"));
3886 comptime assert(mem.eql(u8, boolToStr(true), "true"));
3887 comptime assert(mem.eql(u8, boolToStr(false), "false"));
3888}
3889fn boolToStr(b: bool) []const u8 {
3890 return if (b) "true" else "false";
3891}
3892
3893test "peer resolve array and const slice" {
3894 testPeerResolveArrayConstSlice(true);
3895 comptime testPeerResolveArrayConstSlice(true);
3896}
3897fn testPeerResolveArrayConstSlice(b: bool) void {
3898 const value1 = if (b) "aoeu" else ([]const u8)("zz");
3899 const value2 = if (b) ([]const u8)("zz") else "aoeu";
3900 assert(mem.eql(u8, value1, "aoeu"));
3901 assert(mem.eql(u8, value2, "zz"));
3902}
3903
3904test "peer type resolution: ?T and T" {
3905 assert(peerTypeTAndOptionalT(true, false).? == 0);
3906 assert(peerTypeTAndOptionalT(false, false).? == 3);
3907 comptime {
3908 assert(peerTypeTAndOptionalT(true, false).? == 0);
3909 assert(peerTypeTAndOptionalT(false, false).? == 3);
3910 }
3911}
3912fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize {
3913 if (c) {
3914 return if (b) null else usize(0);
3915 }
3916
3917 return usize(3);
3918}
3919
3920test "peer type resolution: [0]u8 and []const u8" {
3921 assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
3922 assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
3923 comptime {
3924 assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
3925 assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
3926 }
3927}
3928fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
3929 if (a) {
3930 return []const u8{};
3931 }
3932
3933 return slice[0..1];
3934}
3935test "peer type resolution: [0]u8, []const u8, and error![]u8" {
3936 {
3937 var data = "hi";
3938 const slice = data[0..];
3939 assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0);
3940 assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
3941 }
3942 comptime {
3943 var data = "hi";
3944 const slice = data[0..];
3945 assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0);
3946 assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
3947 }
3948}
3949fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) error![]u8 {
3950 if (a) {
3951 return []u8{};
3952 }
3953
3954 return slice[0..1];
3955}
3956 {#code_end#}
38813957 {#header_close#}
38823958 {#header_close#}
38833959
......@@ -4705,10 +4781,7 @@ async fn testSuspendBlock() void {
47054781 <p>
47064782 {#link|Await#} counts as a suspend point.
47074783 </p>
4708 {#header_open|Breaking from Suspend Blocks#}
4709 <p>
4710 Suspend blocks support labeled break, just like {#link|while#} and {#link|for#}.
4711 </p>
4784 {#header_open|Resuming from Suspend Blocks#}
47124785 <p>
47134786 Upon entering a <code>suspend</code> block, the coroutine is already considered
47144787 suspended, and can be resumed. For example, if you started another kernel thread,
......@@ -4741,6 +4814,9 @@ async fn testResumeFromSuspend(my_result: *i32) void {
47414814 my_result.* += 1;
47424815}
47434816 {#code_end#}
4817 <p>
4818 This is guaranteed to be a tail call, and therefore will not cause a new stack frame.
4819 </p>
47444820 {#header_close#}
47454821 {#header_close#}
47464822 {#header_open|Await#}
......@@ -5527,7 +5603,7 @@ fn add(a: i32, b: i32) i32 { return a + b; }
55275603 <p>Returns the field type of a struct or union.</p>
55285604 {#header_close#}
55295605 {#header_open|@memcpy#}
5530 <pre><code class="zig">@memcpy(noalias dest: *u8, noalias source: *const u8, byte_count: usize)</code></pre>
5606 <pre><code class="zig">@memcpy(noalias dest: [*]u8, noalias source: [*]const u8, byte_count: usize)</code></pre>
55315607 <p>
55325608 This function copies bytes from one region of memory to another. <code>dest</code> and
55335609 <code>source</code> are both pointers and must not overlap.
......@@ -5545,7 +5621,7 @@ fn add(a: i32, b: i32) i32 { return a + b; }
55455621mem.copy(u8, dest[0...byte_count], source[0...byte_count]);</code></pre>
55465622 {#header_close#}
55475623 {#header_open|@memset#}
5548 <pre><code class="zig">@memset(dest: *u8, c: u8, byte_count: usize)</code></pre>
5624 <pre><code class="zig">@memset(dest: [*]u8, c: u8, byte_count: usize)</code></pre>
55495625 <p>
55505626 This function sets a region of memory to <code>c</code>. <code>dest</code> is a pointer.
55515627 </p>
......@@ -5817,7 +5893,7 @@ pub const FloatMode = enum {
58175893 {#code_end#}
58185894 <ul>
58195895 <li>
5820 <code>Optimized</code> (default) - Floating point operations may do all of the following:
5896 <code>Optimized</code> - Floating point operations may do all of the following:
58215897 <ul>
58225898 <li>Assume the arguments and result are not NaN. Optimizations are required to retain defined behavior over NaNs, but the value of the result is undefined.</li>
58235899 <li>Assume the arguments and result are not +/-Inf. Optimizations are required to retain defined behavior over +/-Inf, but the value of the result is undefined.</li>
......@@ -5829,7 +5905,7 @@ pub const FloatMode = enum {
58295905 This is equivalent to <code>-ffast-math</code> in GCC.
58305906 </li>
58315907 <li>
5832 <code>Strict</code> - Floating point operations follow strict IEEE compliance.
5908 <code>Strict</code> (default) - Floating point operations follow strict IEEE compliance.
58335909 </li>
58345910 </ul>
58355911 {#see_also|Floating Point Operations#}
......@@ -6035,7 +6111,7 @@ pub const TypeInfo = union(TypeId) {
60356111 size: Size,
60366112 is_const: bool,
60376113 is_volatile: bool,
6038 alignment: u32,
6114 alignment: u29,
60396115 child: type,
60406116
60416117 pub const Size = enum {
......@@ -7543,8 +7619,8 @@ hljs.registerLanguage("zig", function(t) {
75437619 },
75447620 a = t.IR + "\\s*\\(",
75457621 c = {
7546 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse",
7547 built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz popCount import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum",
7622 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume suspend cancel await async orelse",
7623 built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz popCount import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum handle",
75487624 literal: "true false null undefined"
75497625 },
75507626 n = [e, t.CLCM, t.CBCM, s, r];
example/cat/main.zig+1-1
......@@ -20,7 +20,7 @@ pub fn main() !void {
2020 } else if (arg[0] == '-') {
2121 return usage(exe);
2222 } else {
23 var file = os.File.openRead(allocator, arg) catch |err| {
23 var file = os.File.openRead(arg) catch |err| {
2424 warn("Unable to open file: {}\n", @errorName(err));
2525 return err;
2626 };
example/shared_library/mathtest.zig+9
......@@ -1,3 +1,12 @@
1// TODO Remove this workaround
2comptime {
3 const builtin = @import("builtin");
4 if (builtin.os == builtin.Os.macosx) {
5 @export("__mh_execute_header", _mh_execute_header, builtin.GlobalLinkage.Weak);
6 }
7}
8var _mh_execute_header = extern struct {x: usize}{.x = 0};
9
110export fn add(a: i32, b: i32) i32 {
211 return a + b;
312}
src-self-hosted/codegen.zig+2-2
......@@ -19,8 +19,8 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
1919 var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
2020 errdefer output_path.deinit();
2121
22 const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();
23 defer llvm_handle.release(comp.event_loop_local);
22 const llvm_handle = try comp.zig_compiler.getAnyLlvmContext();
23 defer llvm_handle.release(comp.zig_compiler);
2424
2525 const context = llvm_handle.node.data;
2626
src-self-hosted/compilation.zig+347-183
......@@ -30,9 +30,12 @@ const Package = @import("package.zig").Package;
3030const link = @import("link.zig").link;
3131const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
3232const CInt = @import("c_int.zig").CInt;
33const fs = event.fs;
34
35const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
3336
3437/// Data that is local to the event loop.
35pub const EventLoopLocal = struct {
38pub const ZigCompiler = struct {
3639 loop: *event.Loop,
3740 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
3841 lld_lock: event.Lock,
......@@ -44,7 +47,7 @@ pub const EventLoopLocal = struct {
4447
4548 var lazy_init_targets = std.lazyInit(void);
4649
47 fn init(loop: *event.Loop) !EventLoopLocal {
50 fn init(loop: *event.Loop) !ZigCompiler {
4851 lazy_init_targets.get() orelse {
4952 Target.initializeAll();
5053 lazy_init_targets.resolve();
......@@ -54,7 +57,7 @@ pub const EventLoopLocal = struct {
5457 try std.os.getRandomBytes(seed_bytes[0..]);
5558 const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
5659
57 return EventLoopLocal{
60 return ZigCompiler{
5861 .loop = loop,
5962 .lld_lock = event.Lock.init(loop),
6063 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
......@@ -64,7 +67,7 @@ pub const EventLoopLocal = struct {
6467 }
6568
6669 /// Must be called only after EventLoop.run completes.
67 fn deinit(self: *EventLoopLocal) void {
70 fn deinit(self: *ZigCompiler) void {
6871 self.lld_lock.deinit();
6972 while (self.llvm_handle_pool.pop()) |node| {
7073 c.LLVMContextDispose(node.data);
......@@ -74,7 +77,7 @@ pub const EventLoopLocal = struct {
7477
7578 /// Gets an exclusive handle on any LlvmContext.
7679 /// Caller must release the handle when done.
77 pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle {
80 pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle {
7881 if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
7982
8083 const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;
......@@ -89,24 +92,36 @@ pub const EventLoopLocal = struct {
8992 return LlvmHandle{ .node = node };
9093 }
9194
92 pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation {
95 pub async fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
9396 if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr;
9497 try await (async self.native_libc.data.findNative(self.loop) catch unreachable);
9598 self.native_libc.resolve();
9699 return &self.native_libc.data;
97100 }
101
102 /// Must be called only once, ever. Sets global state.
103 pub fn setLlvmArgv(allocator: *Allocator, llvm_argv: []const []const u8) !void {
104 if (llvm_argv.len != 0) {
105 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(allocator, [][]const []const u8{
106 [][]const u8{"zig (LLVM option parsing)"},
107 llvm_argv,
108 });
109 defer c_compatible_args.deinit();
110 c.ZigLLVMParseCommandLineOptions(llvm_argv.len + 1, c_compatible_args.ptr);
111 }
112 }
98113};
99114
100115pub const LlvmHandle = struct {
101116 node: *std.atomic.Stack(llvm.ContextRef).Node,
102117
103 pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void {
104 event_loop_local.llvm_handle_pool.push(self.node);
118 pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void {
119 zig_compiler.llvm_handle_pool.push(self.node);
105120 }
106121};
107122
108123pub const Compilation = struct {
109 event_loop_local: *EventLoopLocal,
124 zig_compiler: *ZigCompiler,
110125 loop: *event.Loop,
111126 name: Buffer,
112127 llvm_triple: Buffer,
......@@ -134,7 +149,6 @@ pub const Compilation = struct {
134149 linker_rdynamic: bool,
135150
136151 clang_argv: []const []const u8,
137 llvm_argv: []const []const u8,
138152 lib_dirs: []const []const u8,
139153 rpath_list: []const []const u8,
140154 assembly_files: []const []const u8,
......@@ -214,6 +228,8 @@ pub const Compilation = struct {
214228 deinit_group: event.Group(void),
215229
216230 destroy_handle: promise,
231 main_loop_handle: promise,
232 main_loop_future: event.Future(void),
217233
218234 have_err_ret_tracing: bool,
219235
......@@ -227,6 +243,8 @@ pub const Compilation = struct {
227243
228244 c_int_types: [CInt.list.len]*Type.Int,
229245
246 fs_watch: *fs.Watch(*Scope.Root),
247
230248 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
231249 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
232250 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
......@@ -239,8 +257,6 @@ pub const Compilation = struct {
239257 pub const BuildError = error{
240258 OutOfMemory,
241259 EndOfStream,
242 BadFd,
243 Io,
244260 IsDir,
245261 Unexpected,
246262 SystemResources,
......@@ -255,7 +271,6 @@ pub const Compilation = struct {
255271 NameTooLong,
256272 SystemFdQuotaExceeded,
257273 NoDevice,
258 PathNotFound,
259274 NoSpaceLeft,
260275 NotDir,
261276 FileSystem,
......@@ -282,6 +297,9 @@ pub const Compilation = struct {
282297 LibCMissingDynamicLinker,
283298 InvalidDarwinVersionString,
284299 UnsupportedLinkArchitecture,
300 UserResourceLimitReached,
301 InvalidUtf8,
302 BadPathName,
285303 };
286304
287305 pub const Event = union(enum) {
......@@ -318,7 +336,7 @@ pub const Compilation = struct {
318336 };
319337
320338 pub fn create(
321 event_loop_local: *EventLoopLocal,
339 zig_compiler: *ZigCompiler,
322340 name: []const u8,
323341 root_src_path: ?[]const u8,
324342 target: Target,
......@@ -327,11 +345,45 @@ pub const Compilation = struct {
327345 is_static: bool,
328346 zig_lib_dir: []const u8,
329347 ) !*Compilation {
330 const loop = event_loop_local.loop;
331 const comp = try event_loop_local.loop.allocator.create(Compilation{
348 var optional_comp: ?*Compilation = null;
349 const handle = try async<zig_compiler.loop.allocator> createAsync(
350 &optional_comp,
351 zig_compiler,
352 name,
353 root_src_path,
354 target,
355 kind,
356 build_mode,
357 is_static,
358 zig_lib_dir,
359 );
360 return optional_comp orelse if (getAwaitResult(
361 zig_compiler.loop.allocator,
362 handle,
363 )) |_| unreachable else |err| err;
364 }
365
366 async fn createAsync(
367 out_comp: *?*Compilation,
368 zig_compiler: *ZigCompiler,
369 name: []const u8,
370 root_src_path: ?[]const u8,
371 target: Target,
372 kind: Kind,
373 build_mode: builtin.Mode,
374 is_static: bool,
375 zig_lib_dir: []const u8,
376 ) !void {
377 // workaround for https://github.com/ziglang/zig/issues/1194
378 suspend {
379 resume @handle();
380 }
381
382 const loop = zig_compiler.loop;
383 var comp = Compilation{
332384 .loop = loop,
333385 .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),
334 .event_loop_local = event_loop_local,
386 .zig_compiler = zig_compiler,
335387 .events = undefined,
336388 .root_src_path = root_src_path,
337389 .target = target,
......@@ -341,6 +393,9 @@ pub const Compilation = struct {
341393 .zig_lib_dir = zig_lib_dir,
342394 .zig_std_dir = undefined,
343395 .tmp_dir = event.Future(BuildError![]u8).init(loop),
396 .destroy_handle = @handle(),
397 .main_loop_handle = undefined,
398 .main_loop_future = event.Future(void).init(loop),
344399
345400 .name = undefined,
346401 .llvm_triple = undefined,
......@@ -365,7 +420,6 @@ pub const Compilation = struct {
365420 .is_static = is_static,
366421 .linker_rdynamic = false,
367422 .clang_argv = [][]const u8{},
368 .llvm_argv = [][]const u8{},
369423 .lib_dirs = [][]const u8{},
370424 .rpath_list = [][]const u8{},
371425 .assembly_files = [][]const u8{},
......@@ -412,25 +466,26 @@ pub const Compilation = struct {
412466 .std_package = undefined,
413467
414468 .override_libc = null,
415 .destroy_handle = undefined,
416469 .have_err_ret_tracing = false,
417470 .primitive_type_table = undefined,
418 });
419 errdefer {
471
472 .fs_watch = undefined,
473 };
474 comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
475 comp.primitive_type_table = TypeTable.init(comp.arena());
476
477 defer {
420478 comp.int_type_table.private_data.deinit();
421479 comp.array_type_table.private_data.deinit();
422480 comp.ptr_type_table.private_data.deinit();
423481 comp.fn_type_table.private_data.deinit();
424482 comp.arena_allocator.deinit();
425 comp.loop.allocator.destroy(comp);
426483 }
427484
428485 comp.name = try Buffer.init(comp.arena(), name);
429486 comp.llvm_triple = try target.getTriple(comp.arena());
430487 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
431 comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
432488 comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
433 comp.primitive_type_table = TypeTable.init(comp.arena());
434489
435490 const opt_level = switch (build_mode) {
436491 builtin.Mode.Debug => llvm.CodeGenLevelNone,
......@@ -444,8 +499,8 @@ pub const Compilation = struct {
444499 // As a workaround we do not use target native features on Windows.
445500 var target_specific_cpu_args: ?[*]u8 = null;
446501 var target_specific_cpu_features: ?[*]u8 = null;
447 errdefer llvm.DisposeMessage(target_specific_cpu_args);
448 errdefer llvm.DisposeMessage(target_specific_cpu_features);
502 defer llvm.DisposeMessage(target_specific_cpu_args);
503 defer llvm.DisposeMessage(target_specific_cpu_features);
449504 if (target == Target.Native and !target.isWindows()) {
450505 target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory;
451506 target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory;
......@@ -460,16 +515,16 @@ pub const Compilation = struct {
460515 reloc_mode,
461516 llvm.CodeModelDefault,
462517 ) orelse return error.OutOfMemory;
463 errdefer llvm.DisposeTargetMachine(comp.target_machine);
518 defer llvm.DisposeTargetMachine(comp.target_machine);
464519
465520 comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory;
466 errdefer llvm.DisposeTargetData(comp.target_data_ref);
521 defer llvm.DisposeTargetData(comp.target_data_ref);
467522
468523 comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory;
469 errdefer llvm.DisposeMessage(comp.target_layout_str);
524 defer llvm.DisposeMessage(comp.target_layout_str);
470525
471526 comp.events = try event.Channel(Event).create(comp.loop, 0);
472 errdefer comp.events.destroy();
527 defer comp.events.destroy();
473528
474529 if (root_src_path) |root_src| {
475530 const dirname = std.os.path.dirname(root_src) orelse ".";
......@@ -482,11 +537,27 @@ pub const Compilation = struct {
482537 comp.root_package = try Package.create(comp.arena(), ".", "");
483538 }
484539
540 comp.fs_watch = try fs.Watch(*Scope.Root).create(loop, 16);
541 defer comp.fs_watch.destroy();
542
485543 try comp.initTypes();
544 defer comp.primitive_type_table.deinit();
545
546 comp.main_loop_handle = async comp.mainLoop() catch unreachable;
547 // Set this to indicate that initialization completed successfully.
548 // from here on out we must not return an error.
549 // This must occur before the first suspend/await.
550 out_comp.* = &comp;
551 // This suspend is resumed by destroy()
552 suspend;
553 // From here on is cleanup.
486554
487 comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
555 await (async comp.deinit_group.wait() catch unreachable);
488556
489 return comp;
557 if (comp.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
558 // TODO evented I/O?
559 os.deleteTree(comp.arena(), tmp_dir) catch {};
560 } else |_| {};
490561 }
491562
492563 /// it does ref the result because it could be an arbitrary integer size
......@@ -672,55 +743,28 @@ pub const Compilation = struct {
672743 assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
673744 }
674745
675 /// This function can safely use async/await, because it manages Compilation's lifetime,
676 /// and EventLoopLocal.deinit will not be called until the event.Loop.run() completes.
677 async fn internalDeinit(self: *Compilation) void {
678 suspend;
679
680 await (async self.deinit_group.wait() catch unreachable);
681 if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
682 // TODO evented I/O?
683 os.deleteTree(self.arena(), tmp_dir) catch {};
684 } else |_| {};
685
686 self.events.destroy();
687
688 llvm.DisposeMessage(self.target_layout_str);
689 llvm.DisposeTargetData(self.target_data_ref);
690 llvm.DisposeTargetMachine(self.target_machine);
691
692 self.primitive_type_table.deinit();
693
694 self.arena_allocator.deinit();
695 self.gpa().destroy(self);
696 }
697
698746 pub fn destroy(self: *Compilation) void {
747 cancel self.main_loop_handle;
699748 resume self.destroy_handle;
700749 }
701750
702 pub fn build(self: *Compilation) !void {
703 if (self.llvm_argv.len != 0) {
704 var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{
705 [][]const u8{"zig (LLVM option parsing)"},
706 self.llvm_argv,
707 });
708 defer c_compatible_args.deinit();
709 // TODO this sets global state
710 c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
711 }
712
713 _ = try async<self.gpa()> self.buildAsync();
751 fn start(self: *Compilation) void {
752 self.main_loop_future.resolve();
714753 }
715754
716 async fn buildAsync(self: *Compilation) void {
717 while (true) {
718 // TODO directly awaiting async should guarantee memory allocation elision
719 const build_result = await (async self.compileAndLink() catch unreachable);
755 async fn mainLoop(self: *Compilation) void {
756 // wait until start() is called
757 _ = await (async self.main_loop_future.get() catch unreachable);
720758
759 var build_result = await (async self.initialCompile() catch unreachable);
760
761 while (true) {
762 const link_result = if (build_result) blk: {
763 break :blk await (async self.maybeLink() catch unreachable);
764 } else |err| err;
721765 // this makes a handy error return trace and stack trace in debug mode
722766 if (std.debug.runtime_safety) {
723 build_result catch unreachable;
767 link_result catch unreachable;
724768 }
725769
726770 const compile_errors = blk: {
......@@ -729,7 +773,7 @@ pub const Compilation = struct {
729773 break :blk held.value.toOwnedSlice();
730774 };
731775
732 if (build_result) |_| {
776 if (link_result) |_| {
733777 if (compile_errors.len == 0) {
734778 await (async self.events.put(Event.Ok) catch unreachable);
735779 } else {
......@@ -742,105 +786,195 @@ pub const Compilation = struct {
742786 await (async self.events.put(Event{ .Error = err }) catch unreachable);
743787 }
744788
745 // for now we stop after 1
746 return;
789 // First, get an item from the watch channel, waiting on the channel.
790 var group = event.Group(BuildError!void).init(self.loop);
791 {
792 const ev = (await (async self.fs_watch.channel.get() catch unreachable)) catch |err| {
793 build_result = err;
794 continue;
795 };
796 const root_scope = ev.data;
797 group.call(rebuildFile, self, root_scope) catch |err| {
798 build_result = err;
799 continue;
800 };
801 }
802 // Next, get all the items from the channel that are buffered up.
803 while (await (async self.fs_watch.channel.getOrNull() catch unreachable)) |ev_or_err| {
804 if (ev_or_err) |ev| {
805 const root_scope = ev.data;
806 group.call(rebuildFile, self, root_scope) catch |err| {
807 build_result = err;
808 continue;
809 };
810 } else |err| {
811 build_result = err;
812 continue;
813 }
814 }
815 build_result = await (async group.wait() catch unreachable);
747816 }
748817 }
749818
750 async fn compileAndLink(self: *Compilation) !void {
751 if (self.root_src_path) |root_src_path| {
752 // TODO async/await os.path.real
753 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
754 try printError("unable to get real path '{}': {}", root_src_path, err);
755 return err;
819 async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void {
820 const tree_scope = blk: {
821 const source_code = (await (async fs.readFile(
822 self.loop,
823 root_scope.realpath,
824 max_src_size,
825 ) catch unreachable)) catch |err| {
826 try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err));
827 return;
756828 };
757 const root_scope = blk: {
758 errdefer self.gpa().free(root_src_real_path);
829 errdefer self.gpa().free(source_code);
759830
760 // TODO async/await readFileAlloc()
761 const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| {
762 try printError("unable to open '{}': {}", root_src_real_path, err);
763 return err;
764 };
765 errdefer self.gpa().free(source_code);
831 const tree = try self.gpa().createOne(ast.Tree);
832 tree.* = try std.zig.parse(self.gpa(), source_code);
833 errdefer {
834 tree.deinit();
835 self.gpa().destroy(tree);
836 }
766837
767 const tree = try self.gpa().createOne(ast.Tree);
768 tree.* = try std.zig.parse(self.gpa(), source_code);
769 errdefer {
770 tree.deinit();
771 self.gpa().destroy(tree);
772 }
838 break :blk try Scope.AstTree.create(self, tree, root_scope);
839 };
840 defer tree_scope.base.deref(self);
773841
774 break :blk try Scope.Root.create(self, tree, root_src_real_path);
775 };
776 defer root_scope.base.deref(self);
777 const tree = root_scope.tree;
842 var error_it = tree_scope.tree.errors.iterator(0);
843 while (error_it.next()) |parse_error| {
844 const msg = try Msg.createFromParseErrorAndScope(self, tree_scope, parse_error);
845 errdefer msg.destroy();
778846
779 var error_it = tree.errors.iterator(0);
780 while (error_it.next()) |parse_error| {
781 const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error);
782 errdefer msg.destroy();
847 try await (async self.addCompileErrorAsync(msg) catch unreachable);
848 }
849 if (tree_scope.tree.errors.len != 0) {
850 return;
851 }
783852
784 try await (async self.addCompileErrorAsync(msg) catch unreachable);
785 }
786 if (tree.errors.len != 0) {
787 return;
788 }
853 const locked_table = await (async root_scope.decls.table.acquireWrite() catch unreachable);
854 defer locked_table.release();
789855
790 const decls = try Scope.Decls.create(self, &root_scope.base);
791 defer decls.base.deref(self);
856 var decl_group = event.Group(BuildError!void).init(self.loop);
857 defer decl_group.deinit();
792858
793 var decl_group = event.Group(BuildError!void).init(self.loop);
794 var decl_group_consumed = false;
795 errdefer if (!decl_group_consumed) decl_group.cancelAll();
859 try await try async self.rebuildChangedDecls(
860 &decl_group,
861 locked_table.value,
862 root_scope.decls,
863 &tree_scope.tree.root_node.decls,
864 tree_scope,
865 );
796866
797 var it = tree.root_node.decls.iterator(0);
798 while (it.next()) |decl_ptr| {
799 const decl = decl_ptr.*;
800 switch (decl.id) {
801 ast.Node.Id.Comptime => {
802 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
867 try await (async decl_group.wait() catch unreachable);
868 }
803869
804 try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node);
805 },
806 ast.Node.Id.VarDecl => @panic("TODO"),
807 ast.Node.Id.FnProto => {
808 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
809
810 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
811 try self.addCompileError(root_scope, Span{
812 .first = fn_proto.fn_token,
813 .last = fn_proto.fn_token + 1,
814 }, "missing function name");
815 continue;
816 };
870 async fn rebuildChangedDecls(
871 self: *Compilation,
872 group: *event.Group(BuildError!void),
873 locked_table: *Decl.Table,
874 decl_scope: *Scope.Decls,
875 ast_decls: *ast.Node.Root.DeclList,
876 tree_scope: *Scope.AstTree,
877 ) !void {
878 var existing_decls = try locked_table.clone();
879 defer existing_decls.deinit();
880
881 var ast_it = ast_decls.iterator(0);
882 while (ast_it.next()) |decl_ptr| {
883 const decl = decl_ptr.*;
884 switch (decl.id) {
885 ast.Node.Id.Comptime => {
886 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
887
888 // TODO connect existing comptime decls to updated source files
889
890 try self.prelink_group.call(addCompTimeBlock, self, tree_scope, &decl_scope.base, comptime_node);
891 },
892 ast.Node.Id.VarDecl => @panic("TODO"),
893 ast.Node.Id.FnProto => {
894 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
895
896 const name = if (fn_proto.name_token) |name_token| tree_scope.tree.tokenSlice(name_token) else {
897 try self.addCompileError(tree_scope, Span{
898 .first = fn_proto.fn_token,
899 .last = fn_proto.fn_token + 1,
900 }, "missing function name");
901 continue;
902 };
817903
904 if (existing_decls.remove(name)) |entry| {
905 // compare new code to existing
906 if (entry.value.cast(Decl.Fn)) |existing_fn_decl| {
907 // Just compare the old bytes to the new bytes of the top level decl.
908 // Even if the AST is technically the same, we want error messages to display
909 // from the most recent source.
910 const old_decl_src = existing_fn_decl.base.tree_scope.tree.getNodeSource(
911 &existing_fn_decl.fn_proto.base,
912 );
913 const new_decl_src = tree_scope.tree.getNodeSource(&fn_proto.base);
914 if (mem.eql(u8, old_decl_src, new_decl_src)) {
915 // it's the same, we can skip this decl
916 continue;
917 } else {
918 @panic("TODO decl changed implementation");
919 // Add the new thing before dereferencing the old thing. This way we don't end
920 // up pointlessly re-creating things we end up using in the new thing.
921 }
922 } else {
923 @panic("TODO decl changed kind");
924 }
925 } else {
926 // add new decl
818927 const fn_decl = try self.gpa().create(Decl.Fn{
819928 .base = Decl{
820929 .id = Decl.Id.Fn,
821930 .name = name,
822 .visib = parseVisibToken(tree, fn_proto.visib_token),
931 .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
823932 .resolution = event.Future(BuildError!void).init(self.loop),
824 .parent_scope = &decls.base,
933 .parent_scope = &decl_scope.base,
934 .tree_scope = tree_scope,
825935 },
826936 .value = Decl.Fn.Val{ .Unresolved = {} },
827937 .fn_proto = fn_proto,
828938 });
939 tree_scope.base.ref();
829940 errdefer self.gpa().destroy(fn_decl);
830941
831 try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base);
832 },
833 ast.Node.Id.TestDecl => @panic("TODO"),
834 else => unreachable,
835 }
942 try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table);
943 }
944 },
945 ast.Node.Id.TestDecl => @panic("TODO"),
946 else => unreachable,
836947 }
837 decl_group_consumed = true;
838 try await (async decl_group.wait() catch unreachable);
948 }
949
950 var existing_decl_it = existing_decls.iterator();
951 while (existing_decl_it.next()) |entry| {
952 // this decl was deleted
953 const existing_decl = entry.value;
954 @panic("TODO handle decl deletion");
955 }
956 }
957
958 async fn initialCompile(self: *Compilation) !void {
959 if (self.root_src_path) |root_src_path| {
960 const root_scope = blk: {
961 // TODO async/await os.path.real
962 const root_src_real_path = os.path.realAlloc(self.gpa(), root_src_path) catch |err| {
963 try self.addCompileErrorCli(root_src_path, "unable to open: {}", @errorName(err));
964 return;
965 };
966 errdefer self.gpa().free(root_src_real_path);
967
968 break :blk try Scope.Root.create(self, root_src_real_path);
969 };
970 defer root_scope.base.deref(self);
839971
840 // Now other code can rely on the decls scope having a complete list of names.
841 decls.name_future.resolve();
972 assert((try await try async self.fs_watch.addFile(root_scope.realpath, root_scope)) == null);
973 try await try async self.rebuildFile(root_scope);
842974 }
975 }
843976
977 async fn maybeLink(self: *Compilation) !void {
844978 (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
845979 error.SemanticAnalysisFailed => {},
846980 else => return err,
......@@ -861,6 +995,7 @@ pub const Compilation = struct {
861995 /// caller takes ownership of resulting Code
862996 async fn genAndAnalyzeCode(
863997 comp: *Compilation,
998 tree_scope: *Scope.AstTree,
864999 scope: *Scope,
8651000 node: *ast.Node,
8661001 expected_type: ?*Type,
......@@ -868,6 +1003,7 @@ pub const Compilation = struct {
8681003 const unanalyzed_code = try await (async ir.gen(
8691004 comp,
8701005 node,
1006 tree_scope,
8711007 scope,
8721008 ) catch unreachable);
8731009 defer unanalyzed_code.destroy(comp.gpa());
......@@ -894,6 +1030,7 @@ pub const Compilation = struct {
8941030
8951031 async fn addCompTimeBlock(
8961032 comp: *Compilation,
1033 tree_scope: *Scope.AstTree,
8971034 scope: *Scope,
8981035 comptime_node: *ast.Node.Comptime,
8991036 ) !void {
......@@ -902,6 +1039,7 @@ pub const Compilation = struct {
9021039
9031040 const analyzed_code = (await (async genAndAnalyzeCode(
9041041 comp,
1042 tree_scope,
9051043 scope,
9061044 comptime_node.expr,
9071045 &void_type.base,
......@@ -914,38 +1052,42 @@ pub const Compilation = struct {
9141052 analyzed_code.destroy(comp.gpa());
9151053 }
9161054
917 async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
918 const tree = decl.findRootScope().tree;
919 const is_export = decl.isExported(tree);
920
921 var add_to_table_resolved = false;
922 const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable;
923 errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261
1055 async fn addTopLevelDecl(
1056 self: *Compilation,
1057 decl: *Decl,
1058 locked_table: *Decl.Table,
1059 ) !void {
1060 const is_export = decl.isExported(decl.tree_scope.tree);
9241061
9251062 if (is_export) {
9261063 try self.prelink_group.call(verifyUniqueSymbol, self, decl);
9271064 try self.prelink_group.call(resolveDecl, self, decl);
9281065 }
9291066
930 add_to_table_resolved = true;
931 try await add_to_table;
1067 const gop = try locked_table.getOrPut(decl.name);
1068 if (gop.found_existing) {
1069 try self.addCompileError(decl.tree_scope, decl.getSpan(), "redefinition of '{}'", decl.name);
1070 // TODO note: other definition here
1071 } else {
1072 gop.kv.value = decl;
1073 }
9321074 }
9331075
934 async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
935 const held = await (async decls.table.acquire() catch unreachable);
936 defer held.release();
1076 fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: ...) !void {
1077 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
1078 errdefer self.gpa().free(text);
9371079
938 if (try held.value.put(decl.name, decl)) |other_decl| {
939 try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);
940 // TODO note: other definition here
941 }
1080 const msg = try Msg.createFromScope(self, tree_scope, span, text);
1081 errdefer msg.destroy();
1082
1083 try self.prelink_group.call(addCompileErrorAsync, self, msg);
9421084 }
9431085
944 fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void {
1086 fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: ...) !void {
9451087 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
9461088 errdefer self.gpa().free(text);
9471089
948 const msg = try Msg.createFromScope(self, root, span, text);
1090 const msg = try Msg.createFromCli(self, realpath, text);
9491091 errdefer msg.destroy();
9501092
9511093 try self.prelink_group.call(addCompileErrorAsync, self, msg);
......@@ -969,7 +1111,7 @@ pub const Compilation = struct {
9691111
9701112 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
9711113 try self.addCompileError(
972 decl.findRootScope(),
1114 decl.tree_scope,
9731115 decl.getSpan(),
9741116 "exported symbol collision: '{}'",
9751117 decl.name,
......@@ -1019,7 +1161,7 @@ pub const Compilation = struct {
10191161 async fn startFindingNativeLibC(self: *Compilation) void {
10201162 await (async self.loop.yield() catch unreachable);
10211163 // we don't care if it fails, we're just trying to kick off the future resolution
1022 _ = (await (async self.event_loop_local.getNativeLibC() catch unreachable)) catch return;
1164 _ = (await (async self.zig_compiler.getNativeLibC() catch unreachable)) catch return;
10231165 }
10241166
10251167 /// General Purpose Allocator. Must free when done.
......@@ -1077,7 +1219,7 @@ pub const Compilation = struct {
10771219 var rand_bytes: [9]u8 = undefined;
10781220
10791221 {
1080 const held = await (async self.event_loop_local.prng.acquire() catch unreachable);
1222 const held = await (async self.zig_compiler.prng.acquire() catch unreachable);
10811223 defer held.release();
10821224
10831225 held.value.random.bytes(rand_bytes[0..]);
......@@ -1093,18 +1235,24 @@ pub const Compilation = struct {
10931235 }
10941236
10951237 /// Returns a value which has been ref()'d once
1096 async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value {
1097 const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable);
1238 async fn analyzeConstValue(
1239 comp: *Compilation,
1240 tree_scope: *Scope.AstTree,
1241 scope: *Scope,
1242 node: *ast.Node,
1243 expected_type: *Type,
1244 ) !*Value {
1245 const analyzed_code = try await (async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type) catch unreachable);
10981246 defer analyzed_code.destroy(comp.gpa());
10991247
11001248 return analyzed_code.getCompTimeResult(comp);
11011249 }
11021250
1103 async fn analyzeTypeExpr(comp: *Compilation, scope: *Scope, node: *ast.Node) !*Type {
1251 async fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
11041252 const meta_type = &Type.MetaType.get(comp).base;
11051253 defer meta_type.base.deref(comp);
11061254
1107 const result_val = try await (async comp.analyzeConstValue(scope, node, meta_type) catch unreachable);
1255 const result_val = try await (async comp.analyzeConstValue(tree_scope, scope, node, meta_type) catch unreachable);
11081256 errdefer result_val.base.deref(comp);
11091257
11101258 return result_val.cast(Type).?;
......@@ -1120,13 +1268,6 @@ pub const Compilation = struct {
11201268 }
11211269};
11221270
1123fn printError(comptime format: []const u8, args: ...) !void {
1124 var stderr_file = try std.io.getStdErr();
1125 var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
1126 const out_stream = &stderr_file_out_stream.stream;
1127 try out_stream.print(format, args);
1128}
1129
11301271fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
11311272 if (optional_token_index) |token_index| {
11321273 const token = tree.tokens.at(token_index);
......@@ -1150,12 +1291,14 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
11501291}
11511292
11521293async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1294 const tree_scope = fn_decl.base.tree_scope;
1295
11531296 const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);
11541297
11551298 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
11561299 defer fndef_scope.base.deref(comp);
11571300
1158 const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
1301 const fn_type = try await (async analyzeFnType(comp, tree_scope, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
11591302 defer fn_type.base.base.deref(comp);
11601303
11611304 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
......@@ -1168,18 +1311,17 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
11681311 symbol_name_consumed = true;
11691312
11701313 // Define local parameter variables
1171 const root_scope = fn_decl.base.findRootScope();
11721314 for (fn_type.key.data.Normal.params) |param, i| {
11731315 //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
11741316 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
11751317 const name_token = param_decl.name_token orelse {
1176 try comp.addCompileError(root_scope, Span{
1318 try comp.addCompileError(tree_scope, Span{
11771319 .first = param_decl.firstToken(),
11781320 .last = param_decl.type_node.firstToken(),
11791321 }, "missing parameter name");
11801322 return error.SemanticAnalysisFailed;
11811323 };
1182 const param_name = root_scope.tree.tokenSlice(name_token);
1324 const param_name = tree_scope.tree.tokenSlice(name_token);
11831325
11841326 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
11851327 // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
......@@ -1201,6 +1343,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
12011343 }
12021344
12031345 const analyzed_code = try await (async comp.genAndAnalyzeCode(
1346 tree_scope,
12041347 fn_val.child_scope,
12051348 body_node,
12061349 fn_type.key.data.Normal.return_type,
......@@ -1231,12 +1374,17 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {
12311374 return os.getAppDataDir(allocator, "zig");
12321375}
12331376
1234async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn {
1377async fn analyzeFnType(
1378 comp: *Compilation,
1379 tree_scope: *Scope.AstTree,
1380 scope: *Scope,
1381 fn_proto: *ast.Node.FnProto,
1382) !*Type.Fn {
12351383 const return_type_node = switch (fn_proto.return_type) {
12361384 ast.Node.FnProto.ReturnType.Explicit => |n| n,
12371385 ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
12381386 };
1239 const return_type = try await (async comp.analyzeTypeExpr(scope, return_type_node) catch unreachable);
1387 const return_type = try await (async comp.analyzeTypeExpr(tree_scope, scope, return_type_node) catch unreachable);
12401388 return_type.base.deref(comp);
12411389
12421390 var params = ArrayList(Type.Fn.Param).init(comp.gpa());
......@@ -1252,7 +1400,7 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
12521400 var it = fn_proto.params.iterator(0);
12531401 while (it.next()) |param_node_ptr| {
12541402 const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
1255 const param_type = try await (async comp.analyzeTypeExpr(scope, param_node.type_node) catch unreachable);
1403 const param_type = try await (async comp.analyzeTypeExpr(tree_scope, scope, param_node.type_node) catch unreachable);
12561404 errdefer param_type.base.deref(comp);
12571405 try params.append(Type.Fn.Param{
12581406 .typ = param_type,
......@@ -1289,7 +1437,12 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
12891437}
12901438
12911439async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1292 const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
1440 const fn_type = try await (async analyzeFnType(
1441 comp,
1442 fn_decl.base.tree_scope,
1443 fn_decl.base.parent_scope,
1444 fn_decl.fn_proto,
1445 ) catch unreachable);
12931446 defer fn_type.base.base.deref(comp);
12941447
12951448 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
......@@ -1301,3 +1454,14 @@ async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
13011454 fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
13021455 symbol_name_consumed = true;
13031456}
1457
1458// TODO these are hacks which should probably be solved by the language
1459fn getAwaitResult(allocator: *Allocator, handle: var) @typeInfo(@typeOf(handle)).Promise.child.? {
1460 var result: ?@typeInfo(@typeOf(handle)).Promise.child.? = null;
1461 cancel (async<allocator> getAwaitResultAsync(handle, &result) catch unreachable);
1462 return result.?;
1463}
1464
1465async fn getAwaitResultAsync(handle: var, out: *?@typeInfo(@typeOf(handle)).Promise.child.?) void {
1466 out.* = await handle;
1467}
src-self-hosted/decl.zig+8-1
......@@ -17,8 +17,16 @@ pub const Decl = struct {
1717 resolution: event.Future(Compilation.BuildError!void),
1818 parent_scope: *Scope,
1919
20 // TODO when we destroy the decl, deref the tree scope
21 tree_scope: *Scope.AstTree,
22
2023 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
2124
25 pub fn cast(base: *Decl, comptime T: type) ?*T {
26 if (base.id != @field(Id, @typeName(T))) return null;
27 return @fieldParentPtr(T, "base", base);
28 }
29
2230 pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
2331 switch (base.id) {
2432 Id.Fn => {
......@@ -95,4 +103,3 @@ pub const Decl = struct {
95103 base: Decl,
96104 };
97105};
98
src-self-hosted/errmsg.zig+87-40
......@@ -33,35 +33,48 @@ pub const Span = struct {
3333};
3434
3535pub const Msg = struct {
36 span: Span,
3736 text: []u8,
37 realpath: []u8,
3838 data: Data,
3939
4040 const Data = union(enum) {
41 Cli: Cli,
4142 PathAndTree: PathAndTree,
4243 ScopeAndComp: ScopeAndComp,
4344 };
4445
4546 const PathAndTree = struct {
46 realpath: []const u8,
47 span: Span,
4748 tree: *ast.Tree,
4849 allocator: *mem.Allocator,
4950 };
5051
5152 const ScopeAndComp = struct {
52 root_scope: *Scope.Root,
53 span: Span,
54 tree_scope: *Scope.AstTree,
5355 compilation: *Compilation,
5456 };
5557
58 const Cli = struct {
59 allocator: *mem.Allocator,
60 };
61
5662 pub fn destroy(self: *Msg) void {
5763 switch (self.data) {
64 Data.Cli => |cli| {
65 cli.allocator.free(self.text);
66 cli.allocator.free(self.realpath);
67 cli.allocator.destroy(self);
68 },
5869 Data.PathAndTree => |path_and_tree| {
5970 path_and_tree.allocator.free(self.text);
71 path_and_tree.allocator.free(self.realpath);
6072 path_and_tree.allocator.destroy(self);
6173 },
6274 Data.ScopeAndComp => |scope_and_comp| {
63 scope_and_comp.root_scope.base.deref(scope_and_comp.compilation);
75 scope_and_comp.tree_scope.base.deref(scope_and_comp.compilation);
6476 scope_and_comp.compilation.gpa().free(self.text);
77 scope_and_comp.compilation.gpa().free(self.realpath);
6578 scope_and_comp.compilation.gpa().destroy(self);
6679 },
6780 }
......@@ -69,6 +82,7 @@ pub const Msg = struct {
6982
7083 fn getAllocator(self: *const Msg) *mem.Allocator {
7184 switch (self.data) {
85 Data.Cli => |cli| return cli.allocator,
7286 Data.PathAndTree => |path_and_tree| {
7387 return path_and_tree.allocator;
7488 },
......@@ -78,71 +92,93 @@ pub const Msg = struct {
7892 }
7993 }
8094
81 pub fn getRealPath(self: *const Msg) []const u8 {
82 switch (self.data) {
83 Data.PathAndTree => |path_and_tree| {
84 return path_and_tree.realpath;
85 },
86 Data.ScopeAndComp => |scope_and_comp| {
87 return scope_and_comp.root_scope.realpath;
88 },
89 }
90 }
91
9295 pub fn getTree(self: *const Msg) *ast.Tree {
9396 switch (self.data) {
97 Data.Cli => unreachable,
9498 Data.PathAndTree => |path_and_tree| {
9599 return path_and_tree.tree;
96100 },
97101 Data.ScopeAndComp => |scope_and_comp| {
98 return scope_and_comp.root_scope.tree;
102 return scope_and_comp.tree_scope.tree;
99103 },
100104 }
101105 }
102106
107 pub fn getSpan(self: *const Msg) Span {
108 return switch (self.data) {
109 Data.Cli => unreachable,
110 Data.PathAndTree => |path_and_tree| path_and_tree.span,
111 Data.ScopeAndComp => |scope_and_comp| scope_and_comp.span,
112 };
113 }
114
103115 /// Takes ownership of text
104 /// References root_scope, and derefs when the msg is freed
105 pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {
116 /// References tree_scope, and derefs when the msg is freed
117 pub fn createFromScope(comp: *Compilation, tree_scope: *Scope.AstTree, span: Span, text: []u8) !*Msg {
118 const realpath = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
119 errdefer comp.gpa().free(realpath);
120
106121 const msg = try comp.gpa().create(Msg{
107122 .text = text,
108 .span = span,
123 .realpath = realpath,
109124 .data = Data{
110125 .ScopeAndComp = ScopeAndComp{
111 .root_scope = root_scope,
126 .tree_scope = tree_scope,
112127 .compilation = comp,
128 .span = span,
113129 },
114130 },
115131 });
116 root_scope.base.ref();
132 tree_scope.base.ref();
133 return msg;
134 }
135
136 /// Caller owns returned Msg and must free with `allocator`
137 /// allocator will additionally be used for printing messages later.
138 pub fn createFromCli(comp: *Compilation, realpath: []const u8, text: []u8) !*Msg {
139 const realpath_copy = try mem.dupe(comp.gpa(), u8, realpath);
140 errdefer comp.gpa().free(realpath_copy);
141
142 const msg = try comp.gpa().create(Msg{
143 .text = text,
144 .realpath = realpath_copy,
145 .data = Data{
146 .Cli = Cli{ .allocator = comp.gpa() },
147 },
148 });
117149 return msg;
118150 }
119151
120152 pub fn createFromParseErrorAndScope(
121153 comp: *Compilation,
122 root_scope: *Scope.Root,
154 tree_scope: *Scope.AstTree,
123155 parse_error: *const ast.Error,
124156 ) !*Msg {
125157 const loc_token = parse_error.loc();
126158 var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
127159 defer text_buf.deinit();
128160
161 const realpath_copy = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
162 errdefer comp.gpa().free(realpath_copy);
163
129164 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
130 try parse_error.render(&root_scope.tree.tokens, out_stream);
165 try parse_error.render(&tree_scope.tree.tokens, out_stream);
131166
132167 const msg = try comp.gpa().create(Msg{
133168 .text = undefined,
134 .span = Span{
135 .first = loc_token,
136 .last = loc_token,
137 },
169 .realpath = realpath_copy,
138170 .data = Data{
139171 .ScopeAndComp = ScopeAndComp{
140 .root_scope = root_scope,
172 .tree_scope = tree_scope,
141173 .compilation = comp,
174 .span = Span{
175 .first = loc_token,
176 .last = loc_token,
177 },
142178 },
143179 },
144180 });
145 root_scope.base.ref();
181 tree_scope.base.ref();
146182 msg.text = text_buf.toOwnedSlice();
147183 return msg;
148184 }
......@@ -161,22 +197,25 @@ pub const Msg = struct {
161197 var text_buf = try std.Buffer.initSize(allocator, 0);
162198 defer text_buf.deinit();
163199
200 const realpath_copy = try mem.dupe(allocator, u8, realpath);
201 errdefer allocator.free(realpath_copy);
202
164203 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
165204 try parse_error.render(&tree.tokens, out_stream);
166205
167206 const msg = try allocator.create(Msg{
168207 .text = undefined,
208 .realpath = realpath_copy,
169209 .data = Data{
170210 .PathAndTree = PathAndTree{
171211 .allocator = allocator,
172 .realpath = realpath,
173212 .tree = tree,
213 .span = Span{
214 .first = loc_token,
215 .last = loc_token,
216 },
174217 },
175218 },
176 .span = Span{
177 .first = loc_token,
178 .last = loc_token,
179 },
180219 });
181220 msg.text = text_buf.toOwnedSlice();
182221 errdefer allocator.destroy(msg);
......@@ -185,20 +224,28 @@ pub const Msg = struct {
185224 }
186225
187226 pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
227 switch (msg.data) {
228 Data.Cli => {
229 try stream.print("{}:-:-: error: {}\n", msg.realpath, msg.text);
230 return;
231 },
232 else => {},
233 }
234
188235 const allocator = msg.getAllocator();
189 const realpath = msg.getRealPath();
190236 const tree = msg.getTree();
191237
192 const cwd = try os.getCwd(allocator);
238 const cwd = try os.getCwdAlloc(allocator);
193239 defer allocator.free(cwd);
194240
195 const relpath = try os.path.relative(allocator, cwd, realpath);
241 const relpath = try os.path.relative(allocator, cwd, msg.realpath);
196242 defer allocator.free(relpath);
197243
198 const path = if (relpath.len < realpath.len) relpath else realpath;
244 const path = if (relpath.len < msg.realpath.len) relpath else msg.realpath;
245 const span = msg.getSpan();
199246
200 const first_token = tree.tokens.at(msg.span.first);
201 const last_token = tree.tokens.at(msg.span.last);
247 const first_token = tree.tokens.at(span.first);
248 const last_token = tree.tokens.at(span.last);
202249 const start_loc = tree.tokenLocationPtr(0, first_token);
203250 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
204251 if (!color_on) {
src-self-hosted/introspect.zig+2-2
......@@ -14,7 +14,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![
1414 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
1515 defer allocator.free(test_index_file);
1616
17 var file = try os.File.openRead(allocator, test_index_file);
17 var file = try os.File.openRead(test_index_file);
1818 file.close();
1919
2020 return test_zig_dir;
......@@ -22,7 +22,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![
2222
2323/// Caller must free result
2424pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {
25 const self_exe_path = try os.selfExeDirPath(allocator);
25 const self_exe_path = try os.selfExeDirPathAlloc(allocator);
2626 defer allocator.free(self_exe_path);
2727
2828 var cur_path: []const u8 = self_exe_path;
src-self-hosted/ir.zig+26-22
......@@ -961,6 +961,7 @@ pub const Code = struct {
961961 basic_block_list: std.ArrayList(*BasicBlock),
962962 arena: std.heap.ArenaAllocator,
963963 return_type: ?*Type,
964 tree_scope: *Scope.AstTree,
964965
965966 /// allocator is comp.gpa()
966967 pub fn destroy(self: *Code, allocator: *Allocator) void {
......@@ -990,14 +991,14 @@ pub const Code = struct {
990991 return ret_value.val.KnownValue.getRef();
991992 }
992993 try comp.addCompileError(
993 ret_value.scope.findRoot(),
994 self.tree_scope,
994995 ret_value.span,
995996 "unable to evaluate constant expression",
996997 );
997998 return error.SemanticAnalysisFailed;
998999 } else if (inst.hasSideEffects()) {
9991000 try comp.addCompileError(
1000 inst.scope.findRoot(),
1001 self.tree_scope,
10011002 inst.span,
10021003 "unable to evaluate constant expression",
10031004 );
......@@ -1013,25 +1014,24 @@ pub const Builder = struct {
10131014 code: *Code,
10141015 current_basic_block: *BasicBlock,
10151016 next_debug_id: usize,
1016 root_scope: *Scope.Root,
10171017 is_comptime: bool,
10181018 is_async: bool,
10191019 begin_scope: ?*Scope,
10201020
10211021 pub const Error = Analyze.Error;
10221022
1023 pub fn init(comp: *Compilation, root_scope: *Scope.Root, begin_scope: ?*Scope) !Builder {
1023 pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, begin_scope: ?*Scope) !Builder {
10241024 const code = try comp.gpa().create(Code{
10251025 .basic_block_list = undefined,
10261026 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
10271027 .return_type = null,
1028 .tree_scope = tree_scope,
10281029 });
10291030 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
10301031 errdefer code.destroy(comp.gpa());
10311032
10321033 return Builder{
10331034 .comp = comp,
1034 .root_scope = root_scope,
10351035 .current_basic_block = undefined,
10361036 .code = code,
10371037 .next_debug_id = 0,
......@@ -1292,6 +1292,7 @@ pub const Builder = struct {
12921292 Scope.Id.FnDef => return false,
12931293 Scope.Id.Decls => unreachable,
12941294 Scope.Id.Root => unreachable,
1295 Scope.Id.AstTree => unreachable,
12951296 Scope.Id.Block,
12961297 Scope.Id.Defer,
12971298 Scope.Id.DeferExpr,
......@@ -1302,7 +1303,7 @@ pub const Builder = struct {
13021303 }
13031304
13041305 pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {
1305 const int_token = irb.root_scope.tree.tokenSlice(int_lit.token);
1306 const int_token = irb.code.tree_scope.tree.tokenSlice(int_lit.token);
13061307
13071308 var base: u8 = undefined;
13081309 var rest: []const u8 = undefined;
......@@ -1341,7 +1342,7 @@ pub const Builder = struct {
13411342 }
13421343
13431344 pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
1344 const str_token = irb.root_scope.tree.tokenSlice(str_lit.token);
1345 const str_token = irb.code.tree_scope.tree.tokenSlice(str_lit.token);
13451346 const src_span = Span.token(str_lit.token);
13461347
13471348 var bad_index: usize = undefined;
......@@ -1349,7 +1350,7 @@ pub const Builder = struct {
13491350 error.OutOfMemory => return error.OutOfMemory,
13501351 error.InvalidCharacter => {
13511352 try irb.comp.addCompileError(
1352 irb.root_scope,
1353 irb.code.tree_scope,
13531354 src_span,
13541355 "invalid character in string literal: '{c}'",
13551356 str_token[bad_index],
......@@ -1427,7 +1428,7 @@ pub const Builder = struct {
14271428
14281429 if (statement_node.cast(ast.Node.Defer)) |defer_node| {
14291430 // defer starts a new scope
1430 const defer_token = irb.root_scope.tree.tokens.at(defer_node.defer_token);
1431 const defer_token = irb.code.tree_scope.tree.tokens.at(defer_node.defer_token);
14311432 const kind = switch (defer_token.id) {
14321433 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
14331434 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
......@@ -1513,7 +1514,7 @@ pub const Builder = struct {
15131514 const src_span = Span.token(control_flow_expr.ltoken);
15141515 if (scope.findFnDef() == null) {
15151516 try irb.comp.addCompileError(
1516 irb.root_scope,
1517 irb.code.tree_scope,
15171518 src_span,
15181519 "return expression outside function definition",
15191520 );
......@@ -1523,7 +1524,7 @@ pub const Builder = struct {
15231524 if (scope.findDeferExpr()) |scope_defer_expr| {
15241525 if (!scope_defer_expr.reported_err) {
15251526 try irb.comp.addCompileError(
1526 irb.root_scope,
1527 irb.code.tree_scope,
15271528 src_span,
15281529 "cannot return from defer expression",
15291530 );
......@@ -1599,7 +1600,7 @@ pub const Builder = struct {
15991600
16001601 pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
16011602 const src_span = Span.token(identifier.token);
1602 const name = irb.root_scope.tree.tokenSlice(identifier.token);
1603 const name = irb.code.tree_scope.tree.tokenSlice(identifier.token);
16031604
16041605 //if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
16051606 // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
......@@ -1622,7 +1623,7 @@ pub const Builder = struct {
16221623 }
16231624 } else |err| switch (err) {
16241625 error.Overflow => {
1625 try irb.comp.addCompileError(irb.root_scope, src_span, "integer too large");
1626 try irb.comp.addCompileError(irb.code.tree_scope, src_span, "integer too large");
16261627 return error.SemanticAnalysisFailed;
16271628 },
16281629 error.OutOfMemory => return error.OutOfMemory,
......@@ -1656,7 +1657,7 @@ pub const Builder = struct {
16561657 // TODO put a variable of same name with invalid type in global scope
16571658 // so that future references to this same name will find a variable with an invalid type
16581659
1659 try irb.comp.addCompileError(irb.root_scope, src_span, "unknown identifier '{}'", name);
1660 try irb.comp.addCompileError(irb.code.tree_scope, src_span, "unknown identifier '{}'", name);
16601661 return error.SemanticAnalysisFailed;
16611662 }
16621663
......@@ -1689,6 +1690,7 @@ pub const Builder = struct {
16891690 => scope = scope.parent orelse break,
16901691
16911692 Scope.Id.DeferExpr => unreachable,
1693 Scope.Id.AstTree => unreachable,
16921694 }
16931695 }
16941696 return result;
......@@ -1740,6 +1742,7 @@ pub const Builder = struct {
17401742 => scope = scope.parent orelse return is_noreturn,
17411743
17421744 Scope.Id.DeferExpr => unreachable,
1745 Scope.Id.AstTree => unreachable,
17431746 }
17441747 }
17451748 }
......@@ -1929,8 +1932,9 @@ pub const Builder = struct {
19291932 Scope.Id.Root => return Ident.NotFound,
19301933 Scope.Id.Decls => {
19311934 const decls = @fieldParentPtr(Scope.Decls, "base", s);
1932 const table = await (async decls.getTableReadOnly() catch unreachable);
1933 if (table.get(name)) |entry| {
1935 const locked_table = await (async decls.table.acquireRead() catch unreachable);
1936 defer locked_table.release();
1937 if (locked_table.value.get(name)) |entry| {
19341938 return Ident{ .Decl = entry.value };
19351939 }
19361940 },
......@@ -1967,8 +1971,8 @@ const Analyze = struct {
19671971 OutOfMemory,
19681972 };
19691973
1970 pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze {
1971 var irb = try Builder.init(comp, root_scope, null);
1974 pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, explicit_return_type: ?*Type) !Analyze {
1975 var irb = try Builder.init(comp, tree_scope, null);
19721976 errdefer irb.abort();
19731977
19741978 return Analyze{
......@@ -2046,7 +2050,7 @@ const Analyze = struct {
20462050 }
20472051
20482052 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {
2049 return self.irb.comp.addCompileError(self.irb.root_scope, span, fmt, args);
2053 return self.irb.comp.addCompileError(self.irb.code.tree_scope, span, fmt, args);
20502054 }
20512055
20522056 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {
......@@ -2534,9 +2538,10 @@ const Analyze = struct {
25342538pub async fn gen(
25352539 comp: *Compilation,
25362540 body_node: *ast.Node,
2541 tree_scope: *Scope.AstTree,
25372542 scope: *Scope,
25382543) !*Code {
2539 var irb = try Builder.init(comp, scope.findRoot(), scope);
2544 var irb = try Builder.init(comp, tree_scope, scope);
25402545 errdefer irb.abort();
25412546
25422547 const entry_block = try irb.createBasicBlock(scope, c"Entry");
......@@ -2554,9 +2559,8 @@ pub async fn gen(
25542559
25552560pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
25562561 const old_entry_bb = old_code.basic_block_list.at(0);
2557 const root_scope = old_entry_bb.scope.findRoot();
25582562
2559 var ira = try Analyze.init(comp, root_scope, expected_type);
2563 var ira = try Analyze.init(comp, old_code.tree_scope, expected_type);
25602564 errdefer ira.abort();
25612565
25622566 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);
src-self-hosted/libc_installation.zig+9-12
......@@ -143,7 +143,7 @@ pub const LibCInstallation = struct {
143143 pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
144144 self.initEmpty();
145145 var group = event.Group(FindError!void).init(loop);
146 errdefer group.cancelAll();
146 errdefer group.deinit();
147147 var windows_sdk: ?*c.ZigWindowsSDK = null;
148148 errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));
149149
......@@ -233,7 +233,7 @@ pub const LibCInstallation = struct {
233233 const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");
234234 defer loop.allocator.free(stdlib_path);
235235
236 if (try fileExists(loop.allocator, stdlib_path)) {
236 if (try fileExists(stdlib_path)) {
237237 self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path);
238238 return;
239239 }
......@@ -257,7 +257,7 @@ pub const LibCInstallation = struct {
257257 const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");
258258 defer loop.allocator.free(stdlib_path);
259259
260 if (try fileExists(loop.allocator, stdlib_path)) {
260 if (try fileExists(stdlib_path)) {
261261 self.include_dir = result_buf.toOwnedSlice();
262262 return;
263263 }
......@@ -285,7 +285,7 @@ pub const LibCInstallation = struct {
285285 }
286286 const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");
287287 defer loop.allocator.free(ucrt_lib_path);
288 if (try fileExists(loop.allocator, ucrt_lib_path)) {
288 if (try fileExists(ucrt_lib_path)) {
289289 self.lib_dir = result_buf.toOwnedSlice();
290290 return;
291291 }
......@@ -313,7 +313,7 @@ pub const LibCInstallation = struct {
313313 },
314314 };
315315 var group = event.Group(FindError!void).init(loop);
316 errdefer group.cancelAll();
316 errdefer group.deinit();
317317 for (dyn_tests) |*dyn_test| {
318318 try group.call(testNativeDynamicLinker, self, loop, dyn_test);
319319 }
......@@ -341,7 +341,6 @@ pub const LibCInstallation = struct {
341341 }
342342 }
343343
344
345344 async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
346345 var search_buf: [2]Search = undefined;
347346 const searches = fillSearch(&search_buf, sdk);
......@@ -361,7 +360,7 @@ pub const LibCInstallation = struct {
361360 }
362361 const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");
363362 defer loop.allocator.free(kernel32_path);
364 if (try fileExists(loop.allocator, kernel32_path)) {
363 if (try fileExists(kernel32_path)) {
365364 self.kernel32_lib_dir = result_buf.toOwnedSlice();
366365 return;
367366 }
......@@ -450,13 +449,11 @@ fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
450449 return search_buf[0..search_end];
451450}
452451
453
454fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {
455 if (std.os.File.access(allocator, path)) |_| {
452fn fileExists(path: []const u8) !bool {
453 if (std.os.File.access(path)) |_| {
456454 return true;
457455 } else |err| switch (err) {
458 error.NotFound, error.PermissionDenied => return false,
459 error.OutOfMemory => return error.OutOfMemory,
456 error.FileNotFound, error.PermissionDenied => return false,
460457 else => return error.FileSystem,
461458 }
462459}
src-self-hosted/link.zig+2-2
......@@ -61,7 +61,7 @@ pub async fn link(comp: *Compilation) !void {
6161 ctx.libc = ctx.comp.override_libc orelse blk: {
6262 switch (comp.target) {
6363 Target.Native => {
64 break :blk (await (async comp.event_loop_local.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
64 break :blk (await (async comp.zig_compiler.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
6565 },
6666 else => return error.LibCRequiredButNotProvidedOrFound,
6767 }
......@@ -83,7 +83,7 @@ pub async fn link(comp: *Compilation) !void {
8383
8484 {
8585 // LLD is not thread-safe, so we grab a global lock.
86 const held = await (async comp.event_loop_local.lld_lock.acquire() catch unreachable);
86 const held = await (async comp.zig_compiler.lld_lock.acquire() catch unreachable);
8787 defer held.release();
8888
8989 // Not evented I/O. LLD does its own multithreading internally.
src-self-hosted/main.zig+169-107
......@@ -14,7 +14,7 @@ const c = @import("c.zig");
1414const introspect = @import("introspect.zig");
1515const Args = arg.Args;
1616const Flag = arg.Flag;
17const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
17const ZigCompiler = @import("compilation.zig").ZigCompiler;
1818const Compilation = @import("compilation.zig").Compilation;
1919const Target = @import("target.zig").Target;
2020const errmsg = @import("errmsg.zig");
......@@ -24,6 +24,8 @@ var stderr_file: os.File = undefined;
2424var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
2525var stdout: *io.OutStream(io.FileOutStream.Error) = undefined;
2626
27const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
28
2729const usage =
2830 \\usage: zig [command] [options]
2931 \\
......@@ -371,6 +373,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
371373 os.exit(1);
372374 }
373375
376 var clang_argv_buf = ArrayList([]const u8).init(allocator);
377 defer clang_argv_buf.deinit();
378
379 const mllvm_flags = flags.many("mllvm");
380 for (mllvm_flags) |mllvm| {
381 try clang_argv_buf.append("-mllvm");
382 try clang_argv_buf.append(mllvm);
383 }
384 try ZigCompiler.setLlvmArgv(allocator, mllvm_flags);
385
374386 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
375387 defer allocator.free(zig_lib_dir);
376388
......@@ -380,11 +392,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
380392 try loop.initMultiThreaded(allocator);
381393 defer loop.deinit();
382394
383 var event_loop_local = try EventLoopLocal.init(&loop);
384 defer event_loop_local.deinit();
395 var zig_compiler = try ZigCompiler.init(&loop);
396 defer zig_compiler.deinit();
385397
386398 var comp = try Compilation.create(
387 &event_loop_local,
399 &zig_compiler,
388400 root_name,
389401 root_source_file,
390402 Target.Native,
......@@ -413,16 +425,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
413425 comp.linker_script = flags.single("linker-script");
414426 comp.each_lib_rpath = flags.present("each-lib-rpath");
415427
416 var clang_argv_buf = ArrayList([]const u8).init(allocator);
417 defer clang_argv_buf.deinit();
418
419 const mllvm_flags = flags.many("mllvm");
420 for (mllvm_flags) |mllvm| {
421 try clang_argv_buf.append("-mllvm");
422 try clang_argv_buf.append(mllvm);
423 }
424
425 comp.llvm_argv = mllvm_flags;
426428 comp.clang_argv = clang_argv_buf.toSliceConst();
427429
428430 comp.strip = flags.present("strip");
......@@ -465,30 +467,34 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
465467 comp.link_out_file = flags.single("output");
466468 comp.link_objects = link_objects;
467469
468 try comp.build();
470 comp.start();
469471 const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);
470472 defer cancel process_build_events_handle;
471473 loop.run();
472474}
473475
474476async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
475 // TODO directly awaiting async should guarantee memory allocation elision
476 const build_event = await (async comp.events.get() catch unreachable);
477
478 switch (build_event) {
479 Compilation.Event.Ok => {
480 return;
481 },
482 Compilation.Event.Error => |err| {
483 std.debug.warn("build failed: {}\n", @errorName(err));
484 os.exit(1);
485 },
486 Compilation.Event.Fail => |msgs| {
487 for (msgs) |msg| {
488 defer msg.destroy();
489 msg.printToFile(&stderr_file, color) catch os.exit(1);
490 }
491 },
477 var count: usize = 0;
478 while (true) {
479 // TODO directly awaiting async should guarantee memory allocation elision
480 const build_event = await (async comp.events.get() catch unreachable);
481 count += 1;
482
483 switch (build_event) {
484 Compilation.Event.Ok => {
485 stderr.print("Build {} succeeded\n", count) catch os.exit(1);
486 },
487 Compilation.Event.Error => |err| {
488 stderr.print("Build {} failed: {}\n", count, @errorName(err)) catch os.exit(1);
489 },
490 Compilation.Event.Fail => |msgs| {
491 stderr.print("Build {} compile errors:\n", count) catch os.exit(1);
492 for (msgs) |msg| {
493 defer msg.destroy();
494 msg.printToFile(&stderr_file, color) catch os.exit(1);
495 }
496 },
497 }
492498 }
493499}
494500
......@@ -528,33 +534,12 @@ const args_fmt_spec = []Flag{
528534};
529535
530536const Fmt = struct {
531 seen: std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8),
532 queue: std.LinkedList([]const u8),
537 seen: event.Locked(SeenMap),
533538 any_error: bool,
539 color: errmsg.Color,
540 loop: *event.Loop,
534541
535 // file_path must outlive Fmt
536 fn addToQueue(self: *Fmt, file_path: []const u8) !void {
537 const new_node = try self.seen.allocator.create(std.LinkedList([]const u8).Node{
538 .prev = undefined,
539 .next = undefined,
540 .data = file_path,
541 });
542
543 if (try self.seen.put(file_path, {})) |_| return;
544
545 self.queue.append(new_node);
546 }
547
548 fn addDirToQueue(self: *Fmt, file_path: []const u8) !void {
549 var dir = try std.os.Dir.open(self.seen.allocator, file_path);
550 defer dir.close();
551 while (try dir.next()) |entry| {
552 if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
553 const full_path = try os.path.join(self.seen.allocator, file_path, entry.name);
554 try self.addToQueue(full_path);
555 }
556 }
557 }
542 const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
558543};
559544
560545fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
......@@ -587,17 +572,17 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
587572 try loop.initMultiThreaded(allocator);
588573 defer loop.deinit();
589574
590 var event_loop_local = try EventLoopLocal.init(&loop);
591 defer event_loop_local.deinit();
575 var zig_compiler = try ZigCompiler.init(&loop);
576 defer zig_compiler.deinit();
592577
593 const handle = try async<loop.allocator> findLibCAsync(&event_loop_local);
578 const handle = try async<loop.allocator> findLibCAsync(&zig_compiler);
594579 defer cancel handle;
595580
596581 loop.run();
597582}
598583
599async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {
600 const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| {
584async fn findLibCAsync(zig_compiler: *ZigCompiler) void {
585 const libc = (await (async zig_compiler.getNativeLibC() catch unreachable)) catch |err| {
601586 stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
602587 os.exit(1);
603588 };
......@@ -636,7 +621,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
636621 var stdin_file = try io.getStdIn();
637622 var stdin = io.FileInStream.init(&stdin_file);
638623
639 const source_code = try stdin.stream.readAllAlloc(allocator, @maxValue(usize));
624 const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size);
640625 defer allocator.free(source_code);
641626
642627 var tree = std.zig.parse(allocator, source_code) catch |err| {
......@@ -665,66 +650,143 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
665650 os.exit(1);
666651 }
667652
653 var loop: event.Loop = undefined;
654 try loop.initMultiThreaded(allocator);
655 defer loop.deinit();
656
657 var result: FmtError!void = undefined;
658 const main_handle = try async<allocator> asyncFmtMainChecked(
659 &result,
660 &loop,
661 flags,
662 color,
663 );
664 defer cancel main_handle;
665 loop.run();
666 return result;
667}
668
669async fn asyncFmtMainChecked(
670 result: *(FmtError!void),
671 loop: *event.Loop,
672 flags: *const Args,
673 color: errmsg.Color,
674) void {
675 result.* = await (async asyncFmtMain(loop, flags, color) catch unreachable);
676}
677
678const FmtError = error{
679 SystemResources,
680 OperationAborted,
681 IoPending,
682 BrokenPipe,
683 Unexpected,
684 WouldBlock,
685 FileClosed,
686 DestinationAddressRequired,
687 DiskQuota,
688 FileTooBig,
689 InputOutput,
690 NoSpaceLeft,
691 AccessDenied,
692 OutOfMemory,
693 RenameAcrossMountPoints,
694 ReadOnlyFileSystem,
695 LinkQuotaExceeded,
696 FileBusy,
697} || os.File.OpenError;
698
699async fn asyncFmtMain(
700 loop: *event.Loop,
701 flags: *const Args,
702 color: errmsg.Color,
703) FmtError!void {
704 suspend {
705 resume @handle();
706 }
668707 var fmt = Fmt{
669 .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator),
670 .queue = std.LinkedList([]const u8).init(),
708 .seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)),
671709 .any_error = false,
710 .color = color,
711 .loop = loop,
672712 };
673713
714 var group = event.Group(FmtError!void).init(loop);
674715 for (flags.positionals.toSliceConst()) |file_path| {
675 try fmt.addToQueue(file_path);
716 try group.call(fmtPath, &fmt, file_path);
676717 }
718 try await (async group.wait() catch unreachable);
719 if (fmt.any_error) {
720 os.exit(1);
721 }
722}
677723
678 while (fmt.queue.popFirst()) |node| {
679 const file_path = node.data;
724async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
725 const file_path = try std.mem.dupe(fmt.loop.allocator, u8, file_path_ref);
726 defer fmt.loop.allocator.free(file_path);
680727
681 var file = try os.File.openRead(allocator, file_path);
682 defer file.close();
728 {
729 const held = await (async fmt.seen.acquire() catch unreachable);
730 defer held.release();
683731
684 const source_code = io.readFileAlloc(allocator, file_path) catch |err| switch (err) {
685 error.IsDir => {
686 try fmt.addDirToQueue(file_path);
687 continue;
688 },
689 else => {
690 try stderr.print("unable to open '{}': {}\n", file_path, err);
691 fmt.any_error = true;
692 continue;
693 },
694 };
695 defer allocator.free(source_code);
732 if (try held.value.put(file_path, {})) |_| return;
733 }
696734
697 var tree = std.zig.parse(allocator, source_code) catch |err| {
698 try stderr.print("error parsing file '{}': {}\n", file_path, err);
735 const source_code = (await try async event.fs.readFile(
736 fmt.loop,
737 file_path,
738 max_src_size,
739 )) catch |err| switch (err) {
740 error.IsDir => {
741 // TODO make event based (and dir.next())
742 var dir = try std.os.Dir.open(fmt.loop.allocator, file_path);
743 defer dir.close();
744
745 var group = event.Group(FmtError!void).init(fmt.loop);
746 while (try dir.next()) |entry| {
747 if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
748 const full_path = try os.path.join(fmt.loop.allocator, file_path, entry.name);
749 try group.call(fmtPath, fmt, full_path);
750 }
751 }
752 return await (async group.wait() catch unreachable);
753 },
754 else => {
755 // TODO lock stderr printing
756 try stderr.print("unable to open '{}': {}\n", file_path, err);
699757 fmt.any_error = true;
700 continue;
701 };
702 defer tree.deinit();
703
704 var error_it = tree.errors.iterator(0);
705 while (error_it.next()) |parse_error| {
706 const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path);
707 defer msg.destroy();
758 return;
759 },
760 };
761 defer fmt.loop.allocator.free(source_code);
708762
709 try msg.printToFile(&stderr_file, color);
710 }
711 if (tree.errors.len != 0) {
712 fmt.any_error = true;
713 continue;
714 }
763 var tree = std.zig.parse(fmt.loop.allocator, source_code) catch |err| {
764 try stderr.print("error parsing file '{}': {}\n", file_path, err);
765 fmt.any_error = true;
766 return;
767 };
768 defer tree.deinit();
715769
716 const baf = try io.BufferedAtomicFile.create(allocator, file_path);
717 defer baf.destroy();
770 var error_it = tree.errors.iterator(0);
771 while (error_it.next()) |parse_error| {
772 const msg = try errmsg.Msg.createFromParseError(fmt.loop.allocator, parse_error, &tree, file_path);
773 defer fmt.loop.allocator.destroy(msg);
718774
719 const anything_changed = try std.zig.render(allocator, baf.stream(), &tree);
720 if (anything_changed) {
721 try stderr.print("{}\n", file_path);
722 try baf.finish();
723 }
775 try msg.printToFile(&stderr_file, fmt.color);
776 }
777 if (tree.errors.len != 0) {
778 fmt.any_error = true;
779 return;
724780 }
725781
726 if (fmt.any_error) {
727 os.exit(1);
782 // TODO make this evented
783 const baf = try io.BufferedAtomicFile.create(fmt.loop.allocator, file_path);
784 defer baf.destroy();
785
786 const anything_changed = try std.zig.render(fmt.loop.allocator, baf.stream(), &tree);
787 if (anything_changed) {
788 try stderr.print("{}\n", file_path);
789 try baf.finish();
728790 }
729791}
730792
src-self-hosted/scope.zig+45-21
......@@ -36,6 +36,7 @@ pub const Scope = struct {
3636 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),
3737 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),
3838 Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),
39 Id.AstTree => @fieldParentPtr(AstTree, "base", base).destroy(comp),
3940 }
4041 }
4142 }
......@@ -62,6 +63,8 @@ pub const Scope = struct {
6263 Id.CompTime,
6364 Id.Var,
6465 => scope = scope.parent.?,
66
67 Id.AstTree => unreachable,
6568 }
6669 }
6770 }
......@@ -82,6 +85,8 @@ pub const Scope = struct {
8285 Id.Root,
8386 Id.Var,
8487 => scope = scope.parent orelse return null,
88
89 Id.AstTree => unreachable,
8590 }
8691 }
8792 }
......@@ -97,6 +102,7 @@ pub const Scope = struct {
97102
98103 pub const Id = enum {
99104 Root,
105 AstTree,
100106 Decls,
101107 Block,
102108 FnDef,
......@@ -108,13 +114,12 @@ pub const Scope = struct {
108114
109115 pub const Root = struct {
110116 base: Scope,
111 tree: *ast.Tree,
112117 realpath: []const u8,
118 decls: *Decls,
113119
114120 /// Creates a Root scope with 1 reference
115121 /// Takes ownership of realpath
116 /// Takes ownership of tree, will deinit and destroy when done.
117 pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
122 pub fn create(comp: *Compilation, realpath: []u8) !*Root {
118123 const self = try comp.gpa().createOne(Root);
119124 self.* = Root{
120125 .base = Scope{
......@@ -122,41 +127,65 @@ pub const Scope = struct {
122127 .parent = null,
123128 .ref_count = std.atomic.Int(usize).init(1),
124129 },
125 .tree = tree,
126130 .realpath = realpath,
131 .decls = undefined,
127132 };
128
133 errdefer comp.gpa().destroy(self);
134 self.decls = try Decls.create(comp, &self.base);
129135 return self;
130136 }
131137
132138 pub fn destroy(self: *Root, comp: *Compilation) void {
139 // TODO comp.fs_watch.removeFile(self.realpath);
140 self.decls.base.deref(comp);
141 comp.gpa().free(self.realpath);
142 comp.gpa().destroy(self);
143 }
144 };
145
146 pub const AstTree = struct {
147 base: Scope,
148 tree: *ast.Tree,
149
150 /// Creates a scope with 1 reference
151 /// Takes ownership of tree, will deinit and destroy when done.
152 pub fn create(comp: *Compilation, tree: *ast.Tree, root_scope: *Root) !*AstTree {
153 const self = try comp.gpa().createOne(AstTree);
154 self.* = AstTree{
155 .base = undefined,
156 .tree = tree,
157 };
158 self.base.init(Id.AstTree, &root_scope.base);
159
160 return self;
161 }
162
163 pub fn destroy(self: *AstTree, comp: *Compilation) void {
133164 comp.gpa().free(self.tree.source);
134165 self.tree.deinit();
135166 comp.gpa().destroy(self.tree);
136 comp.gpa().free(self.realpath);
137167 comp.gpa().destroy(self);
138168 }
169
170 pub fn root(self: *AstTree) *Root {
171 return self.base.findRoot();
172 }
139173 };
140174
141175 pub const Decls = struct {
142176 base: Scope,
143177
144 /// The lock must be respected for writing. However once name_future resolves,
145 /// readers can freely access it.
146 table: event.Locked(Decl.Table),
147
148 /// Once this future is resolved, the table is complete and available for unlocked
149 /// read-only access. It does not mean all the decls are resolved; it means only that
150 /// the table has all the names. Each decl in the table has its own resolution state.
151 name_future: event.Future(void),
178 /// This table remains Write Locked when the names are incomplete or possibly outdated.
179 /// So if a reader manages to grab a lock, it can be sure that the set of names is complete
180 /// and correct.
181 table: event.RwLocked(Decl.Table),
152182
153183 /// Creates a Decls scope with 1 reference
154184 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
155185 const self = try comp.gpa().createOne(Decls);
156186 self.* = Decls{
157187 .base = undefined,
158 .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
159 .name_future = event.Future(void).init(comp.loop),
188 .table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
160189 };
161190 self.base.init(Id.Decls, parent);
162191 return self;
......@@ -166,11 +195,6 @@ pub const Scope = struct {
166195 self.table.deinit();
167196 comp.gpa().destroy(self);
168197 }
169
170 pub async fn getTableReadOnly(self: *Decls) *Decl.Table {
171 _ = await (async self.name_future.get() catch unreachable);
172 return &self.table.private_data;
173 }
174198 };
175199
176200 pub const Block = struct {
src-self-hosted/test.zig+18-17
......@@ -6,7 +6,7 @@ const Compilation = @import("compilation.zig").Compilation;
66const introspect = @import("introspect.zig");
77const assertOrPanic = std.debug.assertOrPanic;
88const errmsg = @import("errmsg.zig");
9const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
9const ZigCompiler = @import("compilation.zig").ZigCompiler;
1010
1111var ctx: TestContext = undefined;
1212
......@@ -25,7 +25,7 @@ const allocator = std.heap.c_allocator;
2525
2626pub const TestContext = struct {
2727 loop: std.event.Loop,
28 event_loop_local: EventLoopLocal,
28 zig_compiler: ZigCompiler,
2929 zig_lib_dir: []u8,
3030 file_index: std.atomic.Int(usize),
3131 group: std.event.Group(error!void),
......@@ -37,20 +37,20 @@ pub const TestContext = struct {
3737 self.* = TestContext{
3838 .any_err = {},
3939 .loop = undefined,
40 .event_loop_local = undefined,
40 .zig_compiler = undefined,
4141 .zig_lib_dir = undefined,
4242 .group = undefined,
4343 .file_index = std.atomic.Int(usize).init(0),
4444 };
4545
46 try self.loop.initMultiThreaded(allocator);
46 try self.loop.initSingleThreaded(allocator);
4747 errdefer self.loop.deinit();
4848
49 self.event_loop_local = try EventLoopLocal.init(&self.loop);
50 errdefer self.event_loop_local.deinit();
49 self.zig_compiler = try ZigCompiler.init(&self.loop);
50 errdefer self.zig_compiler.deinit();
5151
5252 self.group = std.event.Group(error!void).init(&self.loop);
53 errdefer self.group.cancelAll();
53 errdefer self.group.deinit();
5454
5555 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
5656 errdefer allocator.free(self.zig_lib_dir);
......@@ -62,7 +62,7 @@ pub const TestContext = struct {
6262 fn deinit(self: *TestContext) void {
6363 std.os.deleteTree(allocator, tmp_dir_name) catch {};
6464 allocator.free(self.zig_lib_dir);
65 self.event_loop_local.deinit();
65 self.zig_compiler.deinit();
6666 self.loop.deinit();
6767 }
6868
......@@ -94,10 +94,10 @@ pub const TestContext = struct {
9494 }
9595
9696 // TODO async I/O
97 try std.io.writeFile(allocator, file1_path, source);
97 try std.io.writeFile(file1_path, source);
9898
9999 var comp = try Compilation.create(
100 &self.event_loop_local,
100 &self.zig_compiler,
101101 "test",
102102 file1_path,
103103 Target.Native,
......@@ -108,7 +108,7 @@ pub const TestContext = struct {
108108 );
109109 errdefer comp.destroy();
110110
111 try comp.build();
111 comp.start();
112112
113113 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
114114 }
......@@ -128,10 +128,10 @@ pub const TestContext = struct {
128128 }
129129
130130 // TODO async I/O
131 try std.io.writeFile(allocator, file1_path, source);
131 try std.io.writeFile(file1_path, source);
132132
133133 var comp = try Compilation.create(
134 &self.event_loop_local,
134 &self.zig_compiler,
135135 "test",
136136 file1_path,
137137 Target.Native,
......@@ -144,7 +144,7 @@ pub const TestContext = struct {
144144
145145 _ = try comp.addLinkLib("c", true);
146146 comp.link_out_file = output_file;
147 try comp.build();
147 comp.start();
148148
149149 try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
150150 }
......@@ -212,9 +212,10 @@ pub const TestContext = struct {
212212 Compilation.Event.Fail => |msgs| {
213213 assertOrPanic(msgs.len != 0);
214214 for (msgs) |msg| {
215 if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {
216 const first_token = msg.getTree().tokens.at(msg.span.first);
217 const last_token = msg.getTree().tokens.at(msg.span.first);
215 if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) {
216 const span = msg.getSpan();
217 const first_token = msg.getTree().tokens.at(span.first);
218 const last_token = msg.getTree().tokens.at(span.first);
218219 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
219220 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
220221 return;
src-self-hosted/type.zig+2-2
......@@ -184,8 +184,8 @@ pub const Type = struct {
184184 if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
185185
186186 {
187 const held = try comp.event_loop_local.getAnyLlvmContext();
188 defer held.release(comp.event_loop_local);
187 const held = try comp.zig_compiler.getAnyLlvmContext();
188 defer held.release(comp.zig_compiler);
189189
190190 const llvm_context = held.node.data;
191191
src/all_types.hpp+2-2
......@@ -1850,7 +1850,7 @@ struct ScopeDecls {
18501850 HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> decl_table;
18511851 bool safety_off;
18521852 AstNode *safety_set_node;
1853 bool fast_math_off;
1853 bool fast_math_on;
18541854 AstNode *fast_math_set_node;
18551855 ImportTableEntry *import;
18561856 // If this is a scope from a container, this is the type entry, otherwise null
......@@ -1870,7 +1870,7 @@ struct ScopeBlock {
18701870
18711871 bool safety_off;
18721872 AstNode *safety_set_node;
1873 bool fast_math_off;
1873 bool fast_math_on;
18741874 AstNode *fast_math_set_node;
18751875};
18761876
src/analyze.cpp+130-86
......@@ -19,12 +19,12 @@
1919
2020static const size_t default_backward_branch_quota = 1000;
2121
22static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type);
23static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type);
22static Error resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type);
23static Error resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type);
2424
25static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type);
26static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type);
27static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type);
25static Error ATTRIBUTE_MUST_USE resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type);
26static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type);
27static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type);
2828static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry);
2929
3030ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
......@@ -370,15 +370,20 @@ uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry) {
370370 return LLVMSizeOfTypeInBits(g->target_data_ref, type_entry->type_ref);
371371}
372372
373bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry) {
374 type_ensure_zero_bits_known(g, type_entry);
373Result<bool> type_is_copyable(CodeGen *g, TypeTableEntry *type_entry) {
374 Error err;
375 if ((err = type_ensure_zero_bits_known(g, type_entry)))
376 return err;
377
375378 if (!type_has_bits(type_entry))
376379 return true;
377380
378381 if (!handle_is_ptr(type_entry))
379382 return true;
380383
381 ensure_complete_type(g, type_entry);
384 if ((err = ensure_complete_type(g, type_entry)))
385 return err;
386
382387 return type_entry->is_copyable;
383388}
384389
......@@ -447,7 +452,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type
447452 }
448453 }
449454
450 type_ensure_zero_bits_known(g, child_type);
455 assertNoError(type_ensure_zero_bits_known(g, child_type));
451456
452457 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer);
453458 entry->is_copyable = true;
......@@ -554,11 +559,11 @@ TypeTableEntry *get_optional_type(CodeGen *g, TypeTableEntry *child_type) {
554559 TypeTableEntry *entry = child_type->optional_parent;
555560 return entry;
556561 } else {
557 ensure_complete_type(g, child_type);
562 assertNoError(ensure_complete_type(g, child_type));
558563
559564 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdOptional);
560565 assert(child_type->type_ref || child_type->zero_bits);
561 entry->is_copyable = type_is_copyable(g, child_type);
566 entry->is_copyable = type_is_copyable(g, child_type).unwrap();
562567
563568 buf_resize(&entry->name, 0);
564569 buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));
......@@ -650,7 +655,7 @@ TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, T
650655 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion);
651656 entry->is_copyable = true;
652657 assert(payload_type->di_type);
653 ensure_complete_type(g, payload_type);
658 assertNoError(ensure_complete_type(g, payload_type));
654659
655660 buf_resize(&entry->name, 0);
656661 buf_appendf(&entry->name, "%s!%s", buf_ptr(&err_set_type->name), buf_ptr(&payload_type->name));
......@@ -739,7 +744,7 @@ TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t
739744 return entry;
740745 }
741746
742 ensure_complete_type(g, child_type);
747 assertNoError(ensure_complete_type(g, child_type));
743748
744749 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
745750 entry->zero_bits = (array_size == 0) || child_type->zero_bits;
......@@ -1050,13 +1055,13 @@ TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) {
10501055}
10511056
10521057TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
1058 Error err;
10531059 auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
10541060 if (table_entry) {
10551061 return table_entry->value;
10561062 }
10571063 if (fn_type_id->return_type != nullptr) {
1058 ensure_complete_type(g, fn_type_id->return_type);
1059 if (type_is_invalid(fn_type_id->return_type))
1064 if ((err = ensure_complete_type(g, fn_type_id->return_type)))
10601065 return g->builtin_types.entry_invalid;
10611066 assert(fn_type_id->return_type->id != TypeTableEntryIdOpaque);
10621067 } else {
......@@ -1172,8 +1177,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
11721177 gen_param_info->src_index = i;
11731178 gen_param_info->gen_index = SIZE_MAX;
11741179
1175 ensure_complete_type(g, type_entry);
1176 if (type_is_invalid(type_entry))
1180 if ((err = ensure_complete_type(g, type_entry)))
11771181 return g->builtin_types.entry_invalid;
11781182
11791183 if (type_has_bits(type_entry)) {
......@@ -1493,6 +1497,7 @@ TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry) {
14931497static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, FnTableEntry *fn_entry) {
14941498 assert(proto_node->type == NodeTypeFnProto);
14951499 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
1500 Error err;
14961501
14971502 FnTypeId fn_type_id = {0};
14981503 init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
......@@ -1550,7 +1555,8 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
15501555 return g->builtin_types.entry_invalid;
15511556 }
15521557 if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
1553 type_ensure_zero_bits_known(g, type_entry);
1558 if ((err = type_ensure_zero_bits_known(g, type_entry)))
1559 return g->builtin_types.entry_invalid;
15541560 if (!type_has_bits(type_entry)) {
15551561 add_node_error(g, param_node->data.param_decl.type,
15561562 buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
......@@ -1598,7 +1604,8 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
15981604 case TypeTableEntryIdUnion:
15991605 case TypeTableEntryIdFn:
16001606 case TypeTableEntryIdPromise:
1601 type_ensure_zero_bits_known(g, type_entry);
1607 if ((err = type_ensure_zero_bits_known(g, type_entry)))
1608 return g->builtin_types.entry_invalid;
16021609 if (type_requires_comptime(type_entry)) {
16031610 add_node_error(g, param_node->data.param_decl.type,
16041611 buf_sprintf("parameter of type '%s' must be declared comptime",
......@@ -1729,24 +1736,28 @@ bool type_is_invalid(TypeTableEntry *type_entry) {
17291736}
17301737
17311738
1732static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
1739static Error resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
17331740 assert(enum_type->id == TypeTableEntryIdEnum);
17341741
1742 if (enum_type->data.enumeration.is_invalid)
1743 return ErrorSemanticAnalyzeFail;
1744
17351745 if (enum_type->data.enumeration.complete)
1736 return;
1746 return ErrorNone;
17371747
1738 resolve_enum_zero_bits(g, enum_type);
1739 if (type_is_invalid(enum_type))
1740 return;
1748 Error err;
1749 if ((err = resolve_enum_zero_bits(g, enum_type)))
1750 return err;
17411751
17421752 AstNode *decl_node = enum_type->data.enumeration.decl_node;
17431753
17441754 if (enum_type->data.enumeration.embedded_in_current) {
17451755 if (!enum_type->data.enumeration.reported_infinite_err) {
1756 enum_type->data.enumeration.is_invalid = true;
17461757 enum_type->data.enumeration.reported_infinite_err = true;
17471758 add_node_error(g, decl_node, buf_sprintf("enum '%s' contains itself", buf_ptr(&enum_type->name)));
17481759 }
1749 return;
1760 return ErrorSemanticAnalyzeFail;
17501761 }
17511762
17521763 assert(!enum_type->data.enumeration.zero_bits_loop_flag);
......@@ -1778,7 +1789,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
17781789 enum_type->data.enumeration.complete = true;
17791790
17801791 if (enum_type->data.enumeration.is_invalid)
1781 return;
1792 return ErrorSemanticAnalyzeFail;
17821793
17831794 if (enum_type->zero_bits) {
17841795 enum_type->type_ref = LLVMVoidType();
......@@ -1797,7 +1808,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
17971808
17981809 ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type);
17991810 enum_type->di_type = replacement_di_type;
1800 return;
1811 return ErrorNone;
18011812 }
18021813
18031814 TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type;
......@@ -1815,6 +1826,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
18151826
18161827 ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type);
18171828 enum_type->di_type = tag_di_type;
1829 return ErrorNone;
18181830}
18191831
18201832
......@@ -1897,15 +1909,15 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f
18971909 return struct_type;
18981910}
18991911
1900static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
1912static Error resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
19011913 assert(struct_type->id == TypeTableEntryIdStruct);
19021914
19031915 if (struct_type->data.structure.complete)
1904 return;
1916 return ErrorNone;
19051917
1906 resolve_struct_zero_bits(g, struct_type);
1907 if (struct_type->data.structure.is_invalid)
1908 return;
1918 Error err;
1919 if ((err = resolve_struct_zero_bits(g, struct_type)))
1920 return err;
19091921
19101922 AstNode *decl_node = struct_type->data.structure.decl_node;
19111923
......@@ -1916,7 +1928,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
19161928 add_node_error(g, decl_node,
19171929 buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name)));
19181930 }
1919 return;
1931 return ErrorSemanticAnalyzeFail;
19201932 }
19211933
19221934 assert(!struct_type->data.structure.zero_bits_loop_flag);
......@@ -1943,8 +1955,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
19431955 TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
19441956 TypeTableEntry *field_type = type_struct_field->type_entry;
19451957
1946 ensure_complete_type(g, field_type);
1947 if (type_is_invalid(field_type)) {
1958 if ((err = ensure_complete_type(g, field_type))) {
19481959 struct_type->data.structure.is_invalid = true;
19491960 break;
19501961 }
......@@ -2026,7 +2037,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
20262037 struct_type->data.structure.complete = true;
20272038
20282039 if (struct_type->data.structure.is_invalid)
2029 return;
2040 return ErrorSemanticAnalyzeFail;
20302041
20312042 if (struct_type->zero_bits) {
20322043 struct_type->type_ref = LLVMVoidType();
......@@ -2045,7 +2056,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
20452056 0, nullptr, di_element_types, (int)debug_field_count, 0, nullptr, "");
20462057 ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type);
20472058 struct_type->di_type = replacement_di_type;
2048 return;
2059 return ErrorNone;
20492060 }
20502061 assert(struct_type->di_type);
20512062
......@@ -2128,17 +2139,19 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
21282139
21292140 ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type);
21302141 struct_type->di_type = replacement_di_type;
2142
2143 return ErrorNone;
21312144}
21322145
2133static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
2146static Error resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
21342147 assert(union_type->id == TypeTableEntryIdUnion);
21352148
21362149 if (union_type->data.unionation.complete)
2137 return;
2150 return ErrorNone;
21382151
2139 resolve_union_zero_bits(g, union_type);
2140 if (type_is_invalid(union_type))
2141 return;
2152 Error err;
2153 if ((err = resolve_union_zero_bits(g, union_type)))
2154 return err;
21422155
21432156 AstNode *decl_node = union_type->data.unionation.decl_node;
21442157
......@@ -2148,7 +2161,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
21482161 union_type->data.unionation.is_invalid = true;
21492162 add_node_error(g, decl_node, buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name)));
21502163 }
2151 return;
2164 return ErrorSemanticAnalyzeFail;
21522165 }
21532166
21542167 assert(!union_type->data.unionation.zero_bits_loop_flag);
......@@ -2179,8 +2192,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
21792192 TypeUnionField *union_field = &union_type->data.unionation.fields[i];
21802193 TypeTableEntry *field_type = union_field->type_entry;
21812194
2182 ensure_complete_type(g, field_type);
2183 if (type_is_invalid(field_type)) {
2195 if ((err = ensure_complete_type(g, field_type))) {
21842196 union_type->data.unionation.is_invalid = true;
21852197 continue;
21862198 }
......@@ -2219,7 +2231,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
22192231 union_type->data.unionation.most_aligned_union_member = most_aligned_union_member;
22202232
22212233 if (union_type->data.unionation.is_invalid)
2222 return;
2234 return ErrorSemanticAnalyzeFail;
22232235
22242236 if (union_type->zero_bits) {
22252237 union_type->type_ref = LLVMVoidType();
......@@ -2238,7 +2250,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
22382250
22392251 ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
22402252 union_type->di_type = replacement_di_type;
2241 return;
2253 return ErrorNone;
22422254 }
22432255
22442256 uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits;
......@@ -2274,7 +2286,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
22742286
22752287 ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
22762288 union_type->di_type = replacement_di_type;
2277 return;
2289 return ErrorNone;
22782290 }
22792291
22802292 LLVMTypeRef union_type_ref;
......@@ -2293,7 +2305,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
22932305
22942306 ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, tag_type->di_type);
22952307 union_type->di_type = tag_type->di_type;
2296 return;
2308 return ErrorNone;
22972309 } else {
22982310 union_type_ref = most_aligned_union_member->type_ref;
22992311 }
......@@ -2367,19 +2379,21 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
23672379
23682380 ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
23692381 union_type->di_type = replacement_di_type;
2382
2383 return ErrorNone;
23702384}
23712385
2372static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
2386static Error resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
23732387 assert(enum_type->id == TypeTableEntryIdEnum);
23742388
23752389 if (enum_type->data.enumeration.zero_bits_known)
2376 return;
2390 return ErrorNone;
23772391
23782392 if (enum_type->data.enumeration.zero_bits_loop_flag) {
23792393 add_node_error(g, enum_type->data.enumeration.decl_node,
23802394 buf_sprintf("'%s' depends on itself", buf_ptr(&enum_type->name)));
23812395 enum_type->data.enumeration.is_invalid = true;
2382 return;
2396 return ErrorSemanticAnalyzeFail;
23832397 }
23842398
23852399 enum_type->data.enumeration.zero_bits_loop_flag = true;
......@@ -2398,7 +2412,7 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
23982412 enum_type->data.enumeration.is_invalid = true;
23992413 enum_type->data.enumeration.zero_bits_loop_flag = false;
24002414 enum_type->data.enumeration.zero_bits_known = true;
2401 return;
2415 return ErrorSemanticAnalyzeFail;
24022416 }
24032417
24042418 enum_type->data.enumeration.src_field_count = field_count;
......@@ -2525,13 +2539,23 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
25252539 enum_type->data.enumeration.zero_bits_loop_flag = false;
25262540 enum_type->zero_bits = !type_has_bits(tag_int_type);
25272541 enum_type->data.enumeration.zero_bits_known = true;
2542
2543 if (enum_type->data.enumeration.is_invalid)
2544 return ErrorSemanticAnalyzeFail;
2545
2546 return ErrorNone;
25282547}
25292548
2530static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
2549static Error resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
25312550 assert(struct_type->id == TypeTableEntryIdStruct);
25322551
2552 Error err;
2553
2554 if (struct_type->data.structure.is_invalid)
2555 return ErrorSemanticAnalyzeFail;
2556
25332557 if (struct_type->data.structure.zero_bits_known)
2534 return;
2558 return ErrorNone;
25352559
25362560 if (struct_type->data.structure.zero_bits_loop_flag) {
25372561 // If we get here it's due to recursion. This is a design flaw in the compiler,
......@@ -2547,7 +2571,7 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
25472571 struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0));
25482572 }
25492573 }
2550 return;
2574 return ErrorNone;
25512575 }
25522576
25532577 struct_type->data.structure.zero_bits_loop_flag = true;
......@@ -2596,8 +2620,7 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
25962620 buf_sprintf("enums, not structs, support field assignment"));
25972621 }
25982622
2599 type_ensure_zero_bits_known(g, field_type);
2600 if (type_is_invalid(field_type)) {
2623 if ((err = type_ensure_zero_bits_known(g, field_type))) {
26012624 struct_type->data.structure.is_invalid = true;
26022625 continue;
26032626 }
......@@ -2634,16 +2657,27 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
26342657 struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index;
26352658 struct_type->zero_bits = (gen_field_index == 0);
26362659 struct_type->data.structure.zero_bits_known = true;
2660
2661 if (struct_type->data.structure.is_invalid) {
2662 return ErrorSemanticAnalyzeFail;
2663 }
2664
2665 return ErrorNone;
26372666}
26382667
2639static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
2668static Error resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
26402669 assert(union_type->id == TypeTableEntryIdUnion);
26412670
2671 Error err;
2672
2673 if (union_type->data.unionation.is_invalid)
2674 return ErrorSemanticAnalyzeFail;
2675
26422676 if (union_type->data.unionation.zero_bits_known)
2643 return;
2677 return ErrorNone;
26442678
26452679 if (type_is_invalid(union_type))
2646 return;
2680 return ErrorSemanticAnalyzeFail;
26472681
26482682 if (union_type->data.unionation.zero_bits_loop_flag) {
26492683 // If we get here it's due to recursion. From this we conclude that the struct is
......@@ -2660,7 +2694,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
26602694 LLVMPointerType(LLVMInt8Type(), 0));
26612695 }
26622696 }
2663 return;
2697 return ErrorNone;
26642698 }
26652699
26662700 union_type->data.unionation.zero_bits_loop_flag = true;
......@@ -2679,7 +2713,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
26792713 union_type->data.unionation.is_invalid = true;
26802714 union_type->data.unionation.zero_bits_loop_flag = false;
26812715 union_type->data.unionation.zero_bits_known = true;
2682 return;
2716 return ErrorSemanticAnalyzeFail;
26832717 }
26842718 union_type->data.unionation.src_field_count = field_count;
26852719 union_type->data.unionation.fields = allocate<TypeUnionField>(field_count);
......@@ -2711,13 +2745,13 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
27112745 tag_int_type = analyze_type_expr(g, scope, enum_type_node);
27122746 if (type_is_invalid(tag_int_type)) {
27132747 union_type->data.unionation.is_invalid = true;
2714 return;
2748 return ErrorSemanticAnalyzeFail;
27152749 }
27162750 if (tag_int_type->id != TypeTableEntryIdInt) {
27172751 add_node_error(g, enum_type_node,
27182752 buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name)));
27192753 union_type->data.unionation.is_invalid = true;
2720 return;
2754 return ErrorSemanticAnalyzeFail;
27212755 }
27222756 } else {
27232757 tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
......@@ -2744,13 +2778,13 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
27442778 TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node);
27452779 if (type_is_invalid(enum_type)) {
27462780 union_type->data.unionation.is_invalid = true;
2747 return;
2781 return ErrorSemanticAnalyzeFail;
27482782 }
27492783 if (enum_type->id != TypeTableEntryIdEnum) {
27502784 union_type->data.unionation.is_invalid = true;
27512785 add_node_error(g, enum_type_node,
27522786 buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));
2753 return;
2787 return ErrorSemanticAnalyzeFail;
27542788 }
27552789 tag_type = enum_type;
27562790 abi_alignment_so_far = get_abi_alignment(g, enum_type); // this populates src_field_count
......@@ -2789,8 +2823,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
27892823 }
27902824 } else {
27912825 field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
2792 type_ensure_zero_bits_known(g, field_type);
2793 if (type_is_invalid(field_type)) {
2826 if ((err = type_ensure_zero_bits_known(g, field_type))) {
27942827 union_type->data.unionation.is_invalid = true;
27952828 continue;
27962829 }
......@@ -2883,7 +2916,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
28832916 union_type->data.unionation.abi_alignment = abi_alignment_so_far;
28842917
28852918 if (union_type->data.unionation.is_invalid)
2886 return;
2919 return ErrorSemanticAnalyzeFail;
28872920
28882921 bool src_have_tag = decl_node->data.container_decl.auto_enum ||
28892922 decl_node->data.container_decl.init_arg_expr != nullptr;
......@@ -2905,7 +2938,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
29052938 add_node_error(g, source_node,
29062939 buf_sprintf("%s union does not support enum tag type", qual_str));
29072940 union_type->data.unionation.is_invalid = true;
2908 return;
2941 return ErrorSemanticAnalyzeFail;
29092942 }
29102943
29112944 if (create_enum_type) {
......@@ -2970,6 +3003,11 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
29703003 union_type->data.unionation.gen_field_count = gen_field_index;
29713004 union_type->zero_bits = (gen_field_index == 0 && (field_count < 2 || !src_have_tag));
29723005 union_type->data.unionation.zero_bits_known = true;
3006
3007 if (union_type->data.unionation.is_invalid)
3008 return ErrorSemanticAnalyzeFail;
3009
3010 return ErrorNone;
29733011}
29743012
29753013static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) {
......@@ -3035,7 +3073,7 @@ static bool scope_is_root_decls(Scope *scope) {
30353073
30363074static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntry *fn_type) {
30373075 add_node_error(g, proto_node,
3038 buf_sprintf("expected 'fn([]const u8, ?&builtin.StackTrace) unreachable', found '%s'",
3076 buf_sprintf("expected 'fn([]const u8, ?*builtin.StackTrace) noreturn', found '%s'",
30393077 buf_ptr(&fn_type->name)));
30403078}
30413079
......@@ -3463,13 +3501,13 @@ VariableTableEntry *add_variable(CodeGen *g, AstNode *source_node, Scope *parent
34633501 variable_entry->shadowable = false;
34643502 variable_entry->mem_slot_index = SIZE_MAX;
34653503 variable_entry->src_arg_index = SIZE_MAX;
3466 variable_entry->align_bytes = get_abi_alignment(g, value->type);
34673504
34683505 assert(name);
3469
34703506 buf_init_from_buf(&variable_entry->name, name);
34713507
3472 if (value->type->id != TypeTableEntryIdInvalid) {
3508 if (!type_is_invalid(value->type)) {
3509 variable_entry->align_bytes = get_abi_alignment(g, value->type);
3510
34733511 VariableTableEntry *existing_var = find_variable(g, parent_scope, name);
34743512 if (existing_var && !existing_var->shadowable) {
34753513 ErrorMsg *msg = add_node_error(g, source_node,
......@@ -5311,13 +5349,13 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_
53115349
53125350
53135351void init_const_undefined(CodeGen *g, ConstExprValue *const_val) {
5352 Error err;
53145353 TypeTableEntry *wanted_type = const_val->type;
53155354 if (wanted_type->id == TypeTableEntryIdArray) {
53165355 const_val->special = ConstValSpecialStatic;
53175356 const_val->data.x_array.special = ConstArraySpecialUndef;
53185357 } else if (wanted_type->id == TypeTableEntryIdStruct) {
5319 ensure_complete_type(g, wanted_type);
5320 if (type_is_invalid(wanted_type)) {
5358 if ((err = ensure_complete_type(g, wanted_type))) {
53215359 return;
53225360 }
53235361
......@@ -5350,27 +5388,33 @@ ConstExprValue *create_const_vals(size_t count) {
53505388 return vals;
53515389}
53525390
5353void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry) {
5391Error ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry) {
5392 if (type_is_invalid(type_entry))
5393 return ErrorSemanticAnalyzeFail;
53545394 if (type_entry->id == TypeTableEntryIdStruct) {
53555395 if (!type_entry->data.structure.complete)
5356 resolve_struct_type(g, type_entry);
5396 return resolve_struct_type(g, type_entry);
53575397 } else if (type_entry->id == TypeTableEntryIdEnum) {
53585398 if (!type_entry->data.enumeration.complete)
5359 resolve_enum_type(g, type_entry);
5399 return resolve_enum_type(g, type_entry);
53605400 } else if (type_entry->id == TypeTableEntryIdUnion) {
53615401 if (!type_entry->data.unionation.complete)
5362 resolve_union_type(g, type_entry);
5402 return resolve_union_type(g, type_entry);
53635403 }
5404 return ErrorNone;
53645405}
53655406
5366void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry) {
5407Error type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry) {
5408 if (type_is_invalid(type_entry))
5409 return ErrorSemanticAnalyzeFail;
53675410 if (type_entry->id == TypeTableEntryIdStruct) {
5368 resolve_struct_zero_bits(g, type_entry);
5411 return resolve_struct_zero_bits(g, type_entry);
53695412 } else if (type_entry->id == TypeTableEntryIdEnum) {
5370 resolve_enum_zero_bits(g, type_entry);
5413 return resolve_enum_zero_bits(g, type_entry);
53715414 } else if (type_entry->id == TypeTableEntryIdUnion) {
5372 resolve_union_zero_bits(g, type_entry);
5415 return resolve_union_zero_bits(g, type_entry);
53735416 }
5417 return ErrorNone;
53745418}
53755419
53765420bool ir_get_var_is_comptime(VariableTableEntry *var) {
......@@ -6213,7 +6257,7 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) {
62136257}
62146258
62156259uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {
6216 type_ensure_zero_bits_known(g, type_entry);
6260 assertNoError(type_ensure_zero_bits_known(g, type_entry));
62176261 if (type_entry->zero_bits) return 0;
62186262
62196263 // We need to make this function work without requiring ensure_complete_type
src/analyze.hpp+4-3
......@@ -9,6 +9,7 @@
99#define ZIG_ANALYZE_HPP
1010
1111#include "all_types.hpp"
12#include "result.hpp"
1213
1314void semantic_analyze(CodeGen *g);
1415ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
......@@ -88,8 +89,8 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
8889AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index);
8990FnTableEntry *scope_get_fn_if_root(Scope *scope);
9091bool type_requires_comptime(TypeTableEntry *type_entry);
91void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry);
92void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry);
92Error ATTRIBUTE_MUST_USE ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry);
93Error ATTRIBUTE_MUST_USE type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry);
9394void complete_enum(CodeGen *g, TypeTableEntry *enum_type);
9495bool ir_get_var_is_comptime(VariableTableEntry *var);
9596bool const_values_equal(ConstExprValue *a, ConstExprValue *b);
......@@ -178,7 +179,7 @@ TypeTableEntryId type_id_at_index(size_t index);
178179size_t type_id_len();
179180size_t type_id_index(TypeTableEntry *entry);
180181TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id);
181bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry);
182Result<bool> type_is_copyable(CodeGen *g, TypeTableEntry *type_entry);
182183LinkLib *create_link_lib(Buf *name);
183184bool calling_convention_does_first_arg_return(CallingConvention cc);
184185LinkLib *add_link_lib(CodeGen *codegen, Buf *lib);
src/codegen.cpp+16-9
......@@ -829,15 +829,15 @@ static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) {
829829 if (scope->id == ScopeIdBlock) {
830830 ScopeBlock *block_scope = (ScopeBlock *)scope;
831831 if (block_scope->fast_math_set_node)
832 return !block_scope->fast_math_off;
832 return block_scope->fast_math_on;
833833 } else if (scope->id == ScopeIdDecls) {
834834 ScopeDecls *decls_scope = (ScopeDecls *)scope;
835835 if (decls_scope->fast_math_set_node)
836 return !decls_scope->fast_math_off;
836 return decls_scope->fast_math_on;
837837 }
838838 scope = scope->parent;
839839 }
840 return true;
840 return false;
841841}
842842
843843static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) {
......@@ -5131,13 +5131,13 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef
51315131}
51325132
51335133static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, const char *name) {
5134 render_const_val_global(g, const_val, name);
51355134 switch (const_val->data.x_ptr.special) {
51365135 case ConstPtrSpecialInvalid:
51375136 case ConstPtrSpecialDiscard:
51385137 zig_unreachable();
51395138 case ConstPtrSpecialRef:
51405139 {
5140 render_const_val_global(g, const_val, name);
51415141 ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
51425142 render_const_val(g, pointee, "");
51435143 render_const_val_global(g, pointee, "");
......@@ -5148,6 +5148,7 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
51485148 }
51495149 case ConstPtrSpecialBaseArray:
51505150 {
5151 render_const_val_global(g, const_val, name);
51515152 ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
51525153 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
51535154 assert(array_const_val->type->id == TypeTableEntryIdArray);
......@@ -5168,6 +5169,7 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
51685169 }
51695170 case ConstPtrSpecialBaseStruct:
51705171 {
5172 render_const_val_global(g, const_val, name);
51715173 ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val;
51725174 assert(struct_const_val->type->id == TypeTableEntryIdStruct);
51735175 if (struct_const_val->type->zero_bits) {
......@@ -5190,6 +5192,7 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
51905192 }
51915193 case ConstPtrSpecialHardCodedAddr:
51925194 {
5195 render_const_val_global(g, const_val, name);
51935196 uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr;
51945197 TypeTableEntry *usize = g->builtin_types.entry_usize;
51955198 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false),
......@@ -5720,12 +5723,16 @@ static void do_code_gen(CodeGen *g) {
57205723
57215724 LLVMValueRef global_value;
57225725 if (var->linkage == VarLinkageExternal) {
5723 global_value = LLVMAddGlobal(g->module, var->value->type->type_ref, buf_ptr(&var->name));
5724
5725 // TODO debug info for the extern variable
5726 LLVMValueRef existing_llvm_var = LLVMGetNamedGlobal(g->module, buf_ptr(&var->name));
5727 if (existing_llvm_var) {
5728 global_value = LLVMConstBitCast(existing_llvm_var, LLVMPointerType(var->value->type->type_ref, 0));
5729 } else {
5730 global_value = LLVMAddGlobal(g->module, var->value->type->type_ref, buf_ptr(&var->name));
5731 // TODO debug info for the extern variable
57265732
5727 LLVMSetLinkage(global_value, LLVMExternalLinkage);
5728 LLVMSetAlignment(global_value, var->align_bytes);
5733 LLVMSetLinkage(global_value, LLVMExternalLinkage);
5734 LLVMSetAlignment(global_value, var->align_bytes);
5735 }
57295736 } else {
57305737 bool exported = (var->linkage == VarLinkageExport);
57315738 const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported));
src/ir.cpp+196-190
......@@ -8711,6 +8711,7 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *
87118711}
87128712
87138713static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, TypeTableEntry *expected_type, IrInstruction **instructions, size_t instruction_count) {
8714 Error err;
87148715 assert(instruction_count >= 1);
87158716 IrInstruction *prev_inst = instructions[0];
87168717 if (type_is_invalid(prev_inst->value.type)) {
......@@ -9172,8 +9173,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
91729173 if (prev_type->id == TypeTableEntryIdEnum && cur_type->id == TypeTableEntryIdUnion &&
91739174 (cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
91749175 {
9175 type_ensure_zero_bits_known(ira->codegen, cur_type);
9176 if (type_is_invalid(cur_type))
9176 if ((err = type_ensure_zero_bits_known(ira->codegen, cur_type)))
91779177 return ira->codegen->builtin_types.entry_invalid;
91789178 if (cur_type->data.unionation.tag_type == prev_type) {
91799179 continue;
......@@ -9183,8 +9183,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
91839183 if (cur_type->id == TypeTableEntryIdEnum && prev_type->id == TypeTableEntryIdUnion &&
91849184 (prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
91859185 {
9186 type_ensure_zero_bits_known(ira->codegen, prev_type);
9187 if (type_is_invalid(prev_type))
9186 if ((err = type_ensure_zero_bits_known(ira->codegen, prev_type)))
91889187 return ira->codegen->builtin_types.entry_invalid;
91899188 if (prev_type->data.unionation.tag_type == cur_type) {
91909189 prev_inst = cur_inst;
......@@ -9999,11 +9998,11 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s
99999998static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr,
100009999 IrInstruction *target, TypeTableEntry *wanted_type)
1000110000{
10001 Error err;
1000210002 assert(wanted_type->id == TypeTableEntryIdInt);
1000310003
1000410004 TypeTableEntry *actual_type = target->value.type;
10005 ensure_complete_type(ira->codegen, actual_type);
10006 if (type_is_invalid(actual_type))
10005 if ((err = ensure_complete_type(ira->codegen, actual_type)))
1000710006 return ira->codegen->invalid_instruction;
1000810007
1000910008 if (wanted_type != actual_type->data.enumeration.tag_int_type) {
......@@ -10069,6 +10068,7 @@ static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruc
1006910068static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *source_instr,
1007010069 IrInstruction *target, TypeTableEntry *wanted_type)
1007110070{
10071 Error err;
1007210072 assert(wanted_type->id == TypeTableEntryIdUnion);
1007310073 assert(target->value.type->id == TypeTableEntryIdEnum);
1007410074
......@@ -10078,8 +10078,7 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so
1007810078 return ira->codegen->invalid_instruction;
1007910079 TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);
1008010080 assert(union_field != nullptr);
10081 type_ensure_zero_bits_known(ira->codegen, union_field->type_entry);
10082 if (type_is_invalid(union_field->type_entry))
10081 if ((err = type_ensure_zero_bits_known(ira->codegen, union_field->type_entry)))
1008310082 return ira->codegen->invalid_instruction;
1008410083 if (!union_field->type_entry->zero_bits) {
1008510084 AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(
......@@ -10169,12 +10168,12 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction
1016910168static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *source_instr,
1017010169 IrInstruction *target, TypeTableEntry *wanted_type)
1017110170{
10171 Error err;
1017210172 assert(wanted_type->id == TypeTableEntryIdEnum);
1017310173
1017410174 TypeTableEntry *actual_type = target->value.type;
1017510175
10176 ensure_complete_type(ira->codegen, wanted_type);
10177 if (type_is_invalid(wanted_type))
10176 if ((err = ensure_complete_type(ira->codegen, wanted_type)))
1017810177 return ira->codegen->invalid_instruction;
1017910178
1018010179 if (actual_type != wanted_type->data.enumeration.tag_int_type) {
......@@ -10517,6 +10516,7 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
1051710516static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
1051810517 TypeTableEntry *wanted_type, IrInstruction *value)
1051910518{
10519 Error err;
1052010520 TypeTableEntry *actual_type = value->value.type;
1052110521 AstNode *source_node = source_instr->source_node;
1052210522
......@@ -10697,6 +10697,19 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1069710697 return ira->codegen->invalid_instruction;
1069810698
1069910699 return cast2;
10700 } else if (
10701 wanted_child_type->id == TypeTableEntryIdPointer &&
10702 wanted_child_type->data.pointer.ptr_len == PtrLenUnknown &&
10703 actual_type->id == TypeTableEntryIdPointer &&
10704 actual_type->data.pointer.ptr_len == PtrLenSingle &&
10705 actual_type->data.pointer.child_type->id == TypeTableEntryIdArray &&
10706 actual_type->data.pointer.alignment >= wanted_child_type->data.pointer.alignment &&
10707 types_match_const_cast_only(ira, wanted_child_type->data.pointer.child_type,
10708 actual_type->data.pointer.child_type->data.array.child_type, source_node,
10709 !wanted_child_type->data.pointer.is_const).id == ConstCastResultIdOk)
10710 {
10711 IrInstruction *cast1 = ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_child_type);
10712 return ir_analyze_maybe_wrap(ira, source_instr, cast1, wanted_type);
1070010713 }
1070110714 }
1070210715
......@@ -10783,8 +10796,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1078310796 if (actual_type->id == TypeTableEntryIdComptimeFloat ||
1078410797 actual_type->id == TypeTableEntryIdComptimeInt)
1078510798 {
10786 ensure_complete_type(ira->codegen, wanted_type);
10787 if (type_is_invalid(wanted_type))
10799 if ((err = ensure_complete_type(ira->codegen, wanted_type)))
1078810800 return ira->codegen->invalid_instruction;
1078910801 if (wanted_type->id == TypeTableEntryIdEnum) {
1079010802 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value);
......@@ -10840,8 +10852,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1084010852
1084110853 // cast from union to the enum type of the union
1084210854 if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) {
10843 type_ensure_zero_bits_known(ira->codegen, actual_type);
10844 if (type_is_invalid(actual_type))
10855 if ((err = type_ensure_zero_bits_known(ira->codegen, actual_type)))
1084510856 return ira->codegen->invalid_instruction;
1084610857
1084710858 if (actual_type->data.unionation.tag_type == wanted_type) {
......@@ -10854,7 +10865,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1085410865 (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
1085510866 wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
1085610867 {
10857 type_ensure_zero_bits_known(ira->codegen, wanted_type);
10868 if ((err = type_ensure_zero_bits_known(ira->codegen, wanted_type)))
10869 return ira->codegen->invalid_instruction;
10870
1085810871 if (wanted_type->data.unionation.tag_type == actual_type) {
1085910872 return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);
1086010873 }
......@@ -10866,7 +10879,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1086610879 if (union_type->data.unionation.decl_node->data.container_decl.auto_enum ||
1086710880 union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)
1086810881 {
10869 type_ensure_zero_bits_known(ira->codegen, union_type);
10882 if ((err = type_ensure_zero_bits_known(ira->codegen, union_type)))
10883 return ira->codegen->invalid_instruction;
10884
1087010885 if (union_type->data.unionation.tag_type == actual_type) {
1087110886 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, union_type, value);
1087210887 if (type_is_invalid(cast1->value.type))
......@@ -10910,8 +10925,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1091010925 types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
1091110926 actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
1091210927 {
10913 type_ensure_zero_bits_known(ira->codegen, actual_type);
10914 if (type_is_invalid(actual_type)) {
10928 if ((err = type_ensure_zero_bits_known(ira->codegen, actual_type))) {
1091510929 return ira->codegen->invalid_instruction;
1091610930 }
1091710931 if (!type_has_bits(actual_type)) {
......@@ -11310,6 +11324,7 @@ static bool optional_value_is_null(ConstExprValue *val) {
1131011324}
1131111325
1131211326static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
11327 Error err;
1131311328 IrInstruction *op1 = bin_op_instruction->op1->other;
1131411329 IrInstruction *op2 = bin_op_instruction->op2->other;
1131511330 AstNode *source_node = bin_op_instruction->base.source_node;
......@@ -11445,8 +11460,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
1144511460 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2);
1144611461 if (type_is_invalid(resolved_type))
1144711462 return resolved_type;
11448 type_ensure_zero_bits_known(ira->codegen, resolved_type);
11449 if (type_is_invalid(resolved_type))
11463 if ((err = type_ensure_zero_bits_known(ira->codegen, resolved_type)))
1145011464 return resolved_type;
1145111465
1145211466 bool operator_allowed;
......@@ -12393,6 +12407,7 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
1239312407}
1239412408
1239512409static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) {
12410 Error err;
1239612411 VariableTableEntry *var = decl_var_instruction->var;
1239712412
1239812413 IrInstruction *init_value = decl_var_instruction->init_value->other;
......@@ -12426,8 +12441,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
1242612441 if (type_is_invalid(result_type)) {
1242712442 result_type = ira->codegen->builtin_types.entry_invalid;
1242812443 } else {
12429 type_ensure_zero_bits_known(ira->codegen, result_type);
12430 if (type_is_invalid(result_type)) {
12444 if ((err = type_ensure_zero_bits_known(ira->codegen, result_type))) {
1243112445 result_type = ira->codegen->builtin_types.entry_invalid;
1243212446 }
1243312447 }
......@@ -12945,6 +12959,7 @@ static VariableTableEntry *get_fn_var_by_index(FnTableEntry *fn_entry, size_t in
1294512959static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
1294612960 VariableTableEntry *var)
1294712961{
12962 Error err;
1294812963 if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) {
1294912964 assert(ira->codegen->errors.length != 0);
1295012965 return ira->codegen->invalid_instruction;
......@@ -12999,7 +13014,8 @@ no_mem_slot:
1299913014 instruction->scope, instruction->source_node, var);
1300013015 var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type,
1300113016 var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0);
13002 type_ensure_zero_bits_known(ira->codegen, var->value->type);
13017 if ((err = type_ensure_zero_bits_known(ira->codegen, var->value->type)))
13018 return ira->codegen->invalid_instruction;
1300313019
1300413020 bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
1300513021 var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;
......@@ -13011,6 +13027,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1301113027 FnTableEntry *fn_entry, TypeTableEntry *fn_type, IrInstruction *fn_ref,
1301213028 IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline)
1301313029{
13030 Error err;
1301413031 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
1301513032 size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0;
1301613033
......@@ -13375,8 +13392,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1337513392 inst_fn_type_id.return_type = specified_return_type;
1337613393 }
1337713394
13378 type_ensure_zero_bits_known(ira->codegen, specified_return_type);
13379 if (type_is_invalid(specified_return_type))
13395 if ((err = type_ensure_zero_bits_known(ira->codegen, specified_return_type)))
1338013396 return ira->codegen->builtin_types.entry_invalid;
1338113397
1338213398 if (type_requires_comptime(specified_return_type)) {
......@@ -13651,12 +13667,12 @@ static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp
1365113667}
1365213668
1365313669static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
13670 Error err;
1365413671 IrInstruction *value = un_op_instruction->value->other;
1365513672 TypeTableEntry *type_entry = ir_resolve_type(ira, value);
1365613673 if (type_is_invalid(type_entry))
1365713674 return ira->codegen->builtin_types.entry_invalid;
13658 ensure_complete_type(ira->codegen, type_entry);
13659 if (type_is_invalid(type_entry))
13675 if ((err = ensure_complete_type(ira->codegen, type_entry)))
1366013676 return ira->codegen->builtin_types.entry_invalid;
1366113677
1366213678 switch (type_entry->id) {
......@@ -14010,6 +14026,7 @@ static TypeTableEntry *adjust_ptr_len(CodeGen *g, TypeTableEntry *ptr_type, PtrL
1401014026}
1401114027
1401214028static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) {
14029 Error err;
1401314030 IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other;
1401414031 if (type_is_invalid(array_ptr->value.type))
1401514032 return ira->codegen->builtin_types.entry_invalid;
......@@ -14118,8 +14135,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc
1411814135 return ira->codegen->builtin_types.entry_invalid;
1411914136
1412014137 bool safety_check_on = elem_ptr_instruction->safety_check_on;
14121 ensure_complete_type(ira->codegen, return_type->data.pointer.child_type);
14122 if (type_is_invalid(return_type->data.pointer.child_type))
14138 if ((err = ensure_complete_type(ira->codegen, return_type->data.pointer.child_type)))
1412314139 return ira->codegen->builtin_types.entry_invalid;
1412414140
1412514141 uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type);
......@@ -14339,9 +14355,10 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira,
1433914355static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
1434014356 IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type)
1434114357{
14358 Error err;
14359
1434214360 TypeTableEntry *bare_type = container_ref_type(container_type);
14343 ensure_complete_type(ira->codegen, bare_type);
14344 if (type_is_invalid(bare_type))
14361 if ((err = ensure_complete_type(ira->codegen, bare_type)))
1434514362 return ira->codegen->invalid_instruction;
1434614363
1434714364 assert(container_ptr->value.type->id == TypeTableEntryIdPointer);
......@@ -14540,6 +14557,7 @@ static ErrorTableEntry *find_err_table_entry(TypeTableEntry *err_set_type, Buf *
1454014557}
1454114558
1454214559static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {
14560 Error err;
1454314561 IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
1454414562 if (type_is_invalid(container_ptr->value.type))
1454514563 return ira->codegen->builtin_types.entry_invalid;
......@@ -14641,8 +14659,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
1464114659 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
1464214660 }
1464314661 if (child_type->id == TypeTableEntryIdEnum) {
14644 ensure_complete_type(ira->codegen, child_type);
14645 if (type_is_invalid(child_type))
14662 if ((err = ensure_complete_type(ira->codegen, child_type)))
1464614663 return ira->codegen->builtin_types.entry_invalid;
1464714664
1464814665 TypeEnumField *field = find_enum_type_field(child_type, field_name);
......@@ -14666,8 +14683,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
1466614683 (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr ||
1466714684 child_type->data.unionation.decl_node->data.container_decl.auto_enum))
1466814685 {
14669 ensure_complete_type(ira->codegen, child_type);
14670 if (type_is_invalid(child_type))
14686 if ((err = ensure_complete_type(ira->codegen, child_type)))
1467114687 return ira->codegen->builtin_types.entry_invalid;
1467214688 TypeUnionField *field = find_union_type_field(child_type, field_name);
1467314689 if (field) {
......@@ -15187,17 +15203,17 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,
1518715203 return ira->codegen->builtin_types.entry_void;
1518815204 }
1518915205
15190 bool *fast_math_off_ptr;
15206 bool *fast_math_on_ptr;
1519115207 AstNode **fast_math_set_node_ptr;
1519215208 if (target_type->id == TypeTableEntryIdBlock) {
1519315209 ScopeBlock *block_scope = (ScopeBlock *)target_val->data.x_block;
15194 fast_math_off_ptr = &block_scope->fast_math_off;
15210 fast_math_on_ptr = &block_scope->fast_math_on;
1519515211 fast_math_set_node_ptr = &block_scope->fast_math_set_node;
1519615212 } else if (target_type->id == TypeTableEntryIdFn) {
1519715213 assert(target_val->data.x_ptr.special == ConstPtrSpecialFunction);
1519815214 FnTableEntry *target_fn = target_val->data.x_ptr.data.fn.fn_entry;
1519915215 assert(target_fn->def_scope);
15200 fast_math_off_ptr = &target_fn->def_scope->fast_math_off;
15216 fast_math_on_ptr = &target_fn->def_scope->fast_math_on;
1520115217 fast_math_set_node_ptr = &target_fn->def_scope->fast_math_set_node;
1520215218 } else if (target_type->id == TypeTableEntryIdMetaType) {
1520315219 ScopeDecls *decls_scope;
......@@ -15213,7 +15229,7 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,
1521315229 buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&type_arg->name)));
1521415230 return ira->codegen->builtin_types.entry_invalid;
1521515231 }
15216 fast_math_off_ptr = &decls_scope->fast_math_off;
15232 fast_math_on_ptr = &decls_scope->fast_math_on;
1521715233 fast_math_set_node_ptr = &decls_scope->fast_math_set_node;
1521815234 } else {
1521915235 ir_add_error_node(ira, target_instruction->source_node,
......@@ -15235,7 +15251,7 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,
1523515251 return ira->codegen->builtin_types.entry_invalid;
1523615252 }
1523715253 *fast_math_set_node_ptr = source_node;
15238 *fast_math_off_ptr = (float_mode_scalar == FloatModeStrict);
15254 *fast_math_on_ptr = (float_mode_scalar == FloatModeOptimized);
1523915255
1524015256 ir_build_const_from(ira, &instruction->base);
1524115257 return ira->codegen->builtin_types.entry_void;
......@@ -15244,6 +15260,7 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,
1524415260static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
1524515261 IrInstructionSliceType *slice_type_instruction)
1524615262{
15263 Error err;
1524715264 uint32_t align_bytes;
1524815265 if (slice_type_instruction->align_value != nullptr) {
1524915266 if (!ir_resolve_align(ira, slice_type_instruction->align_value->other, &align_bytes))
......@@ -15255,6 +15272,8 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
1525515272 return ira->codegen->builtin_types.entry_invalid;
1525615273
1525715274 if (slice_type_instruction->align_value == nullptr) {
15275 if ((err = type_ensure_zero_bits_known(ira->codegen, child_type)))
15276 return ira->codegen->builtin_types.entry_invalid;
1525815277 align_bytes = get_abi_alignment(ira->codegen, child_type);
1525915278 }
1526015279
......@@ -15293,7 +15312,8 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
1529315312 case TypeTableEntryIdBoundFn:
1529415313 case TypeTableEntryIdPromise:
1529515314 {
15296 type_ensure_zero_bits_known(ira->codegen, child_type);
15315 if ((err = type_ensure_zero_bits_known(ira->codegen, child_type)))
15316 return ira->codegen->builtin_types.entry_invalid;
1529715317 TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type,
1529815318 is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0);
1529915319 TypeTableEntry *result_type = get_slice_type(ira->codegen, slice_ptr_type);
......@@ -15431,11 +15451,11 @@ static TypeTableEntry *ir_analyze_instruction_promise_type(IrAnalyze *ira, IrIns
1543115451static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
1543215452 IrInstructionSizeOf *size_of_instruction)
1543315453{
15454 Error err;
1543415455 IrInstruction *type_value = size_of_instruction->type_value->other;
1543515456 TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
1543615457
15437 ensure_complete_type(ira->codegen, type_entry);
15438 if (type_is_invalid(type_entry))
15458 if ((err = ensure_complete_type(ira->codegen, type_entry)))
1543915459 return ira->codegen->builtin_types.entry_invalid;
1544015460
1544115461 switch (type_entry->id) {
......@@ -15806,6 +15826,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
1580615826static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
1580715827 IrInstructionSwitchTarget *switch_target_instruction)
1580815828{
15829 Error err;
1580915830 IrInstruction *target_value_ptr = switch_target_instruction->target_value_ptr->other;
1581015831 if (type_is_invalid(target_value_ptr->value.type))
1581115832 return ira->codegen->builtin_types.entry_invalid;
......@@ -15832,8 +15853,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
1583215853 if (pointee_val->special == ConstValSpecialRuntime)
1583315854 pointee_val = nullptr;
1583415855 }
15835 ensure_complete_type(ira->codegen, target_type);
15836 if (type_is_invalid(target_type))
15856 if ((err = ensure_complete_type(ira->codegen, target_type)))
1583715857 return ira->codegen->builtin_types.entry_invalid;
1583815858
1583915859 switch (target_type->id) {
......@@ -15897,8 +15917,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
1589715917 return tag_type;
1589815918 }
1589915919 case TypeTableEntryIdEnum: {
15900 type_ensure_zero_bits_known(ira->codegen, target_type);
15901 if (type_is_invalid(target_type))
15920 if ((err = type_ensure_zero_bits_known(ira->codegen, target_type)))
1590215921 return ira->codegen->builtin_types.entry_invalid;
1590315922 if (target_type->data.enumeration.src_field_count < 2) {
1590415923 TypeEnumField *only_field = &target_type->data.enumeration.fields[0];
......@@ -16100,10 +16119,10 @@ static TypeTableEntry *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionR
1610016119static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction,
1610116120 TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
1610216121{
16122 Error err;
1610316123 assert(container_type->id == TypeTableEntryIdUnion);
1610416124
16105 ensure_complete_type(ira->codegen, container_type);
16106 if (type_is_invalid(container_type))
16125 if ((err = ensure_complete_type(ira->codegen, container_type)))
1610716126 return ira->codegen->builtin_types.entry_invalid;
1610816127
1610916128 if (instr_field_count != 1) {
......@@ -16132,8 +16151,7 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir
1613216151 if (casted_field_value == ira->codegen->invalid_instruction)
1613316152 return ira->codegen->builtin_types.entry_invalid;
1613416153
16135 type_ensure_zero_bits_known(ira->codegen, casted_field_value->value.type);
16136 if (type_is_invalid(casted_field_value->value.type))
16154 if ((err = type_ensure_zero_bits_known(ira->codegen, casted_field_value->value.type)))
1613716155 return ira->codegen->builtin_types.entry_invalid;
1613816156
1613916157 bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);
......@@ -16167,6 +16185,7 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir
1616716185static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
1616816186 TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
1616916187{
16188 Error err;
1617016189 if (container_type->id == TypeTableEntryIdUnion) {
1617116190 return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields);
1617216191 }
......@@ -16177,8 +16196,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru
1617716196 return ira->codegen->builtin_types.entry_invalid;
1617816197 }
1617916198
16180 ensure_complete_type(ira->codegen, container_type);
16181 if (type_is_invalid(container_type))
16199 if ((err = ensure_complete_type(ira->codegen, container_type)))
1618216200 return ira->codegen->builtin_types.entry_invalid;
1618316201
1618416202 size_t actual_field_count = container_type->data.structure.src_field_count;
......@@ -16559,6 +16577,7 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc
1655916577}
1656016578
1656116579static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructionTagName *instruction) {
16580 Error err;
1656216581 IrInstruction *target = instruction->target->other;
1656316582 if (type_is_invalid(target->value.type))
1656416583 return ira->codegen->builtin_types.entry_invalid;
......@@ -16566,8 +16585,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn
1656616585 assert(target->value.type->id == TypeTableEntryIdEnum);
1656716586
1656816587 if (instr_is_comptime(target)) {
16569 type_ensure_zero_bits_known(ira->codegen, target->value.type);
16570 if (type_is_invalid(target->value.type))
16588 if ((err = type_ensure_zero_bits_known(ira->codegen, target->value.type)))
1657116589 return ira->codegen->builtin_types.entry_invalid;
1657216590 TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint);
1657316591 ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name);
......@@ -16591,6 +16609,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn
1659116609static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
1659216610 IrInstructionFieldParentPtr *instruction)
1659316611{
16612 Error err;
1659416613 IrInstruction *type_value = instruction->type_value->other;
1659516614 TypeTableEntry *container_type = ir_resolve_type(ira, type_value);
1659616615 if (type_is_invalid(container_type))
......@@ -16611,8 +16630,7 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
1661116630 return ira->codegen->builtin_types.entry_invalid;
1661216631 }
1661316632
16614 ensure_complete_type(ira->codegen, container_type);
16615 if (type_is_invalid(container_type))
16633 if ((err = ensure_complete_type(ira->codegen, container_type)))
1661616634 return ira->codegen->builtin_types.entry_invalid;
1661716635
1661816636 TypeStructField *field = find_struct_type_field(container_type, field_name);
......@@ -16684,13 +16702,13 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
1668416702static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira,
1668516703 IrInstructionOffsetOf *instruction)
1668616704{
16705 Error err;
1668716706 IrInstruction *type_value = instruction->type_value->other;
1668816707 TypeTableEntry *container_type = ir_resolve_type(ira, type_value);
1668916708 if (type_is_invalid(container_type))
1669016709 return ira->codegen->builtin_types.entry_invalid;
1669116710
16692 ensure_complete_type(ira->codegen, container_type);
16693 if (type_is_invalid(container_type))
16711 if ((err = ensure_complete_type(ira->codegen, container_type)))
1669416712 return ira->codegen->builtin_types.entry_invalid;
1669516713
1669616714 IrInstruction *field_name_value = instruction->field_name->other;
......@@ -16735,19 +16753,15 @@ static void ensure_field_index(TypeTableEntry *type, const char *field_name, siz
1673516753 (buf_deinit(field_name_buf), true));
1673616754}
1673716755
16738static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, TypeTableEntry *root = nullptr)
16739{
16756static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, TypeTableEntry *root) {
16757 Error err;
1674016758 static ConstExprValue *type_info_var = nullptr;
1674116759 static TypeTableEntry *type_info_type = nullptr;
16742 if (type_info_var == nullptr)
16743 {
16760 if (type_info_var == nullptr) {
1674416761 type_info_var = get_builtin_value(ira->codegen, "TypeInfo");
1674516762 assert(type_info_var->type->id == TypeTableEntryIdMetaType);
1674616763
16747 ensure_complete_type(ira->codegen, type_info_var->data.x_type);
16748 if (type_is_invalid(type_info_var->data.x_type))
16749 return ira->codegen->builtin_types.entry_invalid;
16750
16764 assertNoError(ensure_complete_type(ira->codegen, type_info_var->data.x_type));
1675116765 type_info_type = type_info_var->data.x_type;
1675216766 assert(type_info_type->id == TypeTableEntryIdUnion);
1675316767 }
......@@ -16772,8 +16786,7 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na
1677216786
1677316787 VariableTableEntry *var = tld->var;
1677416788
16775 ensure_complete_type(ira->codegen, var->value->type);
16776 if (type_is_invalid(var->value->type))
16789 if ((err = ensure_complete_type(ira->codegen, var->value->type)))
1677716790 return ira->codegen->builtin_types.entry_invalid;
1677816791 assert(var->value->type->id == TypeTableEntryIdMetaType);
1677916792 return var->value->data.x_type;
......@@ -16781,9 +16794,9 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na
1678116794
1678216795static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope)
1678316796{
16784 TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition");
16785 ensure_complete_type(ira->codegen, type_info_definition_type);
16786 if (type_is_invalid(type_info_definition_type))
16797 Error err;
16798 TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition", nullptr);
16799 if ((err = ensure_complete_type(ira->codegen, type_info_definition_type)))
1678716800 return false;
1678816801
1678916802 ensure_field_index(type_info_definition_type, "name", 0);
......@@ -16791,18 +16804,15 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
1679116804 ensure_field_index(type_info_definition_type, "data", 2);
1679216805
1679316806 TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type);
16794 ensure_complete_type(ira->codegen, type_info_definition_data_type);
16795 if (type_is_invalid(type_info_definition_data_type))
16807 if ((err = ensure_complete_type(ira->codegen, type_info_definition_data_type)))
1679616808 return false;
1679716809
1679816810 TypeTableEntry *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type);
16799 ensure_complete_type(ira->codegen, type_info_fn_def_type);
16800 if (type_is_invalid(type_info_fn_def_type))
16811 if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_type)))
1680116812 return false;
1680216813
1680316814 TypeTableEntry *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type);
16804 ensure_complete_type(ira->codegen, type_info_fn_def_inline_type);
16805 if (type_is_invalid(type_info_fn_def_inline_type))
16815 if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_inline_type)))
1680616816 return false;
1680716817
1680816818 // Loop through our definitions once to figure out how many definitions we will generate info for.
......@@ -16882,8 +16892,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
1688216892 case TldIdVar:
1688316893 {
1688416894 VariableTableEntry *var = ((TldVar *)curr_entry->value)->var;
16885 ensure_complete_type(ira->codegen, var->value->type);
16886 if (type_is_invalid(var->value->type))
16895 if ((err = ensure_complete_type(ira->codegen, var->value->type)))
1688716896 return false;
1688816897
1688916898 if (var->value->type->id == TypeTableEntryIdMetaType)
......@@ -16940,7 +16949,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
1694016949 // calling_convention: TypeInfo.CallingConvention
1694116950 ensure_field_index(fn_def_val->type, "calling_convention", 2);
1694216951 fn_def_fields[2].special = ConstValSpecialStatic;
16943 fn_def_fields[2].type = ir_type_info_get_type(ira, "CallingConvention");
16952 fn_def_fields[2].type = ir_type_info_get_type(ira, "CallingConvention", nullptr);
1694416953 bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc);
1694516954 // is_var_args: bool
1694616955 ensure_field_index(fn_def_val->type, "is_var_args", 3);
......@@ -17014,8 +17023,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
1701417023 case TldIdContainer:
1701517024 {
1701617025 TypeTableEntry *type_entry = ((TldContainer *)curr_entry->value)->type_entry;
17017 ensure_complete_type(ira->codegen, type_entry);
17018 if (type_is_invalid(type_entry))
17026 if ((err = ensure_complete_type(ira->codegen, type_entry)))
1701917027 return false;
1702017028
1702117029 // This is a type.
......@@ -17041,12 +17049,67 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
1704117049 return true;
1704217050}
1704317051
17052static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, TypeTableEntry *ptr_type_entry) {
17053 TypeTableEntry *attrs_type;
17054 uint32_t size_enum_index;
17055 if (is_slice(ptr_type_entry)) {
17056 attrs_type = ptr_type_entry->data.structure.fields[slice_ptr_index].type_entry;
17057 size_enum_index = 2;
17058 } else if (ptr_type_entry->id == TypeTableEntryIdPointer) {
17059 attrs_type = ptr_type_entry;
17060 size_enum_index = (ptr_type_entry->data.pointer.ptr_len == PtrLenSingle) ? 0 : 1;
17061 } else {
17062 zig_unreachable();
17063 }
17064
17065 TypeTableEntry *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr);
17066 assertNoError(ensure_complete_type(ira->codegen, type_info_pointer_type));
17067
17068 ConstExprValue *result = create_const_vals(1);
17069 result->special = ConstValSpecialStatic;
17070 result->type = type_info_pointer_type;
17071
17072 ConstExprValue *fields = create_const_vals(5);
17073 result->data.x_struct.fields = fields;
17074
17075 // size: Size
17076 ensure_field_index(result->type, "size", 0);
17077 TypeTableEntry *type_info_pointer_size_type = ir_type_info_get_type(ira, "Size", type_info_pointer_type);
17078 assertNoError(ensure_complete_type(ira->codegen, type_info_pointer_size_type));
17079 fields[0].special = ConstValSpecialStatic;
17080 fields[0].type = type_info_pointer_size_type;
17081 bigint_init_unsigned(&fields[0].data.x_enum_tag, size_enum_index);
17082
17083 // is_const: bool
17084 ensure_field_index(result->type, "is_const", 1);
17085 fields[1].special = ConstValSpecialStatic;
17086 fields[1].type = ira->codegen->builtin_types.entry_bool;
17087 fields[1].data.x_bool = attrs_type->data.pointer.is_const;
17088 // is_volatile: bool
17089 ensure_field_index(result->type, "is_volatile", 2);
17090 fields[2].special = ConstValSpecialStatic;
17091 fields[2].type = ira->codegen->builtin_types.entry_bool;
17092 fields[2].data.x_bool = attrs_type->data.pointer.is_volatile;
17093 // alignment: u32
17094 ensure_field_index(result->type, "alignment", 3);
17095 fields[3].special = ConstValSpecialStatic;
17096 fields[3].type = get_int_type(ira->codegen, false, 29);
17097 bigint_init_unsigned(&fields[3].data.x_bigint, attrs_type->data.pointer.alignment);
17098 // child: type
17099 ensure_field_index(result->type, "child", 4);
17100 fields[4].special = ConstValSpecialStatic;
17101 fields[4].type = ira->codegen->builtin_types.entry_type;
17102 fields[4].data.x_type = attrs_type->data.pointer.child_type;
17103
17104 return result;
17105};
17106
1704417107static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) {
17108 Error err;
1704517109 assert(type_entry != nullptr);
1704617110 assert(!type_is_invalid(type_entry));
1704717111
17048 ensure_complete_type(ira->codegen, type_entry);
17049 if (type_is_invalid(type_entry))
17112 if ((err = ensure_complete_type(ira->codegen, type_entry)))
1705017113 return nullptr;
1705117114
1705217115 const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field,
......@@ -17066,63 +17129,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1706617129 enum_field_val->data.x_struct.fields = inner_fields;
1706717130 };
1706817131
17069 const auto create_ptr_like_type_info = [ira](TypeTableEntry *ptr_type_entry) {
17070 TypeTableEntry *attrs_type;
17071 uint32_t size_enum_index;
17072 if (is_slice(ptr_type_entry)) {
17073 attrs_type = ptr_type_entry->data.structure.fields[slice_ptr_index].type_entry;
17074 size_enum_index = 2;
17075 } else if (ptr_type_entry->id == TypeTableEntryIdPointer) {
17076 attrs_type = ptr_type_entry;
17077 size_enum_index = (ptr_type_entry->data.pointer.ptr_len == PtrLenSingle) ? 0 : 1;
17078 } else {
17079 zig_unreachable();
17080 }
17081
17082 TypeTableEntry *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer");
17083 ensure_complete_type(ira->codegen, type_info_pointer_type);
17084 assert(!type_is_invalid(type_info_pointer_type));
17085
17086 ConstExprValue *result = create_const_vals(1);
17087 result->special = ConstValSpecialStatic;
17088 result->type = type_info_pointer_type;
17089
17090 ConstExprValue *fields = create_const_vals(5);
17091 result->data.x_struct.fields = fields;
17092
17093 // size: Size
17094 ensure_field_index(result->type, "size", 0);
17095 TypeTableEntry *type_info_pointer_size_type = ir_type_info_get_type(ira, "Size", type_info_pointer_type);
17096 ensure_complete_type(ira->codegen, type_info_pointer_size_type);
17097 assert(!type_is_invalid(type_info_pointer_size_type));
17098 fields[0].special = ConstValSpecialStatic;
17099 fields[0].type = type_info_pointer_size_type;
17100 bigint_init_unsigned(&fields[0].data.x_enum_tag, size_enum_index);
17101
17102 // is_const: bool
17103 ensure_field_index(result->type, "is_const", 1);
17104 fields[1].special = ConstValSpecialStatic;
17105 fields[1].type = ira->codegen->builtin_types.entry_bool;
17106 fields[1].data.x_bool = attrs_type->data.pointer.is_const;
17107 // is_volatile: bool
17108 ensure_field_index(result->type, "is_volatile", 2);
17109 fields[2].special = ConstValSpecialStatic;
17110 fields[2].type = ira->codegen->builtin_types.entry_bool;
17111 fields[2].data.x_bool = attrs_type->data.pointer.is_volatile;
17112 // alignment: u32
17113 ensure_field_index(result->type, "alignment", 3);
17114 fields[3].special = ConstValSpecialStatic;
17115 fields[3].type = ira->codegen->builtin_types.entry_u32;
17116 bigint_init_unsigned(&fields[3].data.x_bigint, attrs_type->data.pointer.alignment);
17117 // child: type
17118 ensure_field_index(result->type, "child", 4);
17119 fields[4].special = ConstValSpecialStatic;
17120 fields[4].type = ira->codegen->builtin_types.entry_type;
17121 fields[4].data.x_type = attrs_type->data.pointer.child_type;
17122
17123 return result;
17124 };
17125
1712617132 if (type_entry == ira->codegen->builtin_types.entry_global_error_set) {
1712717133 zig_panic("TODO implement @typeInfo for global error set");
1712817134 }
......@@ -17158,7 +17164,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1715817164 {
1715917165 result = create_const_vals(1);
1716017166 result->special = ConstValSpecialStatic;
17161 result->type = ir_type_info_get_type(ira, "Int");
17167 result->type = ir_type_info_get_type(ira, "Int", nullptr);
1716217168
1716317169 ConstExprValue *fields = create_const_vals(2);
1716417170 result->data.x_struct.fields = fields;
......@@ -17180,7 +17186,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1718017186 {
1718117187 result = create_const_vals(1);
1718217188 result->special = ConstValSpecialStatic;
17183 result->type = ir_type_info_get_type(ira, "Float");
17189 result->type = ir_type_info_get_type(ira, "Float", nullptr);
1718417190
1718517191 ConstExprValue *fields = create_const_vals(1);
1718617192 result->data.x_struct.fields = fields;
......@@ -17195,14 +17201,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1719517201 }
1719617202 case TypeTableEntryIdPointer:
1719717203 {
17198 result = create_ptr_like_type_info(type_entry);
17204 result = create_ptr_like_type_info(ira, type_entry);
1719917205 break;
1720017206 }
1720117207 case TypeTableEntryIdArray:
1720217208 {
1720317209 result = create_const_vals(1);
1720417210 result->special = ConstValSpecialStatic;
17205 result->type = ir_type_info_get_type(ira, "Array");
17211 result->type = ir_type_info_get_type(ira, "Array", nullptr);
1720617212
1720717213 ConstExprValue *fields = create_const_vals(2);
1720817214 result->data.x_struct.fields = fields;
......@@ -17224,7 +17230,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1722417230 {
1722517231 result = create_const_vals(1);
1722617232 result->special = ConstValSpecialStatic;
17227 result->type = ir_type_info_get_type(ira, "Optional");
17233 result->type = ir_type_info_get_type(ira, "Optional", nullptr);
1722817234
1722917235 ConstExprValue *fields = create_const_vals(1);
1723017236 result->data.x_struct.fields = fields;
......@@ -17241,7 +17247,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1724117247 {
1724217248 result = create_const_vals(1);
1724317249 result->special = ConstValSpecialStatic;
17244 result->type = ir_type_info_get_type(ira, "Promise");
17250 result->type = ir_type_info_get_type(ira, "Promise", nullptr);
1724517251
1724617252 ConstExprValue *fields = create_const_vals(1);
1724717253 result->data.x_struct.fields = fields;
......@@ -17267,7 +17273,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1726717273 {
1726817274 result = create_const_vals(1);
1726917275 result->special = ConstValSpecialStatic;
17270 result->type = ir_type_info_get_type(ira, "Enum");
17276 result->type = ir_type_info_get_type(ira, "Enum", nullptr);
1727117277
1727217278 ConstExprValue *fields = create_const_vals(4);
1727317279 result->data.x_struct.fields = fields;
......@@ -17275,7 +17281,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1727517281 // layout: ContainerLayout
1727617282 ensure_field_index(result->type, "layout", 0);
1727717283 fields[0].special = ConstValSpecialStatic;
17278 fields[0].type = ir_type_info_get_type(ira, "ContainerLayout");
17284 fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr);
1727917285 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.enumeration.layout);
1728017286 // tag_type: type
1728117287 ensure_field_index(result->type, "tag_type", 1);
......@@ -17285,7 +17291,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1728517291 // fields: []TypeInfo.EnumField
1728617292 ensure_field_index(result->type, "fields", 2);
1728717293
17288 TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField");
17294 TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr);
1728917295 uint32_t enum_field_count = type_entry->data.enumeration.src_field_count;
1729017296
1729117297 ConstExprValue *enum_field_array = create_const_vals(1);
......@@ -17317,7 +17323,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1731717323 {
1731817324 result = create_const_vals(1);
1731917325 result->special = ConstValSpecialStatic;
17320 result->type = ir_type_info_get_type(ira, "ErrorSet");
17326 result->type = ir_type_info_get_type(ira, "ErrorSet", nullptr);
1732117327
1732217328 ConstExprValue *fields = create_const_vals(1);
1732317329 result->data.x_struct.fields = fields;
......@@ -17325,7 +17331,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1732517331 // errors: []TypeInfo.Error
1732617332 ensure_field_index(result->type, "errors", 0);
1732717333
17328 TypeTableEntry *type_info_error_type = ir_type_info_get_type(ira, "Error");
17334 TypeTableEntry *type_info_error_type = ir_type_info_get_type(ira, "Error", nullptr);
1732917335 uint32_t error_count = type_entry->data.error_set.err_count;
1733017336 ConstExprValue *error_array = create_const_vals(1);
1733117337 error_array->special = ConstValSpecialStatic;
......@@ -17367,7 +17373,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1736717373 {
1736817374 result = create_const_vals(1);
1736917375 result->special = ConstValSpecialStatic;
17370 result->type = ir_type_info_get_type(ira, "ErrorUnion");
17376 result->type = ir_type_info_get_type(ira, "ErrorUnion", nullptr);
1737117377
1737217378 ConstExprValue *fields = create_const_vals(2);
1737317379 result->data.x_struct.fields = fields;
......@@ -17390,7 +17396,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1739017396 {
1739117397 result = create_const_vals(1);
1739217398 result->special = ConstValSpecialStatic;
17393 result->type = ir_type_info_get_type(ira, "Union");
17399 result->type = ir_type_info_get_type(ira, "Union", nullptr);
1739417400
1739517401 ConstExprValue *fields = create_const_vals(4);
1739617402 result->data.x_struct.fields = fields;
......@@ -17398,7 +17404,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1739817404 // layout: ContainerLayout
1739917405 ensure_field_index(result->type, "layout", 0);
1740017406 fields[0].special = ConstValSpecialStatic;
17401 fields[0].type = ir_type_info_get_type(ira, "ContainerLayout");
17407 fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr);
1740217408 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.unionation.layout);
1740317409 // tag_type: ?type
1740417410 ensure_field_index(result->type, "tag_type", 1);
......@@ -17420,7 +17426,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1742017426 // fields: []TypeInfo.UnionField
1742117427 ensure_field_index(result->type, "fields", 2);
1742217428
17423 TypeTableEntry *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField");
17429 TypeTableEntry *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField", nullptr);
1742417430 uint32_t union_field_count = type_entry->data.unionation.src_field_count;
1742517431
1742617432 ConstExprValue *union_field_array = create_const_vals(1);
......@@ -17432,7 +17438,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1743217438
1743317439 init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false);
1743417440
17435 TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField");
17441 TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr);
1743617442
1743717443 for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) {
1743817444 TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];
......@@ -17474,13 +17480,13 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1747417480 case TypeTableEntryIdStruct:
1747517481 {
1747617482 if (type_entry->data.structure.is_slice) {
17477 result = create_ptr_like_type_info(type_entry);
17483 result = create_ptr_like_type_info(ira, type_entry);
1747817484 break;
1747917485 }
1748017486
1748117487 result = create_const_vals(1);
1748217488 result->special = ConstValSpecialStatic;
17483 result->type = ir_type_info_get_type(ira, "Struct");
17489 result->type = ir_type_info_get_type(ira, "Struct", nullptr);
1748417490
1748517491 ConstExprValue *fields = create_const_vals(3);
1748617492 result->data.x_struct.fields = fields;
......@@ -17488,12 +17494,12 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1748817494 // layout: ContainerLayout
1748917495 ensure_field_index(result->type, "layout", 0);
1749017496 fields[0].special = ConstValSpecialStatic;
17491 fields[0].type = ir_type_info_get_type(ira, "ContainerLayout");
17497 fields[0].type = ir_type_info_get_type(ira, "ContainerLayout", nullptr);
1749217498 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.structure.layout);
1749317499 // fields: []TypeInfo.StructField
1749417500 ensure_field_index(result->type, "fields", 1);
1749517501
17496 TypeTableEntry *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField");
17502 TypeTableEntry *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField", nullptr);
1749717503 uint32_t struct_field_count = type_entry->data.structure.src_field_count;
1749817504
1749917505 ConstExprValue *struct_field_array = create_const_vals(1);
......@@ -17549,7 +17555,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1754917555 {
1755017556 result = create_const_vals(1);
1755117557 result->special = ConstValSpecialStatic;
17552 result->type = ir_type_info_get_type(ira, "Fn");
17558 result->type = ir_type_info_get_type(ira, "Fn", nullptr);
1755317559
1755417560 ConstExprValue *fields = create_const_vals(6);
1755517561 result->data.x_struct.fields = fields;
......@@ -17557,7 +17563,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1755717563 // calling_convention: TypeInfo.CallingConvention
1755817564 ensure_field_index(result->type, "calling_convention", 0);
1755917565 fields[0].special = ConstValSpecialStatic;
17560 fields[0].type = ir_type_info_get_type(ira, "CallingConvention");
17566 fields[0].type = ir_type_info_get_type(ira, "CallingConvention", nullptr);
1756117567 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
1756217568 // is_generic: bool
1756317569 ensure_field_index(result->type, "is_generic", 1);
......@@ -17598,7 +17604,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1759817604 fields[4].data.x_optional = async_alloc_type;
1759917605 }
1760017606 // args: []TypeInfo.FnArg
17601 TypeTableEntry *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg");
17607 TypeTableEntry *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr);
1760217608 size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count -
1760317609 (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC);
1760417610
......@@ -17673,7 +17679,7 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira,
1767317679 if (type_is_invalid(type_entry))
1767417680 return ira->codegen->builtin_types.entry_invalid;
1767517681
17676 TypeTableEntry *result_type = ir_type_info_get_type(ira, nullptr);
17682 TypeTableEntry *result_type = ir_type_info_get_type(ira, nullptr, nullptr);
1767717683
1767817684 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
1767917685 out_val->type = result_type;
......@@ -18883,13 +18889,13 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
1888318889}
1888418890
1888518891static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInstructionMemberCount *instruction) {
18892 Error err;
1888618893 IrInstruction *container = instruction->container->other;
1888718894 if (type_is_invalid(container->value.type))
1888818895 return ira->codegen->builtin_types.entry_invalid;
1888918896 TypeTableEntry *container_type = ir_resolve_type(ira, container);
1889018897
18891 ensure_complete_type(ira->codegen, container_type);
18892 if (type_is_invalid(container_type))
18898 if ((err = ensure_complete_type(ira->codegen, container_type)))
1889318899 return ira->codegen->builtin_types.entry_invalid;
1889418900
1889518901 uint64_t result;
......@@ -18921,13 +18927,13 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns
1892118927}
1892218928
1892318929static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstructionMemberType *instruction) {
18930 Error err;
1892418931 IrInstruction *container_type_value = instruction->container_type->other;
1892518932 TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);
1892618933 if (type_is_invalid(container_type))
1892718934 return ira->codegen->builtin_types.entry_invalid;
1892818935
18929 ensure_complete_type(ira->codegen, container_type);
18930 if (type_is_invalid(container_type))
18936 if ((err = ensure_complete_type(ira->codegen, container_type)))
1893118937 return ira->codegen->builtin_types.entry_invalid;
1893218938
1893318939
......@@ -18968,13 +18974,13 @@ static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInst
1896818974}
1896918975
1897018976static TypeTableEntry *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstructionMemberName *instruction) {
18977 Error err;
1897118978 IrInstruction *container_type_value = instruction->container_type->other;
1897218979 TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);
1897318980 if (type_is_invalid(container_type))
1897418981 return ira->codegen->builtin_types.entry_invalid;
1897518982
18976 ensure_complete_type(ira->codegen, container_type);
18977 if (type_is_invalid(container_type))
18983 if ((err = ensure_complete_type(ira->codegen, container_type)))
1897818984 return ira->codegen->builtin_types.entry_invalid;
1897918985
1898018986 uint64_t member_index;
......@@ -19055,13 +19061,13 @@ static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructi
1905519061}
1905619062
1905719063static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
19064 Error err;
1905819065 IrInstruction *type_value = instruction->type_value->other;
1905919066 if (type_is_invalid(type_value->value.type))
1906019067 return ira->codegen->builtin_types.entry_invalid;
1906119068 TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
1906219069
19063 type_ensure_zero_bits_known(ira->codegen, type_entry);
19064 if (type_is_invalid(type_entry))
19070 if ((err = type_ensure_zero_bits_known(ira->codegen, type_entry)))
1906519071 return ira->codegen->builtin_types.entry_invalid;
1906619072
1906719073 switch (type_entry->id) {
......@@ -19917,6 +19923,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
1991719923}
1991819924
1991919925static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) {
19926 Error err;
1992019927 IrInstruction *dest_type_value = instruction->dest_type->other;
1992119928 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
1992219929 if (type_is_invalid(dest_type))
......@@ -19927,12 +19934,10 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc
1992719934 if (type_is_invalid(src_type))
1992819935 return ira->codegen->builtin_types.entry_invalid;
1992919936
19930 ensure_complete_type(ira->codegen, dest_type);
19931 if (type_is_invalid(dest_type))
19937 if ((err = ensure_complete_type(ira->codegen, dest_type)))
1993219938 return ira->codegen->builtin_types.entry_invalid;
1993319939
19934 ensure_complete_type(ira->codegen, src_type);
19935 if (type_is_invalid(src_type))
19940 if ((err = ensure_complete_type(ira->codegen, src_type)))
1993619941 return ira->codegen->builtin_types.entry_invalid;
1993719942
1993819943 if (get_codegen_ptr_type(src_type) != nullptr) {
......@@ -20018,6 +20023,7 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc
2001820023}
2001920024
2002020025static TypeTableEntry *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) {
20026 Error err;
2002120027 IrInstruction *dest_type_value = instruction->dest_type->other;
2002220028 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
2002320029 if (type_is_invalid(dest_type))
......@@ -20028,7 +20034,8 @@ static TypeTableEntry *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstr
2002820034 return ira->codegen->builtin_types.entry_invalid;
2002920035 }
2003020036
20031 type_ensure_zero_bits_known(ira->codegen, dest_type);
20037 if ((err = type_ensure_zero_bits_known(ira->codegen, dest_type)))
20038 return ira->codegen->builtin_types.entry_invalid;
2003220039 if (!type_has_bits(dest_type)) {
2003320040 ir_add_error(ira, dest_type_value,
2003420041 buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name)));
......@@ -20161,6 +20168,7 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr
2016120168}
2016220169
2016320170static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtrType *instruction) {
20171 Error err;
2016420172 TypeTableEntry *child_type = ir_resolve_type(ira, instruction->child_type->other);
2016520173 if (type_is_invalid(child_type))
2016620174 return ira->codegen->builtin_types.entry_invalid;
......@@ -20178,8 +20186,7 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruc
2017820186 if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes))
2017920187 return ira->codegen->builtin_types.entry_invalid;
2018020188 } else {
20181 type_ensure_zero_bits_known(ira->codegen, child_type);
20182 if (type_is_invalid(child_type))
20189 if ((err = type_ensure_zero_bits_known(ira->codegen, child_type)))
2018320190 return ira->codegen->builtin_types.entry_invalid;
2018420191 align_bytes = get_abi_alignment(ira->codegen, child_type);
2018520192 }
......@@ -20299,22 +20306,21 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc
2029920306}
2030020307
2030120308static TypeTableEntry *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstructionTagType *instruction) {
20309 Error err;
2030220310 IrInstruction *target_inst = instruction->target->other;
2030320311 TypeTableEntry *enum_type = ir_resolve_type(ira, target_inst);
2030420312 if (type_is_invalid(enum_type))
2030520313 return ira->codegen->builtin_types.entry_invalid;
2030620314
2030720315 if (enum_type->id == TypeTableEntryIdEnum) {
20308 ensure_complete_type(ira->codegen, enum_type);
20309 if (type_is_invalid(enum_type))
20316 if ((err = ensure_complete_type(ira->codegen, enum_type)))
2031020317 return ira->codegen->builtin_types.entry_invalid;
2031120318
2031220319 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
2031320320 out_val->data.x_type = enum_type->data.enumeration.tag_int_type;
2031420321 return ira->codegen->builtin_types.entry_type;
2031520322 } else if (enum_type->id == TypeTableEntryIdUnion) {
20316 ensure_complete_type(ira->codegen, enum_type);
20317 if (type_is_invalid(enum_type))
20323 if ((err = ensure_complete_type(ira->codegen, enum_type)))
2031820324 return ira->codegen->builtin_types.entry_invalid;
2031920325
2032020326 AstNode *decl_node = enum_type->data.unionation.decl_node;
......@@ -20591,7 +20597,7 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr
2059120597 return ira->codegen->builtin_types.entry_invalid;
2059220598
2059320599 IrInstruction *casted_operand = ir_implicit_cast(ira, operand, operand_type);
20594 if (type_is_invalid(casted_ptr->value.type))
20600 if (type_is_invalid(casted_operand->value.type))
2059520601 return ira->codegen->builtin_types.entry_invalid;
2059620602
2059720603 AtomicOrder ordering;
......@@ -20817,6 +20823,7 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction
2081720823}
2081820824
2081920825static TypeTableEntry *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) {
20826 Error err;
2082020827 IrInstruction *target = instruction->target->other;
2082120828 if (type_is_invalid(target->value.type))
2082220829 return ira->codegen->builtin_types.entry_invalid;
......@@ -20827,8 +20834,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInst
2082720834 return ira->codegen->builtin_types.entry_invalid;
2082820835 }
2082920836
20830 type_ensure_zero_bits_known(ira->codegen, target->value.type);
20831 if (type_is_invalid(target->value.type))
20837 if ((err = type_ensure_zero_bits_known(ira->codegen, target->value.type)))
2083220838 return ira->codegen->builtin_types.entry_invalid;
2083320839
2083420840 TypeTableEntry *tag_type = target->value.type->data.enumeration.tag_int_type;
......@@ -20839,6 +20845,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInst
2083920845}
2084020846
2084120847static TypeTableEntry *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) {
20848 Error err;
2084220849 IrInstruction *dest_type_value = instruction->dest_type->other;
2084320850 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
2084420851 if (type_is_invalid(dest_type))
......@@ -20850,8 +20857,7 @@ static TypeTableEntry *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInst
2085020857 return ira->codegen->builtin_types.entry_invalid;
2085120858 }
2085220859
20853 type_ensure_zero_bits_known(ira->codegen, dest_type);
20854 if (type_is_invalid(dest_type))
20860 if ((err = type_ensure_zero_bits_known(ira->codegen, dest_type)))
2085520861 return ira->codegen->builtin_types.entry_invalid;
2085620862
2085720863 TypeTableEntry *tag_type = dest_type->data.enumeration.tag_int_type;
src/result.hpp created+36
......@@ -0,0 +1,36 @@
1/*
2 * Copyright (c) 2018 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#ifndef ZIG_RESULT_HPP
9#define ZIG_RESULT_HPP
10
11#include "error.hpp"
12
13#include <assert.h>
14
15static inline void assertNoError(Error err) {
16 assert(err == ErrorNone);
17}
18
19template<typename T>
20struct Result {
21 T data;
22 Error err;
23
24 Result(T x) : data(x), err(ErrorNone) {}
25
26 Result(Error err) : err(err) {
27 assert(err != ErrorNone);
28 }
29
30 T unwrap() {
31 assert(err == ErrorNone);
32 return data;
33 }
34};
35
36#endif
src/translate_c.cpp+30-11
......@@ -2759,7 +2759,9 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
27592759 AstNode *child_statement;
27602760 child_scope = trans_stmt(c, &child_block_scope->base, stmt->getBody(), &child_statement);
27612761 if (child_scope == nullptr) return nullptr;
2762 body_node->data.block.statements.append(child_statement);
2762 if (child_statement != nullptr) {
2763 body_node->data.block.statements.append(child_statement);
2764 }
27632765 }
27642766
27652767 // if (!cond) break;
......@@ -2769,6 +2771,7 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
27692771 terminator_node->data.if_bool_expr.condition = trans_create_node_prefix_op(c, PrefixOpBoolNot, condition_node);
27702772 terminator_node->data.if_bool_expr.then_block = trans_create_node(c, NodeTypeBreak);
27712773
2774 assert(terminator_node != nullptr);
27722775 body_node->data.block.statements.append(terminator_node);
27732776
27742777 while_scope->node->data.while_expr.body = body_node;
......@@ -2832,7 +2835,12 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt
28322835 TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(), &body_statement);
28332836 if (body_scope == nullptr)
28342837 return nullptr;
2835 while_scope->node->data.while_expr.body = body_statement;
2838
2839 if (body_statement == nullptr) {
2840 while_scope->node->data.while_expr.body = trans_create_node(c, NodeTypeBlock);
2841 } else {
2842 while_scope->node->data.while_expr.body = body_statement;
2843 }
28362844
28372845 return loop_block_node;
28382846}
......@@ -3067,9 +3075,14 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
30673075 trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt));
30683076 case Stmt::DeclStmtClass:
30693077 return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope);
3070 case Stmt::WhileStmtClass:
3071 return wrap_stmt(out_node, out_child_scope, scope,
3072 trans_while_loop(c, scope, (const WhileStmt *)stmt));
3078 case Stmt::WhileStmtClass: {
3079 AstNode *while_node = trans_while_loop(c, scope, (const WhileStmt *)stmt);
3080 assert(while_node->type == NodeTypeWhileExpr);
3081 if (while_node->data.while_expr.body == nullptr) {
3082 while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock);
3083 }
3084 return wrap_stmt(out_node, out_child_scope, scope, while_node);
3085 }
30733086 case Stmt::IfStmtClass:
30743087 return wrap_stmt(out_node, out_child_scope, scope,
30753088 trans_if_statement(c, scope, (const IfStmt *)stmt));
......@@ -3092,12 +3105,18 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
30923105 case Stmt::UnaryExprOrTypeTraitExprClass:
30933106 return wrap_stmt(out_node, out_child_scope, scope,
30943107 trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt));
3095 case Stmt::DoStmtClass:
3096 return wrap_stmt(out_node, out_child_scope, scope,
3097 trans_do_loop(c, scope, (const DoStmt *)stmt));
3098 case Stmt::ForStmtClass:
3099 return wrap_stmt(out_node, out_child_scope, scope,
3100 trans_for_loop(c, scope, (const ForStmt *)stmt));
3108 case Stmt::DoStmtClass: {
3109 AstNode *while_node = trans_do_loop(c, scope, (const DoStmt *)stmt);
3110 assert(while_node->type == NodeTypeWhileExpr);
3111 if (while_node->data.while_expr.body == nullptr) {
3112 while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock);
3113 }
3114 return wrap_stmt(out_node, out_child_scope, scope, while_node);
3115 }
3116 case Stmt::ForStmtClass: {
3117 AstNode *node = trans_for_loop(c, scope, (const ForStmt *)stmt);
3118 return wrap_stmt(out_node, out_child_scope, scope, node);
3119 }
31013120 case Stmt::StringLiteralClass:
31023121 return wrap_stmt(out_node, out_child_scope, scope,
31033122 trans_string_literal(c, scope, (const StringLiteral *)stmt));
src/util.hpp+2
......@@ -21,6 +21,7 @@
2121#define ATTRIBUTE_PRINTF(a, b)
2222#define ATTRIBUTE_RETURNS_NOALIAS __declspec(restrict)
2323#define ATTRIBUTE_NORETURN __declspec(noreturn)
24#define ATTRIBUTE_MUST_USE
2425
2526#else
2627
......@@ -28,6 +29,7 @@
2829#define ATTRIBUTE_PRINTF(a, b) __attribute__((format(printf, a, b)))
2930#define ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__))
3031#define ATTRIBUTE_NORETURN __attribute__((noreturn))
32#define ATTRIBUTE_MUST_USE __attribute__((warn_unused_result))
3133
3234#endif
3335
std/atomic/queue.zig+60-24
......@@ -1,40 +1,38 @@
1const std = @import("../index.zig");
12const builtin = @import("builtin");
23const AtomicOrder = builtin.AtomicOrder;
34const AtomicRmwOp = builtin.AtomicRmwOp;
5const assert = std.debug.assert;
46
57/// Many producer, many consumer, non-allocating, thread-safe.
6/// Uses a spinlock to protect get() and put().
8/// Uses a mutex to protect access.
79pub fn Queue(comptime T: type) type {
810 return struct {
911 head: ?*Node,
1012 tail: ?*Node,
11 lock: u8,
13 mutex: std.Mutex,
1214
1315 pub const Self = this;
14
15 pub const Node = struct {
16 next: ?*Node,
17 data: T,
18 };
16 pub const Node = std.LinkedList(T).Node;
1917
2018 pub fn init() Self {
2119 return Self{
2220 .head = null,
2321 .tail = null,
24 .lock = 0,
22 .mutex = std.Mutex.init(),
2523 };
2624 }
2725
2826 pub fn put(self: *Self, node: *Node) void {
2927 node.next = null;
3028
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);
29 const held = self.mutex.acquire();
30 defer held.release();
3331
34 const opt_tail = self.tail;
32 node.prev = self.tail;
3533 self.tail = node;
36 if (opt_tail) |tail| {
37 tail.next = node;
34 if (node.prev) |prev_tail| {
35 prev_tail.next = node;
3836 } else {
3937 assert(self.head == null);
4038 self.head = node;
......@@ -42,18 +40,27 @@ pub fn Queue(comptime T: type) type {
4240 }
4341
4442 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);
43 const held = self.mutex.acquire();
44 defer held.release();
4745
4846 const head = self.head orelse return null;
4947 self.head = head.next;
50 if (head.next == null) self.tail = null;
48 if (head.next) |new_head| {
49 new_head.prev = null;
50 } else {
51 self.tail = null;
52 }
53 // This way, a get() and a remove() are thread-safe with each other.
54 head.prev = null;
55 head.next = null;
5156 return head;
5257 }
5358
5459 pub fn unget(self: *Self, node: *Node) void {
55 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
56 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
60 node.prev = null;
61
62 const held = self.mutex.acquire();
63 defer held.release();
5764
5865 const opt_head = self.head;
5966 self.head = node;
......@@ -65,13 +72,39 @@ pub fn Queue(comptime T: type) type {
6572 }
6673 }
6774
75 /// Thread-safe with get() and remove(). Returns whether node was actually removed.
76 pub fn remove(self: *Self, node: *Node) bool {
77 const held = self.mutex.acquire();
78 defer held.release();
79
80 if (node.prev == null and node.next == null and self.head != node) {
81 return false;
82 }
83
84 if (node.prev) |prev| {
85 prev.next = node.next;
86 } else {
87 self.head = node.next;
88 }
89 if (node.next) |next| {
90 next.prev = node.prev;
91 } else {
92 self.tail = node.prev;
93 }
94 node.prev = null;
95 node.next = null;
96 return true;
97 }
98
6899 pub fn isEmpty(self: *Self) bool {
69 return @atomicLoad(?*Node, &self.head, builtin.AtomicOrder.SeqCst) != null;
100 const held = self.mutex.acquire();
101 defer held.release();
102 return self.head != null;
70103 }
71104
72105 pub fn dump(self: *Self) void {
73 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
74 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
106 const held = self.mutex.acquire();
107 defer held.release();
75108
76109 std.debug.warn("head: ");
77110 dumpRecursive(self.head, 0);
......@@ -93,9 +126,6 @@ pub fn Queue(comptime T: type) type {
93126 };
94127}
95128
96const std = @import("../index.zig");
97const assert = std.debug.assert;
98
99129const Context = struct {
100130 allocator: *std.mem.Allocator,
101131 queue: *Queue(i32),
......@@ -169,6 +199,7 @@ fn startPuts(ctx: *Context) u8 {
169199 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
170200 const x = @bitCast(i32, r.random.scalar(u32));
171201 const node = ctx.allocator.create(Queue(i32).Node{
202 .prev = undefined,
172203 .next = undefined,
173204 .data = x,
174205 }) catch unreachable;
......@@ -198,12 +229,14 @@ test "std.atomic.Queue single-threaded" {
198229 var node_0 = Queue(i32).Node{
199230 .data = 0,
200231 .next = undefined,
232 .prev = undefined,
201233 };
202234 queue.put(&node_0);
203235
204236 var node_1 = Queue(i32).Node{
205237 .data = 1,
206238 .next = undefined,
239 .prev = undefined,
207240 };
208241 queue.put(&node_1);
209242
......@@ -212,12 +245,14 @@ test "std.atomic.Queue single-threaded" {
212245 var node_2 = Queue(i32).Node{
213246 .data = 2,
214247 .next = undefined,
248 .prev = undefined,
215249 };
216250 queue.put(&node_2);
217251
218252 var node_3 = Queue(i32).Node{
219253 .data = 3,
220254 .next = undefined,
255 .prev = undefined,
221256 };
222257 queue.put(&node_3);
223258
......@@ -228,6 +263,7 @@ test "std.atomic.Queue single-threaded" {
228263 var node_4 = Queue(i32).Node{
229264 .data = 4,
230265 .next = undefined,
266 .prev = undefined,
231267 };
232268 queue.put(&node_4);
233269
std/build.zig+76-61
......@@ -267,7 +267,7 @@ pub const Builder = struct {
267267 if (self.verbose) {
268268 warn("rm {}\n", installed_file);
269269 }
270 _ = os.deleteFile(self.allocator, installed_file);
270 _ = os.deleteFile(installed_file);
271271 }
272272
273273 // TODO remove empty directories
......@@ -424,60 +424,69 @@ pub const Builder = struct {
424424 return mode;
425425 }
426426
427 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) bool {
428 if (self.user_input_options.put(name, UserInputOption{
429 .name = name,
430 .value = UserValue{ .Scalar = value },
431 .used = false,
432 }) catch unreachable) |*prev_value| {
433 // option already exists
434 switch (prev_value.value) {
435 UserValue.Scalar => |s| {
436 // turn it into a list
437 var list = ArrayList([]const u8).init(self.allocator);
438 list.append(s) catch unreachable;
439 list.append(value) catch unreachable;
440 _ = self.user_input_options.put(name, UserInputOption{
441 .name = name,
442 .value = UserValue{ .List = list },
443 .used = false,
444 }) catch unreachable;
445 },
446 UserValue.List => |*list| {
447 // append to the list
448 list.append(value) catch unreachable;
449 _ = self.user_input_options.put(name, UserInputOption{
450 .name = name,
451 .value = UserValue{ .List = list.* },
452 .used = false,
453 }) catch unreachable;
454 },
455 UserValue.Flag => {
456 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
457 return true;
458 },
459 }
427 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {
428 const gop = try self.user_input_options.getOrPut(name);
429 if (!gop.found_existing) {
430 gop.kv.value = UserInputOption{
431 .name = name,
432 .value = UserValue{ .Scalar = value },
433 .used = false,
434 };
435 return false;
436 }
437
438 // option already exists
439 switch (gop.kv.value.value) {
440 UserValue.Scalar => |s| {
441 // turn it into a list
442 var list = ArrayList([]const u8).init(self.allocator);
443 list.append(s) catch unreachable;
444 list.append(value) catch unreachable;
445 _ = self.user_input_options.put(name, UserInputOption{
446 .name = name,
447 .value = UserValue{ .List = list },
448 .used = false,
449 }) catch unreachable;
450 },
451 UserValue.List => |*list| {
452 // append to the list
453 list.append(value) catch unreachable;
454 _ = self.user_input_options.put(name, UserInputOption{
455 .name = name,
456 .value = UserValue{ .List = list.* },
457 .used = false,
458 }) catch unreachable;
459 },
460 UserValue.Flag => {
461 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
462 return true;
463 },
460464 }
461465 return false;
462466 }
463467
464 pub fn addUserInputFlag(self: *Builder, name: []const u8) bool {
465 if (self.user_input_options.put(name, UserInputOption{
466 .name = name,
467 .value = UserValue{ .Flag = {} },
468 .used = false,
469 }) catch unreachable) |*prev_value| {
470 switch (prev_value.value) {
471 UserValue.Scalar => |s| {
472 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
473 return true;
474 },
475 UserValue.List => {
476 warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name);
477 return true;
478 },
479 UserValue.Flag => {},
480 }
468 pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool {
469 const gop = try self.user_input_options.getOrPut(name);
470 if (!gop.found_existing) {
471 gop.kv.value = UserInputOption{
472 .name = name,
473 .value = UserValue{ .Flag = {} },
474 .used = false,
475 };
476 return false;
477 }
478
479 // option already exists
480 switch (gop.kv.value.value) {
481 UserValue.Scalar => |s| {
482 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
483 return true;
484 },
485 UserValue.List => {
486 warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name);
487 return true;
488 },
489 UserValue.Flag => {},
481490 }
482491 return false;
483492 }
......@@ -603,10 +612,10 @@ pub const Builder = struct {
603612 }
604613
605614 fn copyFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
606 return self.copyFileMode(source_path, dest_path, os.default_file_mode);
615 return self.copyFileMode(source_path, dest_path, os.File.default_mode);
607616 }
608617
609 fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: os.FileMode) !void {
618 fn copyFileMode(self: *Builder, source_path: []const u8, dest_path: []const u8, mode: os.File.Mode) !void {
610619 if (self.verbose) {
611620 warn("cp {} {}\n", source_path, dest_path);
612621 }
......@@ -1173,7 +1182,7 @@ pub const LibExeObjStep = struct {
11731182
11741183 if (self.build_options_contents.len() > 0) {
11751184 const build_options_file = try os.path.join(builder.allocator, builder.cache_root, builder.fmt("{}_build_options.zig", self.name));
1176 try std.io.writeFile(builder.allocator, build_options_file, self.build_options_contents.toSliceConst());
1185 try std.io.writeFile(build_options_file, self.build_options_contents.toSliceConst());
11771186 try zig_args.append("--pkg-begin");
11781187 try zig_args.append("build_options");
11791188 try zig_args.append(builder.pathFromRoot(build_options_file));
......@@ -1482,11 +1491,14 @@ pub const LibExeObjStep = struct {
14821491 }
14831492
14841493 if (!is_darwin) {
1485 const rpath_arg = builder.fmt("-Wl,-rpath,{}", os.path.real(builder.allocator, builder.pathFromRoot(builder.cache_root)) catch unreachable);
1494 const rpath_arg = builder.fmt("-Wl,-rpath,{}", try os.path.realAlloc(
1495 builder.allocator,
1496 builder.pathFromRoot(builder.cache_root),
1497 ));
14861498 defer builder.allocator.free(rpath_arg);
1487 cc_args.append(rpath_arg) catch unreachable;
1499 try cc_args.append(rpath_arg);
14881500
1489 cc_args.append("-rdynamic") catch unreachable;
1501 try cc_args.append("-rdynamic");
14901502 }
14911503
14921504 for (self.full_path_libs.toSliceConst()) |full_path_lib| {
......@@ -1557,11 +1569,14 @@ pub const LibExeObjStep = struct {
15571569 cc_args.append("-o") catch unreachable;
15581570 cc_args.append(output_path) catch unreachable;
15591571
1560 const rpath_arg = builder.fmt("-Wl,-rpath,{}", os.path.real(builder.allocator, builder.pathFromRoot(builder.cache_root)) catch unreachable);
1572 const rpath_arg = builder.fmt("-Wl,-rpath,{}", try os.path.realAlloc(
1573 builder.allocator,
1574 builder.pathFromRoot(builder.cache_root),
1575 ));
15611576 defer builder.allocator.free(rpath_arg);
1562 cc_args.append(rpath_arg) catch unreachable;
1577 try cc_args.append(rpath_arg);
15631578
1564 cc_args.append("-rdynamic") catch unreachable;
1579 try cc_args.append("-rdynamic");
15651580
15661581 {
15671582 var it = self.link_libs.iterator();
......@@ -1908,7 +1923,7 @@ pub const WriteFileStep = struct {
19081923 warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));
19091924 return err;
19101925 };
1911 io.writeFile(self.builder.allocator, full_path, self.data) catch |err| {
1926 io.writeFile(full_path, self.data) catch |err| {
19121927 warn("unable to write {}: {}\n", full_path, @errorName(err));
19131928 return err;
19141929 };
std/c/darwin.zig+38-8
......@@ -1,5 +1,8 @@
1const macho = @import("../macho.zig");
2
13extern "c" fn __error() *c_int;
24pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int;
5pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header;
36
47pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize;
58
......@@ -30,10 +33,45 @@ pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlen
3033pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
3134pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int;
3235
36pub extern "c" fn bind(socket: c_int, address: ?*const sockaddr, address_len: socklen_t) c_int;
37pub extern "c" fn socket(domain: c_int, type: c_int, protocol: c_int) c_int;
38
39/// The value of the link editor defined symbol _MH_EXECUTE_SYM is the address
40/// of the mach header in a Mach-O executable file type. It does not appear in
41/// any file type other than a MH_EXECUTE file type. The type of the symbol is
42/// absolute as the header is not part of any section.
43pub extern "c" var _mh_execute_header: if (@sizeOf(usize) == 8) mach_header_64 else mach_header;
44
45pub const mach_header_64 = macho.mach_header_64;
46pub const mach_header = macho.mach_header;
47
3348pub use @import("../os/darwin/errno.zig");
3449
3550pub const _errno = __error;
3651
52pub const in_port_t = u16;
53pub const sa_family_t = u8;
54pub const socklen_t = u32;
55pub const sockaddr = extern union {
56 in: sockaddr_in,
57 in6: sockaddr_in6,
58};
59pub const sockaddr_in = extern struct {
60 len: u8,
61 family: sa_family_t,
62 port: in_port_t,
63 addr: u32,
64 zero: [8]u8,
65};
66pub const sockaddr_in6 = extern struct {
67 len: u8,
68 family: sa_family_t,
69 port: in_port_t,
70 flowinfo: u32,
71 addr: [16]u8,
72 scope_id: u32,
73};
74
3775pub const timeval = extern struct {
3876 tv_sec: isize,
3977 tv_usec: isize,
......@@ -98,14 +136,6 @@ pub const dirent = extern struct {
98136 d_name: u8, // field address is address of first byte of name
99137};
100138
101pub const sockaddr = extern struct {
102 sa_len: u8,
103 sa_family: sa_family_t,
104 sa_data: [14]u8,
105};
106
107pub const sa_family_t = u8;
108
109139pub const pthread_attr_t = extern struct {
110140 __sig: c_long,
111141 __opaque: [56]u8,
std/c/index.zig+3
......@@ -21,8 +21,10 @@ pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize;
2121pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int;
2222pub extern "c" fn raise(sig: c_int) c_int;
2323pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize;
24pub extern "c" fn pread(fd: c_int, buf: *c_void, nbyte: usize, offset: u64) isize;
2425pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;
2526pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize;
27pub extern "c" fn pwrite(fd: c_int, buf: *const c_void, nbyte: usize, offset: u64) isize;
2628pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void;
2729pub extern "c" fn munmap(addr: *c_void, len: usize) c_int;
2830pub extern "c" fn unlink(path: [*]const u8) c_int;
......@@ -58,6 +60,7 @@ pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias at
5860pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int;
5961pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int;
6062pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;
63pub extern "pthread" fn pthread_self() pthread_t;
6164pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int;
6265
6366pub const pthread_t = *@OpaqueType();
std/c/linux.zig+3
......@@ -8,3 +8,6 @@ pub const pthread_attr_t = extern struct {
88 __size: [56]u8,
99 __align: c_long,
1010};
11
12/// See std.elf for constants for this
13pub extern fn getauxval(__type: c_ulong) c_ulong;
std/cstr.zig+6-5
......@@ -9,10 +9,9 @@ pub const line_sep = switch (builtin.os) {
99 else => "\n",
1010};
1111
12/// Deprecated, use mem.len
1213pub fn len(ptr: [*]const u8) usize {
13 var count: usize = 0;
14 while (ptr[count] != 0) : (count += 1) {}
15 return count;
14 return mem.len(u8, ptr);
1615}
1716
1817pub fn cmp(a: [*]const u8, b: [*]const u8) i8 {
......@@ -27,12 +26,14 @@ pub fn cmp(a: [*]const u8, b: [*]const u8) i8 {
2726 }
2827}
2928
29/// Deprecated, use mem.toSliceConst
3030pub fn toSliceConst(str: [*]const u8) []const u8 {
31 return str[0..len(str)];
31 return mem.toSliceConst(u8, str);
3232}
3333
34/// Deprecated, use mem.toSlice
3435pub fn toSlice(str: [*]u8) []u8 {
35 return str[0..len(str)];
36 return mem.toSlice(u8, str);
3637}
3738
3839test "cstr fns" {
std/debug/index.zig+636-159
......@@ -4,8 +4,8 @@ const mem = std.mem;
44const io = std.io;
55const os = std.os;
66const elf = std.elf;
7const DW = std.dwarf;
87const macho = std.macho;
8const DW = std.dwarf;
99const ArrayList = std.ArrayList;
1010const builtin = @import("builtin");
1111
......@@ -19,14 +19,19 @@ pub const runtime_safety = switch (builtin.mode) {
1919
2020/// Tries to write to stderr, unbuffered, and ignores any error returned.
2121/// Does not append a newline.
22/// TODO atomic/multithread support
2322var stderr_file: os.File = undefined;
2423var stderr_file_out_stream: io.FileOutStream = undefined;
24
25/// TODO multithreaded awareness
2526var stderr_stream: ?*io.OutStream(io.FileOutStream.Error) = null;
27var stderr_mutex = std.Mutex.init();
2628pub fn warn(comptime fmt: []const u8, args: ...) void {
29 const held = stderr_mutex.acquire();
30 defer held.release();
2731 const stderr = getStderrStream() catch return;
2832 stderr.print(fmt, args) catch return;
2933}
34
3035pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
3136 if (stderr_stream) |st| {
3237 return st;
......@@ -39,14 +44,15 @@ pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
3944 }
4045}
4146
42var self_debug_info: ?*ElfStackTrace = null;
43pub fn getSelfDebugInfo() !*ElfStackTrace {
44 if (self_debug_info) |info| {
47/// TODO multithreaded awareness
48var self_debug_info: ?DebugInfo = null;
49
50pub fn getSelfDebugInfo() !*DebugInfo {
51 if (self_debug_info) |*info| {
4552 return info;
4653 } else {
47 const info = try openSelfDebugInfo(getDebugInfoAllocator());
48 self_debug_info = info;
49 return info;
54 self_debug_info = try openSelfDebugInfo(getDebugInfoAllocator());
55 return &self_debug_info.?;
5056 }
5157}
5258
......@@ -57,6 +63,7 @@ fn wantTtyColor() bool {
5763}
5864
5965/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
66/// TODO multithreaded awareness
6067pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
6168 const stderr = getStderrStream() catch return;
6269 const debug_info = getSelfDebugInfo() catch |err| {
......@@ -70,6 +77,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
7077}
7178
7279/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
80/// TODO multithreaded awareness
7381pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void {
7482 const stderr = getStderrStream() catch return;
7583 const debug_info = getSelfDebugInfo() catch |err| {
......@@ -124,6 +132,7 @@ pub fn panic(comptime format: []const u8, args: ...) noreturn {
124132 panicExtra(null, first_trace_addr, format, args);
125133}
126134
135/// TODO multithreaded awareness
127136var panicking: u8 = 0; // TODO make this a bool
128137
129138pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn {
......@@ -152,7 +161,7 @@ const WHITE = "\x1b[37;1m";
152161const DIM = "\x1b[2m";
153162const RESET = "\x1b[0m";
154163
155pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool) !void {
164pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, allocator: *mem.Allocator, debug_info: *DebugInfo, tty_color: bool) !void {
156165 var frame_index: usize = undefined;
157166 var frames_left: usize = undefined;
158167 if (stack_trace.index < stack_trace.instruction_addresses.len) {
......@@ -182,7 +191,7 @@ pub inline fn getReturnAddress(frame_count: usize) usize {
182191 return @intToPtr(*const usize, fp + @sizeOf(usize)).*;
183192}
184193
185pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void {
194pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void {
186195 const AddressState = union(enum) {
187196 NotLookingForStartAddress,
188197 LookingForStartAddress: usize,
......@@ -215,130 +224,292 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_
215224 }
216225}
217226
218pub fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void {
227pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
219228 switch (builtin.os) {
220 builtin.Os.windows => return error.UnsupportedDebugInfo,
221 builtin.Os.macosx => {
222 // TODO(bnoordhuis) It's theoretically possible to obtain the
223 // compilation unit from the symbtab but it's not that useful
224 // in practice because the compiler dumps everything in a single
225 // object file. Future improvement: use external dSYM data when
226 // available.
227 const unknown = macho.Symbol{
228 .name = "???",
229 .address = address,
230 };
231 const symbol = debug_info.symbol_table.search(address) orelse &unknown;
232 try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ "0x{x}" ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address);
229 builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color),
230 builtin.Os.linux => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color),
231 builtin.Os.windows => {
232 // TODO https://github.com/ziglang/zig/issues/721
233 return error.UnsupportedOperatingSystem;
233234 },
234 else => {
235 const compile_unit = findCompileUnit(debug_info, address) catch {
236 if (tty_color) {
237 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n ???\n\n", address);
238 } else {
239 try out_stream.print("???:?:?: 0x{x} in ??? (???)\n ???\n\n", address);
240 }
241 return;
242 };
243 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
244 if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
245 defer line_info.deinit();
246 if (tty_color) {
247 try out_stream.print(
248 WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n",
249 line_info.file_name,
250 line_info.line,
251 line_info.column,
252 address,
253 compile_unit_name,
254 );
255 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
256 if (line_info.column == 0) {
257 try out_stream.write("\n");
258 } else {
259 {
260 var col_i: usize = 1;
261 while (col_i < line_info.column) : (col_i += 1) {
262 try out_stream.writeByte(' ');
263 }
264 }
265 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
266 }
267 } else |err| switch (err) {
268 error.EndOfFile => {},
269 else => return err,
270 }
271 } else {
272 try out_stream.print(
273 "{}:{}:{}: 0x{x} in ??? ({})\n",
274 line_info.file_name,
275 line_info.line,
276 line_info.column,
277 address,
278 compile_unit_name,
279 );
280 }
281 } else |err| switch (err) {
282 error.MissingDebugInfo, error.InvalidDebugInfo => {
283 try out_stream.print("0x{x} in ??? ({})\n", address, compile_unit_name);
284 },
285 else => return err,
235 else => return error.UnsupportedOperatingSystem,
236 }
237}
238
239fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
240 var min: usize = 0;
241 var max: usize = symbols.len - 1; // Exclude sentinel.
242 while (min < max) {
243 const mid = min + (max - min) / 2;
244 const curr = &symbols[mid];
245 const next = &symbols[mid + 1];
246 if (address >= next.address()) {
247 min = mid + 1;
248 } else if (address < curr.address()) {
249 max = mid;
250 } else {
251 return curr;
252 }
253 }
254 return null;
255}
256
257fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
258 const base_addr = @ptrToInt(&std.c._mh_execute_header);
259 const adjusted_addr = 0x100000000 + (address - base_addr);
260
261 const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse {
262 if (tty_color) {
263 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address);
264 } else {
265 try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address);
266 }
267 return;
268 };
269
270 const symbol_name = mem.toSliceConst(u8, di.strings.ptr + symbol.nlist.n_strx);
271 const compile_unit_name = if (symbol.ofile) |ofile| blk: {
272 const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx);
273 break :blk os.path.basename(ofile_path);
274 } else "???";
275 if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| {
276 defer line_info.deinit();
277 try printLineInfo(di, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color);
278 } else |err| switch (err) {
279 error.MissingDebugInfo, error.InvalidDebugInfo => {
280 if (tty_color) {
281 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name);
282 } else {
283 try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", address, symbol_name, compile_unit_name);
286284 }
287285 },
286 else => return err,
288287 }
289288}
290289
291pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace {
292 switch (builtin.object_format) {
293 builtin.ObjectFormat.elf => {
294 const st = try allocator.create(ElfStackTrace{
295 .self_exe_file = undefined,
296 .elf = undefined,
297 .debug_info = undefined,
298 .debug_abbrev = undefined,
299 .debug_str = undefined,
300 .debug_line = undefined,
301 .debug_ranges = null,
302 .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
303 .compile_unit_list = ArrayList(CompileUnit).init(allocator),
304 });
305 errdefer allocator.destroy(st);
306 st.self_exe_file = try os.openSelfExe();
307 errdefer st.self_exe_file.close();
308
309 try st.elf.openFile(allocator, &st.self_exe_file);
310 errdefer st.elf.close();
311
312 st.debug_info = (try st.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo;
313 st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) orelse return error.MissingDebugInfo;
314 st.debug_str = (try st.elf.findSection(".debug_str")) orelse return error.MissingDebugInfo;
315 st.debug_line = (try st.elf.findSection(".debug_line")) orelse return error.MissingDebugInfo;
316 st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
317 try scanAllCompileUnits(st);
318 return st;
290pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
291 const compile_unit = findCompileUnit(debug_info, address) catch {
292 if (tty_color) {
293 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address);
294 } else {
295 try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address);
296 }
297 return;
298 };
299 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
300 if (getLineNumberInfoLinux(debug_info, compile_unit, address - 1)) |line_info| {
301 defer line_info.deinit();
302 const symbol_name = "???";
303 try printLineInfo(debug_info, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color);
304 } else |err| switch (err) {
305 error.MissingDebugInfo, error.InvalidDebugInfo => {
306 if (tty_color) {
307 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n\n\n", address, compile_unit_name);
308 } else {
309 try out_stream.print("???:?:?: 0x{x} in ??? ({})\n\n\n", address, compile_unit_name);
310 }
319311 },
320 builtin.ObjectFormat.macho => {
321 var exe_file = try os.openSelfExe();
322 defer exe_file.close();
312 else => return err,
313 }
314}
323315
324 const st = try allocator.create(ElfStackTrace{ .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)) });
325 errdefer allocator.destroy(st);
326 return st;
327 },
328 builtin.ObjectFormat.coff => {
329 return error.TodoSupportCoffDebugInfo;
330 },
331 builtin.ObjectFormat.wasm => {
332 return error.TodoSupportCOFFDebugInfo;
333 },
334 builtin.ObjectFormat.unknown => {
335 return error.UnknownObjectFormat;
316fn printLineInfo(
317 debug_info: *DebugInfo,
318 out_stream: var,
319 line_info: LineInfo,
320 address: usize,
321 symbol_name: []const u8,
322 compile_unit_name: []const u8,
323 tty_color: bool,
324) !void {
325 if (tty_color) {
326 try out_stream.print(
327 WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n",
328 line_info.file_name,
329 line_info.line,
330 line_info.column,
331 address,
332 symbol_name,
333 compile_unit_name,
334 );
335 if (printLineFromFile(out_stream, line_info)) {
336 if (line_info.column == 0) {
337 try out_stream.write("\n");
338 } else {
339 {
340 var col_i: usize = 1;
341 while (col_i < line_info.column) : (col_i += 1) {
342 try out_stream.writeByte(' ');
343 }
344 }
345 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
346 }
347 } else |err| switch (err) {
348 error.EndOfFile => {},
349 else => return err,
350 }
351 } else {
352 try out_stream.print(
353 "{}:{}:{}: 0x{x} in {} ({})\n",
354 line_info.file_name,
355 line_info.line,
356 line_info.column,
357 address,
358 symbol_name,
359 compile_unit_name,
360 );
361 }
362}
363
364// TODO use this
365pub const OpenSelfDebugInfoError = error{
366 MissingDebugInfo,
367 OutOfMemory,
368 UnsupportedOperatingSystem,
369};
370
371pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo {
372 switch (builtin.os) {
373 builtin.Os.linux => return openSelfDebugInfoLinux(allocator),
374 builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator),
375 builtin.Os.windows => {
376 // TODO: https://github.com/ziglang/zig/issues/721
377 return error.UnsupportedOperatingSystem;
336378 },
379 else => return error.UnsupportedOperatingSystem,
337380 }
338381}
339382
340fn printLineFromFile(allocator: *mem.Allocator, out_stream: var, line_info: *const LineInfo) !void {
341 var f = try os.File.openRead(allocator, line_info.file_name);
383fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
384 var di = DebugInfo{
385 .self_exe_file = undefined,
386 .elf = undefined,
387 .debug_info = undefined,
388 .debug_abbrev = undefined,
389 .debug_str = undefined,
390 .debug_line = undefined,
391 .debug_ranges = null,
392 .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
393 .compile_unit_list = ArrayList(CompileUnit).init(allocator),
394 };
395 di.self_exe_file = try os.openSelfExe();
396 errdefer di.self_exe_file.close();
397
398 try di.elf.openFile(allocator, &di.self_exe_file);
399 errdefer di.elf.close();
400
401 di.debug_info = (try di.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo;
402 di.debug_abbrev = (try di.elf.findSection(".debug_abbrev")) orelse return error.MissingDebugInfo;
403 di.debug_str = (try di.elf.findSection(".debug_str")) orelse return error.MissingDebugInfo;
404 di.debug_line = (try di.elf.findSection(".debug_line")) orelse return error.MissingDebugInfo;
405 di.debug_ranges = (try di.elf.findSection(".debug_ranges"));
406 try scanAllCompileUnits(&di);
407 return di;
408}
409
410pub fn findElfSection(elf: *Elf, name: []const u8) ?*elf.Shdr {
411 var file_stream = io.FileInStream.init(elf.in_file);
412 const in = &file_stream.stream;
413
414 section_loop: for (elf.section_headers) |*elf_section| {
415 if (elf_section.sh_type == SHT_NULL) continue;
416
417 const name_offset = elf.string_section.offset + elf_section.name;
418 try elf.in_file.seekTo(name_offset);
419
420 for (name) |expected_c| {
421 const target_c = try in.readByte();
422 if (target_c == 0 or expected_c != target_c) continue :section_loop;
423 }
424
425 {
426 const null_byte = try in.readByte();
427 if (null_byte == 0) return elf_section;
428 }
429 }
430
431 return null;
432}
433
434fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
435 const hdr = &std.c._mh_execute_header;
436 assert(hdr.magic == std.macho.MH_MAGIC_64);
437
438 const hdr_base = @ptrCast([*]u8, hdr);
439 var ptr = hdr_base + @sizeOf(macho.mach_header_64);
440 var ncmd: u32 = hdr.ncmds;
441 const symtab = while (ncmd != 0) : (ncmd -= 1) {
442 const lc = @ptrCast(*std.macho.load_command, ptr);
443 switch (lc.cmd) {
444 std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr),
445 else => {},
446 }
447 ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403
448 } else {
449 return error.MissingDebugInfo;
450 };
451 const syms = @ptrCast([*]macho.nlist_64, hdr_base + symtab.symoff)[0..symtab.nsyms];
452 const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize];
453
454 const symbols_buf = try allocator.alloc(MachoSymbol, syms.len);
455
456 var ofile: ?*macho.nlist_64 = null;
457 var reloc: u64 = 0;
458 var symbol_index: usize = 0;
459 var last_len: u64 = 0;
460 for (syms) |*sym| {
461 if (sym.n_type & std.macho.N_STAB != 0) {
462 switch (sym.n_type) {
463 std.macho.N_OSO => {
464 ofile = sym;
465 reloc = 0;
466 },
467 std.macho.N_FUN => {
468 if (sym.n_sect == 0) {
469 last_len = sym.n_value;
470 } else {
471 symbols_buf[symbol_index] = MachoSymbol{
472 .nlist = sym,
473 .ofile = ofile,
474 .reloc = reloc,
475 };
476 symbol_index += 1;
477 }
478 },
479 std.macho.N_BNSYM => {
480 if (reloc == 0) {
481 reloc = sym.n_value;
482 }
483 },
484 else => continue,
485 }
486 }
487 }
488 const sentinel = try allocator.createOne(macho.nlist_64);
489 sentinel.* = macho.nlist_64{
490 .n_strx = 0,
491 .n_type = 36,
492 .n_sect = 0,
493 .n_desc = 0,
494 .n_value = symbols_buf[symbol_index - 1].nlist.n_value + last_len,
495 };
496
497 const symbols = allocator.shrink(MachoSymbol, symbols_buf, symbol_index);
498
499 // Even though lld emits symbols in ascending order, this debug code
500 // should work for programs linked in any valid way.
501 // This sort is so that we can binary search later.
502 std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan);
503
504 return DebugInfo{
505 .ofiles = DebugInfo.OFileTable.init(allocator),
506 .symbols = symbols,
507 .strings = strings,
508 };
509}
510
511fn printLineFromFile(out_stream: var, line_info: *const LineInfo) !void {
512 var f = try os.File.openRead(line_info.file_name);
342513 defer f.close();
343514 // TODO fstat and make sure that the file has the correct size
344515
......@@ -369,12 +540,42 @@ fn printLineFromFile(allocator: *mem.Allocator, out_stream: var, line_info: *con
369540 }
370541}
371542
372pub const ElfStackTrace = switch (builtin.os) {
373 builtin.Os.macosx => struct {
374 symbol_table: macho.SymbolTable,
543const MachoSymbol = struct {
544 nlist: *macho.nlist_64,
545 ofile: ?*macho.nlist_64,
546 reloc: u64,
547
548 /// Returns the address from the macho file
549 fn address(self: MachoSymbol) u64 {
550 return self.nlist.n_value;
551 }
552
553 fn addressLessThan(lhs: MachoSymbol, rhs: MachoSymbol) bool {
554 return lhs.address() < rhs.address();
555 }
556};
557
558const MachOFile = struct {
559 bytes: []align(@alignOf(macho.mach_header_64)) const u8,
560 sect_debug_info: ?*const macho.section_64,
561 sect_debug_line: ?*const macho.section_64,
562};
375563
376 pub fn close(self: *ElfStackTrace) void {
377 self.symbol_table.deinit();
564pub const DebugInfo = switch (builtin.os) {
565 builtin.Os.macosx => struct {
566 symbols: []const MachoSymbol,
567 strings: []const u8,
568 ofiles: OFileTable,
569
570 const OFileTable = std.HashMap(
571 *macho.nlist_64,
572 MachOFile,
573 std.hash_map.getHashPtrAddrFn(*macho.nlist_64),
574 std.hash_map.getTrivialEqlFn(*macho.nlist_64),
575 );
576
577 pub fn allocator(self: DebugInfo) *mem.Allocator {
578 return self.ofiles.allocator;
378579 }
379580 },
380581 else => struct {
......@@ -388,17 +589,17 @@ pub const ElfStackTrace = switch (builtin.os) {
388589 abbrev_table_list: ArrayList(AbbrevTableHeader),
389590 compile_unit_list: ArrayList(CompileUnit),
390591
391 pub fn allocator(self: *const ElfStackTrace) *mem.Allocator {
592 pub fn allocator(self: DebugInfo) *mem.Allocator {
392593 return self.abbrev_table_list.allocator;
393594 }
394595
395 pub fn readString(self: *ElfStackTrace) ![]u8 {
596 pub fn readString(self: *DebugInfo) ![]u8 {
396597 var in_file_stream = io.FileInStream.init(&self.self_exe_file);
397598 const in_stream = &in_file_stream.stream;
398599 return readStringRaw(self.allocator(), in_stream);
399600 }
400601
401 pub fn close(self: *ElfStackTrace) void {
602 pub fn close(self: *DebugInfo) void {
402603 self.self_exe_file.close();
403604 self.elf.close();
404605 }
......@@ -505,7 +706,7 @@ const Die = struct {
505706 };
506707 }
507708
508 fn getAttrString(self: *const Die, st: *ElfStackTrace, id: u64) ![]u8 {
709 fn getAttrString(self: *const Die, st: *DebugInfo, id: u64) ![]u8 {
509710 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
510711 return switch (form_value.*) {
511712 FormValue.String => |value| value,
......@@ -620,7 +821,7 @@ fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 {
620821 return buf.toSlice();
621822}
622823
623fn getString(st: *ElfStackTrace, offset: u64) ![]u8 {
824fn getString(st: *DebugInfo, offset: u64) ![]u8 {
624825 const pos = st.debug_str.offset + offset;
625826 try st.self_exe_file.seekTo(pos);
626827 return st.readString();
......@@ -672,14 +873,10 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type
672873
673874const ParseFormValueError = error{
674875 EndOfStream,
675 Io,
676 BadFd,
677 Unexpected,
678876 InvalidDebugInfo,
679877 EndOfFile,
680 IsDir,
681878 OutOfMemory,
682};
879} || std.os.File.ReadError;
683880
684881fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue {
685882 return switch (form_id) {
......@@ -731,7 +928,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
731928 };
732929}
733930
734fn parseAbbrevTable(st: *ElfStackTrace) !AbbrevTable {
931fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable {
735932 const in_file = &st.self_exe_file;
736933 var in_file_stream = io.FileInStream.init(in_file);
737934 const in_stream = &in_file_stream.stream;
......@@ -761,7 +958,7 @@ fn parseAbbrevTable(st: *ElfStackTrace) !AbbrevTable {
761958
762959/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
763960/// seeks in the stream and parses it.
764fn getAbbrevTable(st: *ElfStackTrace, abbrev_offset: u64) !*const AbbrevTable {
961fn getAbbrevTable(st: *DebugInfo, abbrev_offset: u64) !*const AbbrevTable {
765962 for (st.abbrev_table_list.toSlice()) |*header| {
766963 if (header.offset == abbrev_offset) {
767964 return &header.table;
......@@ -782,7 +979,7 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con
782979 return null;
783980}
784981
785fn parseDie(st: *ElfStackTrace, abbrev_table: *const AbbrevTable, is_64: bool) !Die {
982fn parseDie(st: *DebugInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die {
786983 const in_file = &st.self_exe_file;
787984 var in_file_stream = io.FileInStream.init(in_file);
788985 const in_stream = &in_file_stream.stream;
......@@ -804,12 +1001,210 @@ fn parseDie(st: *ElfStackTrace, abbrev_table: *const AbbrevTable, is_64: bool) !
8041001 return result;
8051002}
8061003
807fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, target_address: usize) !LineInfo {
808 const compile_unit_cwd = try compile_unit.die.getAttrString(st, DW.AT_comp_dir);
1004fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo {
1005 const ofile = symbol.ofile orelse return error.MissingDebugInfo;
1006 const gop = try di.ofiles.getOrPut(ofile);
1007 const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: {
1008 errdefer _ = di.ofiles.remove(ofile);
1009 const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx);
1010
1011 gop.kv.value = MachOFile{
1012 .bytes = try std.io.readFileAllocAligned(di.ofiles.allocator, ofile_path, @alignOf(macho.mach_header_64)),
1013 .sect_debug_info = null,
1014 .sect_debug_line = null,
1015 };
1016 const hdr = @ptrCast(*const macho.mach_header_64, gop.kv.value.bytes.ptr);
1017 if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo;
1018
1019 const hdr_base = @ptrCast([*]const u8, hdr);
1020 var ptr = hdr_base + @sizeOf(macho.mach_header_64);
1021 var ncmd: u32 = hdr.ncmds;
1022 const segcmd = while (ncmd != 0) : (ncmd -= 1) {
1023 const lc = @ptrCast(*const std.macho.load_command, ptr);
1024 switch (lc.cmd) {
1025 std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, ptr),
1026 else => {},
1027 }
1028 ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403
1029 } else {
1030 return error.MissingDebugInfo;
1031 };
1032 const sections = @alignCast(@alignOf(macho.section_64), @ptrCast([*]const macho.section_64, ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects];
1033 for (sections) |*sect| {
1034 if (sect.flags & macho.SECTION_TYPE == macho.S_REGULAR and
1035 (sect.flags & macho.SECTION_ATTRIBUTES) & macho.S_ATTR_DEBUG == macho.S_ATTR_DEBUG)
1036 {
1037 const sect_name = mem.toSliceConst(u8, &sect.sectname);
1038 if (mem.eql(u8, sect_name, "__debug_line")) {
1039 gop.kv.value.sect_debug_line = sect;
1040 } else if (mem.eql(u8, sect_name, "__debug_info")) {
1041 gop.kv.value.sect_debug_info = sect;
1042 }
1043 }
1044 }
8091045
810 const in_file = &st.self_exe_file;
811 const debug_line_end = st.debug_line.offset + st.debug_line.size;
812 var this_offset = st.debug_line.offset;
1046 break :blk &gop.kv.value;
1047 };
1048
1049 const sect_debug_line = mach_o_file.sect_debug_line orelse return error.MissingDebugInfo;
1050 var ptr = mach_o_file.bytes.ptr + sect_debug_line.offset;
1051
1052 var is_64: bool = undefined;
1053 const unit_length = try readInitialLengthMem(&ptr, &is_64);
1054 if (unit_length == 0) return error.MissingDebugInfo;
1055
1056 const version = readIntMem(&ptr, u16, builtin.Endian.Little);
1057 // TODO support 3 and 5
1058 if (version != 2 and version != 4) return error.InvalidDebugInfo;
1059
1060 const prologue_length = if (is_64)
1061 readIntMem(&ptr, u64, builtin.Endian.Little)
1062 else
1063 readIntMem(&ptr, u32, builtin.Endian.Little);
1064 const prog_start = ptr + prologue_length;
1065
1066 const minimum_instruction_length = readByteMem(&ptr);
1067 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
1068
1069 if (version >= 4) {
1070 // maximum_operations_per_instruction
1071 ptr += 1;
1072 }
1073
1074 const default_is_stmt = readByteMem(&ptr) != 0;
1075 const line_base = readByteSignedMem(&ptr);
1076
1077 const line_range = readByteMem(&ptr);
1078 if (line_range == 0) return error.InvalidDebugInfo;
1079
1080 const opcode_base = readByteMem(&ptr);
1081
1082 const standard_opcode_lengths = ptr[0 .. opcode_base - 1];
1083 ptr += opcode_base - 1;
1084
1085 var include_directories = ArrayList([]const u8).init(di.allocator());
1086 try include_directories.append("");
1087 while (true) {
1088 const dir = readStringMem(&ptr);
1089 if (dir.len == 0) break;
1090 try include_directories.append(dir);
1091 }
1092
1093 var file_entries = ArrayList(FileEntry).init(di.allocator());
1094 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
1095
1096 while (true) {
1097 const file_name = readStringMem(&ptr);
1098 if (file_name.len == 0) break;
1099 const dir_index = try readULeb128Mem(&ptr);
1100 const mtime = try readULeb128Mem(&ptr);
1101 const len_bytes = try readULeb128Mem(&ptr);
1102 try file_entries.append(FileEntry{
1103 .file_name = file_name,
1104 .dir_index = dir_index,
1105 .mtime = mtime,
1106 .len_bytes = len_bytes,
1107 });
1108 }
1109
1110 ptr = prog_start;
1111 while (true) {
1112 const opcode = readByteMem(&ptr);
1113
1114 if (opcode == DW.LNS_extended_op) {
1115 const op_size = try readULeb128Mem(&ptr);
1116 if (op_size < 1) return error.InvalidDebugInfo;
1117 var sub_op = readByteMem(&ptr);
1118 switch (sub_op) {
1119 DW.LNE_end_sequence => {
1120 prog.end_sequence = true;
1121 if (try prog.checkLineMatch()) |info| return info;
1122 return error.MissingDebugInfo;
1123 },
1124 DW.LNE_set_address => {
1125 const addr = readIntMem(&ptr, usize, builtin.Endian.Little);
1126 prog.address = symbol.reloc + addr;
1127 },
1128 DW.LNE_define_file => {
1129 const file_name = readStringMem(&ptr);
1130 const dir_index = try readULeb128Mem(&ptr);
1131 const mtime = try readULeb128Mem(&ptr);
1132 const len_bytes = try readULeb128Mem(&ptr);
1133 try file_entries.append(FileEntry{
1134 .file_name = file_name,
1135 .dir_index = dir_index,
1136 .mtime = mtime,
1137 .len_bytes = len_bytes,
1138 });
1139 },
1140 else => {
1141 ptr += op_size - 1;
1142 },
1143 }
1144 } else if (opcode >= opcode_base) {
1145 // special opcodes
1146 const adjusted_opcode = opcode - opcode_base;
1147 const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
1148 const inc_line = i32(line_base) + i32(adjusted_opcode % line_range);
1149 prog.line += inc_line;
1150 prog.address += inc_addr;
1151 if (try prog.checkLineMatch()) |info| return info;
1152 prog.basic_block = false;
1153 } else {
1154 switch (opcode) {
1155 DW.LNS_copy => {
1156 if (try prog.checkLineMatch()) |info| return info;
1157 prog.basic_block = false;
1158 },
1159 DW.LNS_advance_pc => {
1160 const arg = try readULeb128Mem(&ptr);
1161 prog.address += arg * minimum_instruction_length;
1162 },
1163 DW.LNS_advance_line => {
1164 const arg = try readILeb128Mem(&ptr);
1165 prog.line += arg;
1166 },
1167 DW.LNS_set_file => {
1168 const arg = try readULeb128Mem(&ptr);
1169 prog.file = arg;
1170 },
1171 DW.LNS_set_column => {
1172 const arg = try readULeb128Mem(&ptr);
1173 prog.column = arg;
1174 },
1175 DW.LNS_negate_stmt => {
1176 prog.is_stmt = !prog.is_stmt;
1177 },
1178 DW.LNS_set_basic_block => {
1179 prog.basic_block = true;
1180 },
1181 DW.LNS_const_add_pc => {
1182 const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
1183 prog.address += inc_addr;
1184 },
1185 DW.LNS_fixed_advance_pc => {
1186 const arg = readIntMem(&ptr, u16, builtin.Endian.Little);
1187 prog.address += arg;
1188 },
1189 DW.LNS_set_prologue_end => {},
1190 else => {
1191 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
1192 const len_bytes = standard_opcode_lengths[opcode - 1];
1193 ptr += len_bytes;
1194 },
1195 }
1196 }
1197 }
1198
1199 return error.MissingDebugInfo;
1200}
1201
1202fn getLineNumberInfoLinux(di: *DebugInfo, compile_unit: *const CompileUnit, target_address: usize) !LineInfo {
1203 const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
1204
1205 const in_file = &di.self_exe_file;
1206 const debug_line_end = di.debug_line.offset + di.debug_line.size;
1207 var this_offset = di.debug_line.offset;
8131208 var this_index: usize = 0;
8141209
8151210 var in_file_stream = io.FileInStream.init(in_file);
......@@ -828,11 +1223,11 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
8281223 continue;
8291224 }
8301225
831 const version = try in_stream.readInt(st.elf.endian, u16);
1226 const version = try in_stream.readInt(di.elf.endian, u16);
8321227 // TODO support 3 and 5
8331228 if (version != 2 and version != 4) return error.InvalidDebugInfo;
8341229
835 const prologue_length = if (is_64) try in_stream.readInt(st.elf.endian, u64) else try in_stream.readInt(st.elf.endian, u32);
1230 const prologue_length = if (is_64) try in_stream.readInt(di.elf.endian, u64) else try in_stream.readInt(di.elf.endian, u32);
8361231 const prog_start_offset = (try in_file.getPos()) + prologue_length;
8371232
8381233 const minimum_instruction_length = try in_stream.readByte();
......@@ -851,7 +1246,7 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
8511246
8521247 const opcode_base = try in_stream.readByte();
8531248
854 const standard_opcode_lengths = try st.allocator().alloc(u8, opcode_base - 1);
1249 const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
8551250
8561251 {
8571252 var i: usize = 0;
......@@ -860,19 +1255,19 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
8601255 }
8611256 }
8621257
863 var include_directories = ArrayList([]u8).init(st.allocator());
1258 var include_directories = ArrayList([]u8).init(di.allocator());
8641259 try include_directories.append(compile_unit_cwd);
8651260 while (true) {
866 const dir = try st.readString();
1261 const dir = try di.readString();
8671262 if (dir.len == 0) break;
8681263 try include_directories.append(dir);
8691264 }
8701265
871 var file_entries = ArrayList(FileEntry).init(st.allocator());
1266 var file_entries = ArrayList(FileEntry).init(di.allocator());
8721267 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
8731268
8741269 while (true) {
875 const file_name = try st.readString();
1270 const file_name = try di.readString();
8761271 if (file_name.len == 0) break;
8771272 const dir_index = try readULeb128(in_stream);
8781273 const mtime = try readULeb128(in_stream);
......@@ -890,11 +1285,10 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
8901285 while (true) {
8911286 const opcode = try in_stream.readByte();
8921287
893 var sub_op: u8 = undefined; // TODO move this to the correct scope and fix the compiler crash
8941288 if (opcode == DW.LNS_extended_op) {
8951289 const op_size = try readULeb128(in_stream);
8961290 if (op_size < 1) return error.InvalidDebugInfo;
897 sub_op = try in_stream.readByte();
1291 var sub_op = try in_stream.readByte();
8981292 switch (sub_op) {
8991293 DW.LNE_end_sequence => {
9001294 prog.end_sequence = true;
......@@ -902,11 +1296,11 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
9021296 return error.MissingDebugInfo;
9031297 },
9041298 DW.LNE_set_address => {
905 const addr = try in_stream.readInt(st.elf.endian, usize);
1299 const addr = try in_stream.readInt(di.elf.endian, usize);
9061300 prog.address = addr;
9071301 },
9081302 DW.LNE_define_file => {
909 const file_name = try st.readString();
1303 const file_name = try di.readString();
9101304 const dir_index = try readULeb128(in_stream);
9111305 const mtime = try readULeb128(in_stream);
9121306 const len_bytes = try readULeb128(in_stream);
......@@ -964,7 +1358,7 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
9641358 prog.address += inc_addr;
9651359 },
9661360 DW.LNS_fixed_advance_pc => {
967 const arg = try in_stream.readInt(st.elf.endian, u16);
1361 const arg = try in_stream.readInt(di.elf.endian, u16);
9681362 prog.address += arg;
9691363 },
9701364 DW.LNS_set_prologue_end => {},
......@@ -983,7 +1377,7 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
9831377 return error.MissingDebugInfo;
9841378}
9851379
986fn scanAllCompileUnits(st: *ElfStackTrace) !void {
1380fn scanAllCompileUnits(st: *DebugInfo) !void {
9871381 const debug_info_end = st.debug_info.offset + st.debug_info.size;
9881382 var this_unit_offset = st.debug_info.offset;
9891383 var cu_index: usize = 0;
......@@ -1053,7 +1447,7 @@ fn scanAllCompileUnits(st: *ElfStackTrace) !void {
10531447 }
10541448}
10551449
1056fn findCompileUnit(st: *ElfStackTrace, target_address: u64) !*const CompileUnit {
1450fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit {
10571451 var in_file_stream = io.FileInStream.init(&st.self_exe_file);
10581452 const in_stream = &in_file_stream.stream;
10591453 for (st.compile_unit_list.toSlice()) |*compile_unit| {
......@@ -1087,6 +1481,89 @@ fn findCompileUnit(st: *ElfStackTrace, target_address: u64) !*const CompileUnit
10871481 return error.MissingDebugInfo;
10881482}
10891483
1484fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T {
1485 const result = mem.readInt(ptr.*[0..@sizeOf(T)], T, endian);
1486 ptr.* += @sizeOf(T);
1487 return result;
1488}
1489
1490fn readByteMem(ptr: *[*]const u8) u8 {
1491 const result = ptr.*[0];
1492 ptr.* += 1;
1493 return result;
1494}
1495
1496fn readByteSignedMem(ptr: *[*]const u8) i8 {
1497 return @bitCast(i8, readByteMem(ptr));
1498}
1499
1500fn readInitialLengthMem(ptr: *[*]const u8, is_64: *bool) !u64 {
1501 const first_32_bits = mem.readIntLE(u32, ptr.*[0..4]);
1502 is_64.* = (first_32_bits == 0xffffffff);
1503 if (is_64.*) {
1504 ptr.* += 4;
1505 const result = mem.readIntLE(u64, ptr.*[0..8]);
1506 ptr.* += 8;
1507 return result;
1508 } else {
1509 if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
1510 ptr.* += 4;
1511 return u64(first_32_bits);
1512 }
1513}
1514
1515fn readStringMem(ptr: *[*]const u8) []const u8 {
1516 const result = mem.toSliceConst(u8, ptr.*);
1517 ptr.* += result.len + 1;
1518 return result;
1519}
1520
1521fn readULeb128Mem(ptr: *[*]const u8) !u64 {
1522 var result: u64 = 0;
1523 var shift: usize = 0;
1524 var i: usize = 0;
1525
1526 while (true) {
1527 const byte = ptr.*[i];
1528 i += 1;
1529
1530 var operand: u64 = undefined;
1531
1532 if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
1533
1534 result |= operand;
1535
1536 if ((byte & 0b10000000) == 0) {
1537 ptr.* += i;
1538 return result;
1539 }
1540
1541 shift += 7;
1542 }
1543}
1544fn readILeb128Mem(ptr: *[*]const u8) !i64 {
1545 var result: i64 = 0;
1546 var shift: usize = 0;
1547 var i: usize = 0;
1548
1549 while (true) {
1550 const byte = ptr.*[i];
1551 i += 1;
1552
1553 var operand: i64 = undefined;
1554 if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
1555
1556 result |= operand;
1557 shift += 7;
1558
1559 if ((byte & 0b10000000) == 0) {
1560 if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift));
1561 ptr.* += i;
1562 return result;
1563 }
1564 }
1565}
1566
10901567fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
10911568 const first_32_bits = try in_stream.readIntLe(u32);
10921569 is_64.* = (first_32_bits == 0xffffffff);
......@@ -1143,7 +1620,7 @@ pub const global_allocator = &global_fixed_allocator.allocator;
11431620var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]);
11441621var global_allocator_mem: [100 * 1024]u8 = undefined;
11451622
1146// TODO make thread safe
1623/// TODO multithreaded awareness
11471624var debug_info_allocator: ?*mem.Allocator = null;
11481625var debug_info_direct_allocator: std.heap.DirectAllocator = undefined;
11491626var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
std/elf.zig+5
......@@ -869,6 +869,11 @@ pub const Phdr = switch (@sizeOf(usize)) {
869869 8 => Elf64_Phdr,
870870 else => @compileError("expected pointer size of 32 or 64"),
871871};
872pub const Shdr = switch (@sizeOf(usize)) {
873 4 => Elf32_Shdr,
874 8 => Elf64_Shdr,
875 else => @compileError("expected pointer size of 32 or 64"),
876};
872877pub const Sym = switch (@sizeOf(usize)) {
873878 4 => Elf32_Sym,
874879 8 => Elf64_Sym,
std/event.zig+14-8
......@@ -1,17 +1,23 @@
1pub const Channel = @import("event/channel.zig").Channel;
2pub const Future = @import("event/future.zig").Future;
3pub const Group = @import("event/group.zig").Group;
4pub const Lock = @import("event/lock.zig").Lock;
15pub const Locked = @import("event/locked.zig").Locked;
6pub const RwLock = @import("event/rwlock.zig").RwLock;
7pub const RwLocked = @import("event/rwlocked.zig").RwLocked;
28pub const Loop = @import("event/loop.zig").Loop;
3pub const Lock = @import("event/lock.zig").Lock;
9pub const fs = @import("event/fs.zig");
410pub const tcp = @import("event/tcp.zig");
5pub const Channel = @import("event/channel.zig").Channel;
6pub const Group = @import("event/group.zig").Group;
7pub const Future = @import("event/future.zig").Future;
811
912test "import event tests" {
13 _ = @import("event/channel.zig");
14 _ = @import("event/fs.zig");
15 _ = @import("event/future.zig");
16 _ = @import("event/group.zig");
17 _ = @import("event/lock.zig");
1018 _ = @import("event/locked.zig");
19 _ = @import("event/rwlock.zig");
20 _ = @import("event/rwlocked.zig");
1121 _ = @import("event/loop.zig");
12 _ = @import("event/lock.zig");
1322 _ = @import("event/tcp.zig");
14 _ = @import("event/channel.zig");
15 _ = @import("event/group.zig");
16 _ = @import("event/future.zig");
1723}
std/event/channel.zig+161-24
......@@ -5,7 +5,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
55const AtomicOrder = builtin.AtomicOrder;
66const Loop = std.event.Loop;
77
8/// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size
8/// many producer, many consumer, thread-safe, runtime configurable buffer size
99/// when buffer is empty, consumers suspend and are resumed by producers
1010/// when buffer is full, producers suspend and are resumed by consumers
1111pub fn Channel(comptime T: type) type {
......@@ -13,6 +13,7 @@ pub fn Channel(comptime T: type) type {
1313 loop: *Loop,
1414
1515 getters: std.atomic.Queue(GetNode),
16 or_null_queue: std.atomic.Queue(*std.atomic.Queue(GetNode).Node),
1617 putters: std.atomic.Queue(PutNode),
1718 get_count: usize,
1819 put_count: usize,
......@@ -26,8 +27,22 @@ pub fn Channel(comptime T: type) type {
2627
2728 const SelfChannel = this;
2829 const GetNode = struct {
29 ptr: *T,
3030 tick_node: *Loop.NextTickNode,
31 data: Data,
32
33 const Data = union(enum) {
34 Normal: Normal,
35 OrNull: OrNull,
36 };
37
38 const Normal = struct {
39 ptr: *T,
40 };
41
42 const OrNull = struct {
43 ptr: *?T,
44 or_null: *std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node,
45 };
3146 };
3247 const PutNode = struct {
3348 data: T,
......@@ -48,6 +63,7 @@ pub fn Channel(comptime T: type) type {
4863 .need_dispatch = 0,
4964 .getters = std.atomic.Queue(GetNode).init(),
5065 .putters = std.atomic.Queue(PutNode).init(),
66 .or_null_queue = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).init(),
5167 .get_count = 0,
5268 .put_count = 0,
5369 });
......@@ -71,18 +87,29 @@ pub fn Channel(comptime T: type) type {
7187 /// puts a data item in the channel. The promise completes when the value has been added to the
7288 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
7389 pub async fn put(self: *SelfChannel, data: T) void {
90 // TODO fix this workaround
91 suspend {
92 resume @handle();
93 }
94
95 var my_tick_node = Loop.NextTickNode.init(@handle());
96 var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{
97 .tick_node = &my_tick_node,
98 .data = data,
99 });
100
101 // TODO test canceling a put()
102 errdefer {
103 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
104 const need_dispatch = !self.putters.remove(&queue_node);
105 self.loop.cancelOnNextTick(&my_tick_node);
106 if (need_dispatch) {
107 // oops we made the put_count incorrect for a period of time. fix by dispatching.
108 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
109 self.dispatch();
110 }
111 }
74112 suspend {
75 var my_tick_node = Loop.NextTickNode{
76 .next = undefined,
77 .data = @handle(),
78 };
79 var queue_node = std.atomic.Queue(PutNode).Node{
80 .data = PutNode{
81 .tick_node = &my_tick_node,
82 .data = data,
83 },
84 .next = undefined,
85 };
86113 self.putters.put(&queue_node);
87114 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
88115
......@@ -93,23 +120,95 @@ pub fn Channel(comptime T: type) type {
93120 /// await this function to get an item from the channel. If the buffer is empty, the promise will
94121 /// complete when the next item is put in the channel.
95122 pub async fn get(self: *SelfChannel) T {
123 // TODO fix this workaround
124 suspend {
125 resume @handle();
126 }
127
96128 // TODO integrate this function with named return values
97129 // so we can get rid of this extra result copy
98130 var result: T = undefined;
131 var my_tick_node = Loop.NextTickNode.init(@handle());
132 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
133 .tick_node = &my_tick_node,
134 .data = GetNode.Data{
135 .Normal = GetNode.Normal{ .ptr = &result },
136 },
137 });
138
139 // TODO test canceling a get()
140 errdefer {
141 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
142 const need_dispatch = !self.getters.remove(&queue_node);
143 self.loop.cancelOnNextTick(&my_tick_node);
144 if (need_dispatch) {
145 // oops we made the get_count incorrect for a period of time. fix by dispatching.
146 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
147 self.dispatch();
148 }
149 }
150
151 suspend {
152 self.getters.put(&queue_node);
153 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
154
155 self.dispatch();
156 }
157 return result;
158 }
159
160 //pub async fn select(comptime EnumUnion: type, channels: ...) EnumUnion {
161 // assert(@memberCount(EnumUnion) == channels.len); // enum union and channels mismatch
162 // assert(channels.len != 0); // enum unions cannot have 0 fields
163 // if (channels.len == 1) {
164 // const result = await (async channels[0].get() catch unreachable);
165 // return @unionInit(EnumUnion, @memberName(EnumUnion, 0), result);
166 // }
167 //}
168
169 /// Await this function to get an item from the channel. If the buffer is empty and there are no
170 /// puts waiting, this returns null.
171 /// Await is necessary for locking purposes. The function will be resumed after checking the channel
172 /// for data and will not wait for data to be available.
173 pub async fn getOrNull(self: *SelfChannel) ?T {
174 // TODO fix this workaround
99175 suspend {
100 var my_tick_node = Loop.NextTickNode{
101 .next = undefined,
102 .data = @handle(),
103 };
104 var queue_node = std.atomic.Queue(GetNode).Node{
105 .data = GetNode{
176 resume @handle();
177 }
178
179 // TODO integrate this function with named return values
180 // so we can get rid of this extra result copy
181 var result: ?T = null;
182 var my_tick_node = Loop.NextTickNode.init(@handle());
183 var or_null_node = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node.init(undefined);
184 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
185 .tick_node = &my_tick_node,
186 .data = GetNode.Data{
187 .OrNull = GetNode.OrNull{
106188 .ptr = &result,
107 .tick_node = &my_tick_node,
189 .or_null = &or_null_node,
108190 },
109 .next = undefined,
110 };
191 },
192 });
193 or_null_node.data = &queue_node;
194
195 // TODO test canceling getOrNull
196 errdefer {
197 _ = self.or_null_queue.remove(&or_null_node);
198 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
199 const need_dispatch = !self.getters.remove(&queue_node);
200 self.loop.cancelOnNextTick(&my_tick_node);
201 if (need_dispatch) {
202 // oops we made the get_count incorrect for a period of time. fix by dispatching.
203 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
204 self.dispatch();
205 }
206 }
207
208 suspend {
111209 self.getters.put(&queue_node);
112210 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
211 self.or_null_queue.put(&or_null_node);
113212
114213 self.dispatch();
115214 }
......@@ -139,7 +238,15 @@ pub fn Channel(comptime T: type) type {
139238 if (get_count == 0) break :one_dispatch;
140239
141240 const get_node = &self.getters.get().?.data;
142 get_node.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
241 switch (get_node.data) {
242 GetNode.Data.Normal => |info| {
243 info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
244 },
245 GetNode.Data.OrNull => |info| {
246 _ = self.or_null_queue.remove(info.or_null);
247 info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
248 },
249 }
143250 self.loop.onNextTick(get_node.tick_node);
144251 self.buffer_len -= 1;
145252
......@@ -151,7 +258,15 @@ pub fn Channel(comptime T: type) type {
151258 const get_node = &self.getters.get().?.data;
152259 const put_node = &self.putters.get().?.data;
153260
154 get_node.ptr.* = put_node.data;
261 switch (get_node.data) {
262 GetNode.Data.Normal => |info| {
263 info.ptr.* = put_node.data;
264 },
265 GetNode.Data.OrNull => |info| {
266 _ = self.or_null_queue.remove(info.or_null);
267 info.ptr.* = put_node.data;
268 },
269 }
155270 self.loop.onNextTick(get_node.tick_node);
156271 self.loop.onNextTick(put_node.tick_node);
157272
......@@ -176,6 +291,16 @@ pub fn Channel(comptime T: type) type {
176291 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
177292 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
178293
294 // All the "get or null" functions should resume now.
295 var remove_count: usize = 0;
296 while (self.or_null_queue.get()) |or_null_node| {
297 remove_count += @boolToInt(self.getters.remove(or_null_node.data));
298 self.loop.onNextTick(or_null_node.data.data.tick_node);
299 }
300 if (remove_count != 0) {
301 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, remove_count, AtomicOrder.SeqCst);
302 }
303
179304 // clear need-dispatch flag
180305 const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
181306 if (need_dispatch != 0) continue;
......@@ -226,6 +351,15 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {
226351 const value2_promise = try async channel.get();
227352 const value2 = await value2_promise;
228353 assert(value2 == 4567);
354
355 const value3_promise = try async channel.getOrNull();
356 const value3 = await value3_promise;
357 assert(value3 == null);
358
359 const last_put = try async testPut(channel, 4444);
360 const value4 = await try async channel.getOrNull();
361 assert(value4.? == 4444);
362 await last_put;
229363}
230364
231365async fn testChannelPutter(channel: *Channel(i32)) void {
......@@ -233,3 +367,6 @@ async fn testChannelPutter(channel: *Channel(i32)) void {
233367 await (async channel.put(4567) catch @panic("out of memory"));
234368}
235369
370async fn testPut(channel: *Channel(i32), value: i32) void {
371 await (async channel.put(value) catch @panic("out of memory"));
372}
std/event/fs.zig created+1347
......@@ -0,0 +1,1347 @@
1const builtin = @import("builtin");
2const std = @import("../index.zig");
3const event = std.event;
4const assert = std.debug.assert;
5const os = std.os;
6const mem = std.mem;
7const posix = os.posix;
8const windows = os.windows;
9const Loop = event.Loop;
10
11pub const RequestNode = std.atomic.Queue(Request).Node;
12
13pub const Request = struct {
14 msg: Msg,
15 finish: Finish,
16
17 pub const Finish = union(enum) {
18 TickNode: Loop.NextTickNode,
19 DeallocCloseOperation: *CloseOperation,
20 NoAction,
21 };
22
23 pub const Msg = union(enum) {
24 PWriteV: PWriteV,
25 PReadV: PReadV,
26 Open: Open,
27 Close: Close,
28 WriteFile: WriteFile,
29 End, // special - means the fs thread should exit
30
31 pub const PWriteV = struct {
32 fd: os.FileHandle,
33 iov: []os.posix.iovec_const,
34 offset: usize,
35 result: Error!void,
36
37 pub const Error = os.File.WriteError;
38 };
39
40 pub const PReadV = struct {
41 fd: os.FileHandle,
42 iov: []os.posix.iovec,
43 offset: usize,
44 result: Error!usize,
45
46 pub const Error = os.File.ReadError;
47 };
48
49 pub const Open = struct {
50 /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265
51 path: []const u8,
52 flags: u32,
53 mode: os.File.Mode,
54 result: Error!os.FileHandle,
55
56 pub const Error = os.File.OpenError;
57 };
58
59 pub const WriteFile = struct {
60 /// must be null terminated. TODO https://github.com/ziglang/zig/issues/265
61 path: []const u8,
62 contents: []const u8,
63 mode: os.File.Mode,
64 result: Error!void,
65
66 pub const Error = os.File.OpenError || os.File.WriteError;
67 };
68
69 pub const Close = struct {
70 fd: os.FileHandle,
71 };
72 };
73};
74
75/// data - just the inner references - must live until pwritev promise completes.
76pub async fn pwritev(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {
77 switch (builtin.os) {
78 builtin.Os.macosx,
79 builtin.Os.linux,
80 => return await (async pwritevPosix(loop, fd, data, offset) catch unreachable),
81 builtin.Os.windows => return await (async pwritevWindows(loop, fd, data, offset) catch unreachable),
82 else => @compileError("Unsupported OS"),
83 }
84}
85
86/// data - just the inner references - must live until pwritev promise completes.
87pub async fn pwritevWindows(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {
88 if (data.len == 0) return;
89 if (data.len == 1) return await (async pwriteWindows(loop, fd, data[0], offset) catch unreachable);
90
91 const data_copy = try std.mem.dupe(loop.allocator, []const u8, data);
92 defer loop.allocator.free(data_copy);
93
94 // TODO do these in parallel
95 var off = offset;
96 for (data_copy) |buf| {
97 try await (async pwriteWindows(loop, fd, buf, off) catch unreachable);
98 off += buf.len;
99 }
100}
101
102pub async fn pwriteWindows(loop: *Loop, fd: os.FileHandle, data: []const u8, offset: u64) os.WindowsWriteError!void {
103 // workaround for https://github.com/ziglang/zig/issues/1194
104 suspend {
105 resume @handle();
106 }
107
108 var resume_node = Loop.ResumeNode.Basic{
109 .base = Loop.ResumeNode{
110 .id = Loop.ResumeNode.Id.Basic,
111 .handle = @handle(),
112 },
113 };
114 const completion_key = @ptrToInt(&resume_node.base);
115 // TODO support concurrent async ops on the file handle
116 // we can do this by ignoring completion key and using @fieldParentPtr with the *Overlapped
117 _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, completion_key, undefined);
118 var overlapped = windows.OVERLAPPED{
119 .Internal = 0,
120 .InternalHigh = 0,
121 .Offset = @truncate(u32, offset),
122 .OffsetHigh = @truncate(u32, offset >> 32),
123 .hEvent = null,
124 };
125 loop.beginOneEvent();
126 errdefer loop.finishOneEvent();
127
128 errdefer {
129 _ = windows.CancelIoEx(fd, &overlapped);
130 }
131 suspend {
132 _ = windows.WriteFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &overlapped);
133 }
134 var bytes_transferred: windows.DWORD = undefined;
135 if (windows.GetOverlappedResult(fd, &overlapped, &bytes_transferred, windows.FALSE) == 0) {
136 const err = windows.GetLastError();
137 return switch (err) {
138 windows.ERROR.IO_PENDING => unreachable,
139 windows.ERROR.INVALID_USER_BUFFER => error.SystemResources,
140 windows.ERROR.NOT_ENOUGH_MEMORY => error.SystemResources,
141 windows.ERROR.OPERATION_ABORTED => error.OperationAborted,
142 windows.ERROR.NOT_ENOUGH_QUOTA => error.SystemResources,
143 windows.ERROR.BROKEN_PIPE => error.BrokenPipe,
144 else => os.unexpectedErrorWindows(err),
145 };
146 }
147}
148
149/// data - just the inner references - must live until pwritev promise completes.
150pub async fn pwritevPosix(loop: *Loop, fd: os.FileHandle, data: []const []const u8, offset: usize) !void {
151 // workaround for https://github.com/ziglang/zig/issues/1194
152 suspend {
153 resume @handle();
154 }
155
156 const iovecs = try loop.allocator.alloc(os.posix.iovec_const, data.len);
157 defer loop.allocator.free(iovecs);
158
159 for (data) |buf, i| {
160 iovecs[i] = os.posix.iovec_const{
161 .iov_base = buf.ptr,
162 .iov_len = buf.len,
163 };
164 }
165
166 var req_node = RequestNode{
167 .prev = null,
168 .next = null,
169 .data = Request{
170 .msg = Request.Msg{
171 .PWriteV = Request.Msg.PWriteV{
172 .fd = fd,
173 .iov = iovecs,
174 .offset = offset,
175 .result = undefined,
176 },
177 },
178 .finish = Request.Finish{
179 .TickNode = Loop.NextTickNode{
180 .prev = null,
181 .next = null,
182 .data = @handle(),
183 },
184 },
185 },
186 };
187
188 errdefer loop.posixFsCancel(&req_node);
189
190 suspend {
191 loop.posixFsRequest(&req_node);
192 }
193
194 return req_node.data.msg.PWriteV.result;
195}
196
197/// data - just the inner references - must live until preadv promise completes.
198pub async fn preadv(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset: usize) !usize {
199 assert(data.len != 0);
200 switch (builtin.os) {
201 builtin.Os.macosx,
202 builtin.Os.linux,
203 => return await (async preadvPosix(loop, fd, data, offset) catch unreachable),
204 builtin.Os.windows => return await (async preadvWindows(loop, fd, data, offset) catch unreachable),
205 else => @compileError("Unsupported OS"),
206 }
207}
208
209pub async fn preadvWindows(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset: u64) !usize {
210 assert(data.len != 0);
211 if (data.len == 1) return await (async preadWindows(loop, fd, data[0], offset) catch unreachable);
212
213 const data_copy = try std.mem.dupe(loop.allocator, []u8, data);
214 defer loop.allocator.free(data_copy);
215
216 // TODO do these in parallel?
217 var off: usize = 0;
218 var iov_i: usize = 0;
219 var inner_off: usize = 0;
220 while (true) {
221 const v = data_copy[iov_i];
222 const amt_read = try await (async preadWindows(loop, fd, v[inner_off .. v.len - inner_off], offset + off) catch unreachable);
223 off += amt_read;
224 inner_off += amt_read;
225 if (inner_off == v.len) {
226 iov_i += 1;
227 inner_off = 0;
228 if (iov_i == data_copy.len) {
229 return off;
230 }
231 }
232 if (amt_read == 0) return off; // EOF
233 }
234}
235
236pub async fn preadWindows(loop: *Loop, fd: os.FileHandle, data: []u8, offset: u64) !usize {
237 // workaround for https://github.com/ziglang/zig/issues/1194
238 suspend {
239 resume @handle();
240 }
241
242 var resume_node = Loop.ResumeNode.Basic{
243 .base = Loop.ResumeNode{
244 .id = Loop.ResumeNode.Id.Basic,
245 .handle = @handle(),
246 },
247 };
248 const completion_key = @ptrToInt(&resume_node.base);
249 // TODO support concurrent async ops on the file handle
250 // we can do this by ignoring completion key and using @fieldParentPtr with the *Overlapped
251 _ = try os.windowsCreateIoCompletionPort(fd, loop.os_data.io_port, completion_key, undefined);
252 var overlapped = windows.OVERLAPPED{
253 .Internal = 0,
254 .InternalHigh = 0,
255 .Offset = @truncate(u32, offset),
256 .OffsetHigh = @truncate(u32, offset >> 32),
257 .hEvent = null,
258 };
259 loop.beginOneEvent();
260 errdefer loop.finishOneEvent();
261
262 errdefer {
263 _ = windows.CancelIoEx(fd, &overlapped);
264 }
265 suspend {
266 _ = windows.ReadFile(fd, data.ptr, @intCast(windows.DWORD, data.len), null, &overlapped);
267 }
268 var bytes_transferred: windows.DWORD = undefined;
269 if (windows.GetOverlappedResult(fd, &overlapped, &bytes_transferred, windows.FALSE) == 0) {
270 const err = windows.GetLastError();
271 return switch (err) {
272 windows.ERROR.IO_PENDING => unreachable,
273 windows.ERROR.OPERATION_ABORTED => error.OperationAborted,
274 windows.ERROR.BROKEN_PIPE => error.BrokenPipe,
275 else => os.unexpectedErrorWindows(err),
276 };
277 }
278 return usize(bytes_transferred);
279}
280
281/// data - just the inner references - must live until preadv promise completes.
282pub async fn preadvPosix(loop: *Loop, fd: os.FileHandle, data: []const []u8, offset: usize) !usize {
283 // workaround for https://github.com/ziglang/zig/issues/1194
284 suspend {
285 resume @handle();
286 }
287
288 const iovecs = try loop.allocator.alloc(os.posix.iovec, data.len);
289 defer loop.allocator.free(iovecs);
290
291 for (data) |buf, i| {
292 iovecs[i] = os.posix.iovec{
293 .iov_base = buf.ptr,
294 .iov_len = buf.len,
295 };
296 }
297
298 var req_node = RequestNode{
299 .prev = null,
300 .next = null,
301 .data = Request{
302 .msg = Request.Msg{
303 .PReadV = Request.Msg.PReadV{
304 .fd = fd,
305 .iov = iovecs,
306 .offset = offset,
307 .result = undefined,
308 },
309 },
310 .finish = Request.Finish{
311 .TickNode = Loop.NextTickNode{
312 .prev = null,
313 .next = null,
314 .data = @handle(),
315 },
316 },
317 },
318 };
319
320 errdefer loop.posixFsCancel(&req_node);
321
322 suspend {
323 loop.posixFsRequest(&req_node);
324 }
325
326 return req_node.data.msg.PReadV.result;
327}
328
329pub async fn openPosix(
330 loop: *Loop,
331 path: []const u8,
332 flags: u32,
333 mode: os.File.Mode,
334) os.File.OpenError!os.FileHandle {
335 // workaround for https://github.com/ziglang/zig/issues/1194
336 suspend {
337 resume @handle();
338 }
339
340 const path_c = try std.os.toPosixPath(path);
341
342 var req_node = RequestNode{
343 .prev = null,
344 .next = null,
345 .data = Request{
346 .msg = Request.Msg{
347 .Open = Request.Msg.Open{
348 .path = path_c[0..path.len],
349 .flags = flags,
350 .mode = mode,
351 .result = undefined,
352 },
353 },
354 .finish = Request.Finish{
355 .TickNode = Loop.NextTickNode{
356 .prev = null,
357 .next = null,
358 .data = @handle(),
359 },
360 },
361 },
362 };
363
364 errdefer loop.posixFsCancel(&req_node);
365
366 suspend {
367 loop.posixFsRequest(&req_node);
368 }
369
370 return req_node.data.msg.Open.result;
371}
372
373pub async fn openRead(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle {
374 switch (builtin.os) {
375 builtin.Os.macosx, builtin.Os.linux => {
376 const flags = posix.O_LARGEFILE | posix.O_RDONLY | posix.O_CLOEXEC;
377 return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable);
378 },
379
380 builtin.Os.windows => return os.windowsOpen(
381 path,
382 windows.GENERIC_READ,
383 windows.FILE_SHARE_READ,
384 windows.OPEN_EXISTING,
385 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,
386 ),
387
388 else => @compileError("Unsupported OS"),
389 }
390}
391
392/// Creates if does not exist. Truncates the file if it exists.
393/// Uses the default mode.
394pub async fn openWrite(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle {
395 return await (async openWriteMode(loop, path, os.File.default_mode) catch unreachable);
396}
397
398/// Creates if does not exist. Truncates the file if it exists.
399pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: os.File.Mode) os.File.OpenError!os.FileHandle {
400 switch (builtin.os) {
401 builtin.Os.macosx,
402 builtin.Os.linux,
403 => {
404 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
405 return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable);
406 },
407 builtin.Os.windows => return os.windowsOpen(
408 path,
409 windows.GENERIC_WRITE,
410 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
411 windows.CREATE_ALWAYS,
412 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,
413 ),
414 else => @compileError("Unsupported OS"),
415 }
416}
417
418/// Creates if does not exist. Does not truncate.
419pub async fn openReadWrite(
420 loop: *Loop,
421 path: []const u8,
422 mode: os.File.Mode,
423) os.File.OpenError!os.FileHandle {
424 switch (builtin.os) {
425 builtin.Os.macosx, builtin.Os.linux => {
426 const flags = posix.O_LARGEFILE | posix.O_RDWR | posix.O_CREAT | posix.O_CLOEXEC;
427 return await (async openPosix(loop, path, flags, mode) catch unreachable);
428 },
429
430 builtin.Os.windows => return os.windowsOpen(
431 path,
432 windows.GENERIC_WRITE | windows.GENERIC_READ,
433 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
434 windows.OPEN_ALWAYS,
435 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,
436 ),
437
438 else => @compileError("Unsupported OS"),
439 }
440}
441
442/// This abstraction helps to close file handles in defer expressions
443/// without the possibility of failure and without the use of suspend points.
444/// Start a `CloseOperation` before opening a file, so that you can defer
445/// `CloseOperation.finish`.
446/// If you call `setHandle` then finishing will close the fd; otherwise finishing
447/// will deallocate the `CloseOperation`.
448pub const CloseOperation = struct {
449 loop: *Loop,
450 os_data: OsData,
451
452 const OsData = switch (builtin.os) {
453 builtin.Os.linux, builtin.Os.macosx => OsDataPosix,
454
455 builtin.Os.windows => struct {
456 handle: ?os.FileHandle,
457 },
458
459 else => @compileError("Unsupported OS"),
460 };
461
462 const OsDataPosix = struct {
463 have_fd: bool,
464 close_req_node: RequestNode,
465 };
466
467 pub fn start(loop: *Loop) (error{OutOfMemory}!*CloseOperation) {
468 const self = try loop.allocator.createOne(CloseOperation);
469 self.* = CloseOperation{
470 .loop = loop,
471 .os_data = switch (builtin.os) {
472 builtin.Os.linux, builtin.Os.macosx => initOsDataPosix(self),
473 builtin.Os.windows => OsData{ .handle = null },
474 else => @compileError("Unsupported OS"),
475 },
476 };
477 return self;
478 }
479
480 fn initOsDataPosix(self: *CloseOperation) OsData {
481 return OsData{
482 .have_fd = false,
483 .close_req_node = RequestNode{
484 .prev = null,
485 .next = null,
486 .data = Request{
487 .msg = Request.Msg{
488 .Close = Request.Msg.Close{ .fd = undefined },
489 },
490 .finish = Request.Finish{ .DeallocCloseOperation = self },
491 },
492 },
493 };
494 }
495
496 /// Defer this after creating.
497 pub fn finish(self: *CloseOperation) void {
498 switch (builtin.os) {
499 builtin.Os.linux,
500 builtin.Os.macosx,
501 => {
502 if (self.os_data.have_fd) {
503 self.loop.posixFsRequest(&self.os_data.close_req_node);
504 } else {
505 self.loop.allocator.destroy(self);
506 }
507 },
508 builtin.Os.windows => {
509 if (self.os_data.handle) |handle| {
510 os.close(handle);
511 }
512 self.loop.allocator.destroy(self);
513 },
514 else => @compileError("Unsupported OS"),
515 }
516 }
517
518 pub fn setHandle(self: *CloseOperation, handle: os.FileHandle) void {
519 switch (builtin.os) {
520 builtin.Os.linux,
521 builtin.Os.macosx,
522 => {
523 self.os_data.close_req_node.data.msg.Close.fd = handle;
524 self.os_data.have_fd = true;
525 },
526 builtin.Os.windows => {
527 self.os_data.handle = handle;
528 },
529 else => @compileError("Unsupported OS"),
530 }
531 }
532
533 /// Undo a `setHandle`.
534 pub fn clearHandle(self: *CloseOperation) void {
535 switch (builtin.os) {
536 builtin.Os.linux,
537 builtin.Os.macosx,
538 => {
539 self.os_data.have_fd = false;
540 },
541 builtin.Os.windows => {
542 self.os_data.handle = null;
543 },
544 else => @compileError("Unsupported OS"),
545 }
546 }
547
548 pub fn getHandle(self: *CloseOperation) os.FileHandle {
549 switch (builtin.os) {
550 builtin.Os.linux,
551 builtin.Os.macosx,
552 => {
553 assert(self.os_data.have_fd);
554 return self.os_data.close_req_node.data.msg.Close.fd;
555 },
556 builtin.Os.windows => {
557 return self.os_data.handle.?;
558 },
559 else => @compileError("Unsupported OS"),
560 }
561 }
562};
563
564/// contents must remain alive until writeFile completes.
565/// TODO make this atomic or provide writeFileAtomic and rename this one to writeFileTruncate
566pub async fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void {
567 return await (async writeFileMode(loop, path, contents, os.File.default_mode) catch unreachable);
568}
569
570/// contents must remain alive until writeFile completes.
571pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: os.File.Mode) !void {
572 switch (builtin.os) {
573 builtin.Os.linux,
574 builtin.Os.macosx,
575 => return await (async writeFileModeThread(loop, path, contents, mode) catch unreachable),
576 builtin.Os.windows => return await (async writeFileWindows(loop, path, contents) catch unreachable),
577 else => @compileError("Unsupported OS"),
578 }
579}
580
581async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void {
582 const handle = try os.windowsOpen(
583 path,
584 windows.GENERIC_WRITE,
585 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
586 windows.CREATE_ALWAYS,
587 windows.FILE_ATTRIBUTE_NORMAL | windows.FILE_FLAG_OVERLAPPED,
588 );
589 defer os.close(handle);
590
591 try await (async pwriteWindows(loop, handle, contents, 0) catch unreachable);
592}
593
594async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: os.File.Mode) !void {
595 // workaround for https://github.com/ziglang/zig/issues/1194
596 suspend {
597 resume @handle();
598 }
599
600 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
601 defer loop.allocator.free(path_with_null);
602
603 var req_node = RequestNode{
604 .prev = null,
605 .next = null,
606 .data = Request{
607 .msg = Request.Msg{
608 .WriteFile = Request.Msg.WriteFile{
609 .path = path_with_null[0..path.len],
610 .contents = contents,
611 .mode = mode,
612 .result = undefined,
613 },
614 },
615 .finish = Request.Finish{
616 .TickNode = Loop.NextTickNode{
617 .prev = null,
618 .next = null,
619 .data = @handle(),
620 },
621 },
622 },
623 };
624
625 errdefer loop.posixFsCancel(&req_node);
626
627 suspend {
628 loop.posixFsRequest(&req_node);
629 }
630
631 return req_node.data.msg.WriteFile.result;
632}
633
634/// The promise resumes when the last data has been confirmed written, but before the file handle
635/// is closed.
636/// Caller owns returned memory.
637pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 {
638 var close_op = try CloseOperation.start(loop);
639 defer close_op.finish();
640
641 const fd = try await (async openRead(loop, file_path) catch unreachable);
642 close_op.setHandle(fd);
643
644 var list = std.ArrayList(u8).init(loop.allocator);
645 defer list.deinit();
646
647 while (true) {
648 try list.ensureCapacity(list.len + os.page_size);
649 const buf = list.items[list.len..];
650 const buf_array = [][]u8{buf};
651 const amt = try await (async preadv(loop, fd, buf_array, list.len) catch unreachable);
652 list.len += amt;
653 if (list.len > max_size) {
654 return error.FileTooBig;
655 }
656 if (amt < buf.len) {
657 return list.toOwnedSlice();
658 }
659 }
660}
661
662pub const WatchEventId = enum {
663 CloseWrite,
664 Delete,
665};
666
667pub const WatchEventError = error{
668 UserResourceLimitReached,
669 SystemResources,
670 AccessDenied,
671 Unexpected, // TODO remove this possibility
672};
673
674pub fn Watch(comptime V: type) type {
675 return struct {
676 channel: *event.Channel(Event.Error!Event),
677 os_data: OsData,
678
679 const OsData = switch (builtin.os) {
680 builtin.Os.macosx => struct {
681 file_table: FileTable,
682 table_lock: event.Lock,
683
684 const FileTable = std.AutoHashMap([]const u8, *Put);
685 const Put = struct {
686 putter: promise,
687 value_ptr: *V,
688 };
689 },
690
691 builtin.Os.linux => LinuxOsData,
692 builtin.Os.windows => WindowsOsData,
693
694 else => @compileError("Unsupported OS"),
695 };
696
697 const WindowsOsData = struct {
698 table_lock: event.Lock,
699 dir_table: DirTable,
700 all_putters: std.atomic.Queue(promise),
701 ref_count: std.atomic.Int(usize),
702
703 const DirTable = std.AutoHashMap([]const u8, *Dir);
704 const FileTable = std.AutoHashMap([]const u16, V);
705
706 const Dir = struct {
707 putter: promise,
708 file_table: FileTable,
709 table_lock: event.Lock,
710 };
711 };
712
713 const LinuxOsData = struct {
714 putter: promise,
715 inotify_fd: i32,
716 wd_table: WdTable,
717 table_lock: event.Lock,
718
719 const WdTable = std.AutoHashMap(i32, Dir);
720 const FileTable = std.AutoHashMap([]const u8, V);
721
722 const Dir = struct {
723 dirname: []const u8,
724 file_table: FileTable,
725 };
726 };
727
728 const FileToHandle = std.AutoHashMap([]const u8, promise);
729
730 const Self = this;
731
732 pub const Event = struct {
733 id: Id,
734 data: V,
735
736 pub const Id = WatchEventId;
737 pub const Error = WatchEventError;
738 };
739
740 pub fn create(loop: *Loop, event_buf_count: usize) !*Self {
741 const channel = try event.Channel(Self.Event.Error!Self.Event).create(loop, event_buf_count);
742 errdefer channel.destroy();
743
744 switch (builtin.os) {
745 builtin.Os.linux => {
746 const inotify_fd = try os.linuxINotifyInit1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
747 errdefer os.close(inotify_fd);
748
749 var result: *Self = undefined;
750 _ = try async<loop.allocator> linuxEventPutter(inotify_fd, channel, &result);
751 return result;
752 },
753
754 builtin.Os.windows => {
755 const self = try loop.allocator.createOne(Self);
756 errdefer loop.allocator.destroy(self);
757 self.* = Self{
758 .channel = channel,
759 .os_data = OsData{
760 .table_lock = event.Lock.init(loop),
761 .dir_table = OsData.DirTable.init(loop.allocator),
762 .ref_count = std.atomic.Int(usize).init(1),
763 .all_putters = std.atomic.Queue(promise).init(),
764 },
765 };
766 return self;
767 },
768
769 builtin.Os.macosx => {
770 const self = try loop.allocator.createOne(Self);
771 errdefer loop.allocator.destroy(self);
772
773 self.* = Self{
774 .channel = channel,
775 .os_data = OsData{
776 .table_lock = event.Lock.init(loop),
777 .file_table = OsData.FileTable.init(loop.allocator),
778 },
779 };
780 return self;
781 },
782 else => @compileError("Unsupported OS"),
783 }
784 }
785
786 /// All addFile calls and removeFile calls must have completed.
787 pub fn destroy(self: *Self) void {
788 switch (builtin.os) {
789 builtin.Os.macosx => {
790 // TODO we need to cancel the coroutines before destroying the lock
791 self.os_data.table_lock.deinit();
792 var it = self.os_data.file_table.iterator();
793 while (it.next()) |entry| {
794 cancel entry.value.putter;
795 self.channel.loop.allocator.free(entry.key);
796 }
797 self.channel.destroy();
798 },
799 builtin.Os.linux => cancel self.os_data.putter,
800 builtin.Os.windows => {
801 while (self.os_data.all_putters.get()) |putter_node| {
802 cancel putter_node.data;
803 }
804 self.deref();
805 },
806 else => @compileError("Unsupported OS"),
807 }
808 }
809
810 fn ref(self: *Self) void {
811 _ = self.os_data.ref_count.incr();
812 }
813
814 fn deref(self: *Self) void {
815 if (self.os_data.ref_count.decr() == 1) {
816 const allocator = self.channel.loop.allocator;
817 self.os_data.table_lock.deinit();
818 var it = self.os_data.dir_table.iterator();
819 while (it.next()) |entry| {
820 allocator.free(entry.key);
821 allocator.destroy(entry.value);
822 }
823 self.os_data.dir_table.deinit();
824 self.channel.destroy();
825 allocator.destroy(self);
826 }
827 }
828
829 pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
830 switch (builtin.os) {
831 builtin.Os.macosx => return await (async addFileMacosx(self, file_path, value) catch unreachable),
832 builtin.Os.linux => return await (async addFileLinux(self, file_path, value) catch unreachable),
833 builtin.Os.windows => return await (async addFileWindows(self, file_path, value) catch unreachable),
834 else => @compileError("Unsupported OS"),
835 }
836 }
837
838 async fn addFileMacosx(self: *Self, file_path: []const u8, value: V) !?V {
839 const resolved_path = try os.path.resolve(self.channel.loop.allocator, file_path);
840 var resolved_path_consumed = false;
841 defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path);
842
843 var close_op = try CloseOperation.start(self.channel.loop);
844 var close_op_consumed = false;
845 defer if (!close_op_consumed) close_op.finish();
846
847 const flags = posix.O_SYMLINK | posix.O_EVTONLY;
848 const mode = 0;
849 const fd = try await (async openPosix(self.channel.loop, resolved_path, flags, mode) catch unreachable);
850 close_op.setHandle(fd);
851
852 var put_data: *OsData.Put = undefined;
853 const putter = try async self.kqPutEvents(close_op, value, &put_data);
854 close_op_consumed = true;
855 errdefer cancel putter;
856
857 const result = blk: {
858 const held = await (async self.os_data.table_lock.acquire() catch unreachable);
859 defer held.release();
860
861 const gop = try self.os_data.file_table.getOrPut(resolved_path);
862 if (gop.found_existing) {
863 const prev_value = gop.kv.value.value_ptr.*;
864 cancel gop.kv.value.putter;
865 gop.kv.value = put_data;
866 break :blk prev_value;
867 } else {
868 resolved_path_consumed = true;
869 gop.kv.value = put_data;
870 break :blk null;
871 }
872 };
873
874 return result;
875 }
876
877 async fn kqPutEvents(self: *Self, close_op: *CloseOperation, value: V, out_put: **OsData.Put) void {
878 // TODO https://github.com/ziglang/zig/issues/1194
879 suspend {
880 resume @handle();
881 }
882
883 var value_copy = value;
884 var put = OsData.Put{
885 .putter = @handle(),
886 .value_ptr = &value_copy,
887 };
888 out_put.* = &put;
889 self.channel.loop.beginOneEvent();
890
891 defer {
892 close_op.finish();
893 self.channel.loop.finishOneEvent();
894 }
895
896 while (true) {
897 if (await (async self.channel.loop.bsdWaitKev(
898 @intCast(usize, close_op.getHandle()),
899 posix.EVFILT_VNODE,
900 posix.NOTE_WRITE | posix.NOTE_DELETE,
901 ) catch unreachable)) |kev| {
902 // TODO handle EV_ERROR
903 if (kev.fflags & posix.NOTE_DELETE != 0) {
904 await (async self.channel.put(Self.Event{
905 .id = Event.Id.Delete,
906 .data = value_copy,
907 }) catch unreachable);
908 } else if (kev.fflags & posix.NOTE_WRITE != 0) {
909 await (async self.channel.put(Self.Event{
910 .id = Event.Id.CloseWrite,
911 .data = value_copy,
912 }) catch unreachable);
913 }
914 } else |err| switch (err) {
915 error.EventNotFound => unreachable,
916 error.ProcessNotFound => unreachable,
917 error.AccessDenied, error.SystemResources => {
918 // TODO https://github.com/ziglang/zig/issues/769
919 const casted_err = @errSetCast(error{
920 AccessDenied,
921 SystemResources,
922 }, err);
923 await (async self.channel.put(casted_err) catch unreachable);
924 },
925 }
926 }
927 }
928
929 async fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V {
930 const value_copy = value;
931
932 const dirname = os.path.dirname(file_path) orelse ".";
933 const dirname_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, dirname);
934 var dirname_with_null_consumed = false;
935 defer if (!dirname_with_null_consumed) self.channel.loop.allocator.free(dirname_with_null);
936
937 const basename = os.path.basename(file_path);
938 const basename_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, basename);
939 var basename_with_null_consumed = false;
940 defer if (!basename_with_null_consumed) self.channel.loop.allocator.free(basename_with_null);
941
942 const wd = try os.linuxINotifyAddWatchC(
943 self.os_data.inotify_fd,
944 dirname_with_null.ptr,
945 os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_EXCL_UNLINK,
946 );
947 // wd is either a newly created watch or an existing one.
948
949 const held = await (async self.os_data.table_lock.acquire() catch unreachable);
950 defer held.release();
951
952 const gop = try self.os_data.wd_table.getOrPut(wd);
953 if (!gop.found_existing) {
954 gop.kv.value = OsData.Dir{
955 .dirname = dirname_with_null,
956 .file_table = OsData.FileTable.init(self.channel.loop.allocator),
957 };
958 dirname_with_null_consumed = true;
959 }
960 const dir = &gop.kv.value;
961
962 const file_table_gop = try dir.file_table.getOrPut(basename_with_null);
963 if (file_table_gop.found_existing) {
964 const prev_value = file_table_gop.kv.value;
965 file_table_gop.kv.value = value_copy;
966 return prev_value;
967 } else {
968 file_table_gop.kv.value = value_copy;
969 basename_with_null_consumed = true;
970 return null;
971 }
972 }
973
974 async fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {
975 const value_copy = value;
976 // TODO we might need to convert dirname and basename to canonical file paths ("short"?)
977
978 const dirname = try std.mem.dupe(self.channel.loop.allocator, u8, os.path.dirname(file_path) orelse ".");
979 var dirname_consumed = false;
980 defer if (!dirname_consumed) self.channel.loop.allocator.free(dirname);
981
982 const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, dirname);
983 defer self.channel.loop.allocator.free(dirname_utf16le);
984
985 // TODO https://github.com/ziglang/zig/issues/265
986 const basename = os.path.basename(file_path);
987 const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename);
988 var basename_utf16le_null_consumed = false;
989 defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null);
990 const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
991
992 const dir_handle = windows.CreateFileW(
993 dirname_utf16le.ptr,
994 windows.FILE_LIST_DIRECTORY,
995 windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE,
996 null,
997 windows.OPEN_EXISTING,
998 windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED,
999 null,
1000 );
1001 if (dir_handle == windows.INVALID_HANDLE_VALUE) {
1002 const err = windows.GetLastError();
1003 switch (err) {
1004 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
1005 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
1006 else => return os.unexpectedErrorWindows(err),
1007 }
1008 }
1009 var dir_handle_consumed = false;
1010 defer if (!dir_handle_consumed) os.close(dir_handle);
1011
1012 const held = await (async self.os_data.table_lock.acquire() catch unreachable);
1013 defer held.release();
1014
1015 const gop = try self.os_data.dir_table.getOrPut(dirname);
1016 if (gop.found_existing) {
1017 const dir = gop.kv.value;
1018 const held_dir_lock = await (async dir.table_lock.acquire() catch unreachable);
1019 defer held_dir_lock.release();
1020
1021 const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null);
1022 if (file_gop.found_existing) {
1023 const prev_value = file_gop.kv.value;
1024 file_gop.kv.value = value_copy;
1025 return prev_value;
1026 } else {
1027 file_gop.kv.value = value_copy;
1028 basename_utf16le_null_consumed = true;
1029 return null;
1030 }
1031 } else {
1032 errdefer _ = self.os_data.dir_table.remove(dirname);
1033 const dir = try self.channel.loop.allocator.createOne(OsData.Dir);
1034 errdefer self.channel.loop.allocator.destroy(dir);
1035
1036 dir.* = OsData.Dir{
1037 .file_table = OsData.FileTable.init(self.channel.loop.allocator),
1038 .table_lock = event.Lock.init(self.channel.loop),
1039 .putter = undefined,
1040 };
1041 gop.kv.value = dir;
1042 assert((try dir.file_table.put(basename_utf16le_no_null, value_copy)) == null);
1043 basename_utf16le_null_consumed = true;
1044
1045 dir.putter = try async self.windowsDirReader(dir_handle, dir);
1046 dir_handle_consumed = true;
1047
1048 dirname_consumed = true;
1049
1050 return null;
1051 }
1052 }
1053
1054 async fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void {
1055 // TODO https://github.com/ziglang/zig/issues/1194
1056 suspend {
1057 resume @handle();
1058 }
1059
1060 self.ref();
1061 defer self.deref();
1062
1063 defer os.close(dir_handle);
1064
1065 var putter_node = std.atomic.Queue(promise).Node{
1066 .data = @handle(),
1067 .prev = null,
1068 .next = null,
1069 };
1070 self.os_data.all_putters.put(&putter_node);
1071 defer _ = self.os_data.all_putters.remove(&putter_node);
1072
1073 var resume_node = Loop.ResumeNode.Basic{
1074 .base = Loop.ResumeNode{
1075 .id = Loop.ResumeNode.Id.Basic,
1076 .handle = @handle(),
1077 },
1078 };
1079 const completion_key = @ptrToInt(&resume_node.base);
1080 var overlapped = windows.OVERLAPPED{
1081 .Internal = 0,
1082 .InternalHigh = 0,
1083 .Offset = 0,
1084 .OffsetHigh = 0,
1085 .hEvent = null,
1086 };
1087 var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined;
1088
1089 // TODO handle this error not in the channel but in the setup
1090 _ = os.windowsCreateIoCompletionPort(
1091 dir_handle,
1092 self.channel.loop.os_data.io_port,
1093 completion_key,
1094 undefined,
1095 ) catch |err| {
1096 await (async self.channel.put(err) catch unreachable);
1097 return;
1098 };
1099
1100 while (true) {
1101 {
1102 // TODO only 1 beginOneEvent for the whole coroutine
1103 self.channel.loop.beginOneEvent();
1104 errdefer self.channel.loop.finishOneEvent();
1105 errdefer {
1106 _ = windows.CancelIoEx(dir_handle, &overlapped);
1107 }
1108 suspend {
1109 _ = windows.ReadDirectoryChangesW(
1110 dir_handle,
1111 &event_buf,
1112 @intCast(windows.DWORD, event_buf.len),
1113 windows.FALSE, // watch subtree
1114 windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME |
1115 windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE |
1116 windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |
1117 windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,
1118 null, // number of bytes transferred (unused for async)
1119 &overlapped,
1120 null, // completion routine - unused because we use IOCP
1121 );
1122 }
1123 }
1124 var bytes_transferred: windows.DWORD = undefined;
1125 if (windows.GetOverlappedResult(dir_handle, &overlapped, &bytes_transferred, windows.FALSE) == 0) {
1126 const errno = windows.GetLastError();
1127 const err = switch (errno) {
1128 else => os.unexpectedErrorWindows(errno),
1129 };
1130 await (async self.channel.put(err) catch unreachable);
1131 } else {
1132 // can't use @bytesToSlice because of the special variable length name field
1133 var ptr = event_buf[0..].ptr;
1134 const end_ptr = ptr + bytes_transferred;
1135 var ev: *windows.FILE_NOTIFY_INFORMATION = undefined;
1136 while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += ev.NextEntryOffset) {
1137 ev = @ptrCast(*windows.FILE_NOTIFY_INFORMATION, ptr);
1138 const emit = switch (ev.Action) {
1139 windows.FILE_ACTION_REMOVED => WatchEventId.Delete,
1140 windows.FILE_ACTION_MODIFIED => WatchEventId.CloseWrite,
1141 else => null,
1142 };
1143 if (emit) |id| {
1144 const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2];
1145 const user_value = blk: {
1146 const held = await (async dir.table_lock.acquire() catch unreachable);
1147 defer held.release();
1148
1149 if (dir.file_table.get(basename_utf16le)) |entry| {
1150 break :blk entry.value;
1151 } else {
1152 break :blk null;
1153 }
1154 };
1155 if (user_value) |v| {
1156 await (async self.channel.put(Event{
1157 .id = id,
1158 .data = v,
1159 }) catch unreachable);
1160 }
1161 }
1162 if (ev.NextEntryOffset == 0) break;
1163 }
1164 }
1165 }
1166 }
1167
1168 pub async fn removeFile(self: *Self, file_path: []const u8) ?V {
1169 @panic("TODO");
1170 }
1171
1172 async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event.Error!Event), out_watch: **Self) void {
1173 // TODO https://github.com/ziglang/zig/issues/1194
1174 suspend {
1175 resume @handle();
1176 }
1177
1178 const loop = channel.loop;
1179
1180 var watch = Self{
1181 .channel = channel,
1182 .os_data = OsData{
1183 .putter = @handle(),
1184 .inotify_fd = inotify_fd,
1185 .wd_table = OsData.WdTable.init(loop.allocator),
1186 .table_lock = event.Lock.init(loop),
1187 },
1188 };
1189 out_watch.* = &watch;
1190
1191 loop.beginOneEvent();
1192
1193 defer {
1194 watch.os_data.table_lock.deinit();
1195 var wd_it = watch.os_data.wd_table.iterator();
1196 while (wd_it.next()) |wd_entry| {
1197 var file_it = wd_entry.value.file_table.iterator();
1198 while (file_it.next()) |file_entry| {
1199 loop.allocator.free(file_entry.key);
1200 }
1201 loop.allocator.free(wd_entry.value.dirname);
1202 }
1203 loop.finishOneEvent();
1204 os.close(inotify_fd);
1205 channel.destroy();
1206 }
1207
1208 var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
1209
1210 while (true) {
1211 const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);
1212 const errno = os.linux.getErrno(rc);
1213 switch (errno) {
1214 0 => {
1215 // can't use @bytesToSlice because of the special variable length name field
1216 var ptr = event_buf[0..].ptr;
1217 const end_ptr = ptr + event_buf.len;
1218 var ev: *os.linux.inotify_event = undefined;
1219 while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) {
1220 ev = @ptrCast(*os.linux.inotify_event, ptr);
1221 if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
1222 const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);
1223 const basename_with_null = basename_ptr[0 .. std.cstr.len(basename_ptr) + 1];
1224 const user_value = blk: {
1225 const held = await (async watch.os_data.table_lock.acquire() catch unreachable);
1226 defer held.release();
1227
1228 const dir = &watch.os_data.wd_table.get(ev.wd).?.value;
1229 if (dir.file_table.get(basename_with_null)) |entry| {
1230 break :blk entry.value;
1231 } else {
1232 break :blk null;
1233 }
1234 };
1235 if (user_value) |v| {
1236 await (async channel.put(Event{
1237 .id = WatchEventId.CloseWrite,
1238 .data = v,
1239 }) catch unreachable);
1240 }
1241 }
1242 }
1243 },
1244 os.linux.EINTR => continue,
1245 os.linux.EINVAL => unreachable,
1246 os.linux.EFAULT => unreachable,
1247 os.linux.EAGAIN => {
1248 (await (async loop.linuxWaitFd(
1249 inotify_fd,
1250 os.linux.EPOLLET | os.linux.EPOLLIN,
1251 ) catch unreachable)) catch |err| {
1252 const transformed_err = switch (err) {
1253 error.InvalidFileDescriptor => unreachable,
1254 error.FileDescriptorAlreadyPresentInSet => unreachable,
1255 error.InvalidSyscall => unreachable,
1256 error.OperationCausesCircularLoop => unreachable,
1257 error.FileDescriptorNotRegistered => unreachable,
1258 error.SystemResources => error.SystemResources,
1259 error.UserResourceLimitReached => error.UserResourceLimitReached,
1260 error.FileDescriptorIncompatibleWithEpoll => unreachable,
1261 error.Unexpected => unreachable,
1262 };
1263 await (async channel.put(transformed_err) catch unreachable);
1264 };
1265 },
1266 else => unreachable,
1267 }
1268 }
1269 }
1270 };
1271}
1272
1273const test_tmp_dir = "std_event_fs_test";
1274
1275test "write a file, watch it, write it again" {
1276 var da = std.heap.DirectAllocator.init();
1277 defer da.deinit();
1278
1279 const allocator = &da.allocator;
1280
1281 // TODO move this into event loop too
1282 try os.makePath(allocator, test_tmp_dir);
1283 defer os.deleteTree(allocator, test_tmp_dir) catch {};
1284
1285 var loop: Loop = undefined;
1286 try loop.initMultiThreaded(allocator);
1287 defer loop.deinit();
1288
1289 var result: error!void = error.ResultNeverWritten;
1290 const handle = try async<allocator> testFsWatchCantFail(&loop, &result);
1291 defer cancel handle;
1292
1293 loop.run();
1294 return result;
1295}
1296
1297async fn testFsWatchCantFail(loop: *Loop, result: *(error!void)) void {
1298 result.* = await async testFsWatch(loop) catch unreachable;
1299}
1300
1301async fn testFsWatch(loop: *Loop) !void {
1302 const file_path = try os.path.join(loop.allocator, test_tmp_dir, "file.txt");
1303 defer loop.allocator.free(file_path);
1304
1305 const contents =
1306 \\line 1
1307 \\line 2
1308 ;
1309 const line2_offset = 7;
1310
1311 // first just write then read the file
1312 try await try async writeFile(loop, file_path, contents);
1313
1314 const read_contents = try await try async readFile(loop, file_path, 1024 * 1024);
1315 assert(mem.eql(u8, read_contents, contents));
1316
1317 // now watch the file
1318 var watch = try Watch(void).create(loop, 0);
1319 defer watch.destroy();
1320
1321 assert((try await try async watch.addFile(file_path, {})) == null);
1322
1323 const ev = try async watch.channel.get();
1324 var ev_consumed = false;
1325 defer if (!ev_consumed) cancel ev;
1326
1327 // overwrite line 2
1328 const fd = try await try async openReadWrite(loop, file_path, os.File.default_mode);
1329 {
1330 defer os.close(fd);
1331
1332 try await try async pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset);
1333 }
1334
1335 ev_consumed = true;
1336 switch ((try await ev).id) {
1337 WatchEventId.CloseWrite => {},
1338 WatchEventId.Delete => @panic("wrong event"),
1339 }
1340 const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);
1341 assert(mem.eql(u8, contents_updated,
1342 \\line 1
1343 \\lorem ipsum
1344 ));
1345
1346 // TODO test deleting the file and then re-adding it. we should get events for both
1347}
std/event/group.zig+13-15
......@@ -29,6 +29,17 @@ pub fn Group(comptime ReturnType: type) type {
2929 };
3030 }
3131
32 /// Cancel all the outstanding promises. Can be called even if wait was already called.
33 pub fn deinit(self: *Self) void {
34 while (self.coro_stack.pop()) |node| {
35 cancel node.data;
36 }
37 while (self.alloc_stack.pop()) |node| {
38 cancel node.data;
39 self.lock.loop.allocator.destroy(node);
40 }
41 }
42
3243 /// Add a promise to the group. Thread-safe.
3344 pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) {
3445 const node = try self.lock.loop.allocator.create(Stack.Node{
......@@ -88,7 +99,7 @@ pub fn Group(comptime ReturnType: type) type {
8899 await node.data;
89100 } else {
90101 (await node.data) catch |err| {
91 self.cancelAll();
102 self.deinit();
92103 return err;
93104 };
94105 }
......@@ -100,25 +111,12 @@ pub fn Group(comptime ReturnType: type) type {
100111 await handle;
101112 } else {
102113 (await handle) catch |err| {
103 self.cancelAll();
114 self.deinit();
104115 return err;
105116 };
106117 }
107118 }
108119 }
109
110 /// Cancel all the outstanding promises. May only be called if wait was never called.
111 /// TODO These should be `cancelasync` not `cancel`.
112 /// See https://github.com/ziglang/zig/issues/1261
113 pub fn cancelAll(self: *Self) void {
114 while (self.coro_stack.pop()) |node| {
115 cancel node.data;
116 }
117 while (self.alloc_stack.pop()) |node| {
118 cancel node.data;
119 self.lock.loop.allocator.destroy(node);
120 }
121 }
122120 };
123121}
124122
std/event/lock.zig+10-5
......@@ -9,6 +9,7 @@ const Loop = std.event.Loop;
99/// Thread-safe async/await lock.
1010/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
1111/// are resumed when the lock is released, in order.
12/// Allows only one actor to hold the lock.
1213pub const Lock = struct {
1314 loop: *Loop,
1415 shared_bit: u8, // TODO make this a bool
......@@ -90,13 +91,14 @@ pub const Lock = struct {
9091 }
9192
9293 pub async fn acquire(self: *Lock) Held {
94 // TODO explicitly put this memory in the coroutine frame #1194
9395 suspend {
94 // TODO explicitly put this memory in the coroutine frame #1194
95 var my_tick_node = Loop.NextTickNode{
96 .data = @handle(),
97 .next = undefined,
98 };
96 resume @handle();
97 }
98 var my_tick_node = Loop.NextTickNode.init(@handle());
9999
100 errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire
101 suspend {
100102 self.queue.put(&my_tick_node);
101103
102104 // At this point, we are in the queue, so we might have already been resumed and this coroutine
......@@ -146,6 +148,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
146148 }
147149 const handle1 = async lockRunner(lock) catch @panic("out of memory");
148150 var tick_node1 = Loop.NextTickNode{
151 .prev = undefined,
149152 .next = undefined,
150153 .data = handle1,
151154 };
......@@ -153,6 +156,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
153156
154157 const handle2 = async lockRunner(lock) catch @panic("out of memory");
155158 var tick_node2 = Loop.NextTickNode{
159 .prev = undefined,
156160 .next = undefined,
157161 .data = handle2,
158162 };
......@@ -160,6 +164,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
160164
161165 const handle3 = async lockRunner(lock) catch @panic("out of memory");
162166 var tick_node3 = Loop.NextTickNode{
167 .prev = undefined,
163168 .next = undefined,
164169 .data = handle3,
165170 };
std/event/loop.zig+337-99
......@@ -2,10 +2,12 @@ const std = @import("../index.zig");
22const builtin = @import("builtin");
33const assert = std.debug.assert;
44const mem = std.mem;
5const posix = std.os.posix;
6const windows = std.os.windows;
75const AtomicRmwOp = builtin.AtomicRmwOp;
86const AtomicOrder = builtin.AtomicOrder;
7const fs = std.event.fs;
8const os = std.os;
9const posix = os.posix;
10const windows = os.windows;
911
1012pub const Loop = struct {
1113 allocator: *mem.Allocator,
......@@ -13,7 +15,7 @@ pub const Loop = struct {
1315 os_data: OsData,
1416 final_resume_node: ResumeNode,
1517 pending_event_count: usize,
16 extra_threads: []*std.os.Thread,
18 extra_threads: []*os.Thread,
1719
1820 // pre-allocated eventfds. all permanently active.
1921 // this is how we send promises to be resumed on other threads.
......@@ -50,6 +52,22 @@ pub const Loop = struct {
5052 base: ResumeNode,
5153 kevent: posix.Kevent,
5254 };
55
56 pub const Basic = switch (builtin.os) {
57 builtin.Os.macosx => MacOsBasic,
58 builtin.Os.linux => struct {
59 base: ResumeNode,
60 },
61 builtin.Os.windows => struct {
62 base: ResumeNode,
63 },
64 else => @compileError("unsupported OS"),
65 };
66
67 const MacOsBasic = struct {
68 base: ResumeNode,
69 kev: posix.Kevent,
70 };
5371 };
5472
5573 /// After initialization, call run().
......@@ -65,7 +83,7 @@ pub const Loop = struct {
6583 /// TODO copy elision / named return values so that the threads referencing *Loop
6684 /// have the correct pointer value.
6785 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
68 const core_count = try std.os.cpuCount(allocator);
86 const core_count = try os.cpuCount(allocator);
6987 return self.initInternal(allocator, core_count);
7088 }
7189
......@@ -92,7 +110,7 @@ pub const Loop = struct {
92110 );
93111 errdefer self.allocator.free(self.eventfd_resume_nodes);
94112
95 self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count);
113 self.extra_threads = try self.allocator.alloc(*os.Thread, extra_thread_count);
96114 errdefer self.allocator.free(self.extra_threads);
97115
98116 try self.initOsData(extra_thread_count);
......@@ -104,17 +122,30 @@ pub const Loop = struct {
104122 self.allocator.free(self.extra_threads);
105123 }
106124
107 const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError ||
108 std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError ||
109 std.os.WindowsCreateIoCompletionPortError;
125 const InitOsDataError = os.LinuxEpollCreateError || mem.Allocator.Error || os.LinuxEventFdError ||
126 os.SpawnThreadError || os.LinuxEpollCtlError || os.BsdKEventError ||
127 os.WindowsCreateIoCompletionPortError;
110128
111129 const wakeup_bytes = []u8{0x1} ** 8;
112130
113131 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {
114132 switch (builtin.os) {
115133 builtin.Os.linux => {
134 self.os_data.fs_queue = std.atomic.Queue(fs.Request).init();
135 self.os_data.fs_queue_item = 0;
136 // we need another thread for the file system because Linux does not have an async
137 // file system I/O API.
138 self.os_data.fs_end_request = fs.RequestNode{
139 .prev = undefined,
140 .next = undefined,
141 .data = fs.Request{
142 .msg = fs.Request.Msg.End,
143 .finish = fs.Request.Finish.NoAction,
144 },
145 };
146
116147 errdefer {
117 while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);
148 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
118149 }
119150 for (self.eventfd_resume_nodes) |*eventfd_node| {
120151 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
......@@ -123,7 +154,7 @@ pub const Loop = struct {
123154 .id = ResumeNode.Id.EventFd,
124155 .handle = undefined,
125156 },
126 .eventfd = try std.os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK),
157 .eventfd = try os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK),
127158 .epoll_op = posix.EPOLL_CTL_ADD,
128159 },
129160 .next = undefined,
......@@ -131,44 +162,62 @@ pub const Loop = struct {
131162 self.available_eventfd_resume_nodes.push(eventfd_node);
132163 }
133164
134 self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC);
135 errdefer std.os.close(self.os_data.epollfd);
165 self.os_data.epollfd = try os.linuxEpollCreate(posix.EPOLL_CLOEXEC);
166 errdefer os.close(self.os_data.epollfd);
136167
137 self.os_data.final_eventfd = try std.os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK);
138 errdefer std.os.close(self.os_data.final_eventfd);
168 self.os_data.final_eventfd = try os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK);
169 errdefer os.close(self.os_data.final_eventfd);
139170
140171 self.os_data.final_eventfd_event = posix.epoll_event{
141172 .events = posix.EPOLLIN,
142173 .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) },
143174 };
144 try std.os.linuxEpollCtl(
175 try os.linuxEpollCtl(
145176 self.os_data.epollfd,
146177 posix.EPOLL_CTL_ADD,
147178 self.os_data.final_eventfd,
148179 &self.os_data.final_eventfd_event,
149180 );
150181
182 self.os_data.fs_thread = try os.spawnThread(self, posixFsRun);
183 errdefer {
184 self.posixFsRequest(&self.os_data.fs_end_request);
185 self.os_data.fs_thread.wait();
186 }
187
151188 var extra_thread_index: usize = 0;
152189 errdefer {
153190 // writing 8 bytes to an eventfd cannot fail
154 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
191 os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
155192 while (extra_thread_index != 0) {
156193 extra_thread_index -= 1;
157194 self.extra_threads[extra_thread_index].wait();
158195 }
159196 }
160197 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
161 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);
198 self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun);
162199 }
163200 },
164201 builtin.Os.macosx => {
165 self.os_data.kqfd = try std.os.bsdKQueue();
166 errdefer std.os.close(self.os_data.kqfd);
167
168 self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count);
169 errdefer self.allocator.free(self.os_data.kevents);
202 self.os_data.kqfd = try os.bsdKQueue();
203 errdefer os.close(self.os_data.kqfd);
204
205 self.os_data.fs_kqfd = try os.bsdKQueue();
206 errdefer os.close(self.os_data.fs_kqfd);
207
208 self.os_data.fs_queue = std.atomic.Queue(fs.Request).init();
209 // we need another thread for the file system because Darwin does not have an async
210 // file system I/O API.
211 self.os_data.fs_end_request = fs.RequestNode{
212 .prev = undefined,
213 .next = undefined,
214 .data = fs.Request{
215 .msg = fs.Request.Msg.End,
216 .finish = fs.Request.Finish.NoAction,
217 },
218 };
170219
171 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
220 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
172221
173222 for (self.eventfd_resume_nodes) |*eventfd_node, i| {
174223 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
......@@ -191,18 +240,9 @@ pub const Loop = struct {
191240 };
192241 self.available_eventfd_resume_nodes.push(eventfd_node);
193242 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent);
194 _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);
243 _ = try os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null);
195244 eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE;
196245 eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER;
197 // this one is for waiting for events
198 self.os_data.kevents[i] = posix.Kevent{
199 .ident = i,
200 .filter = posix.EVFILT_USER,
201 .flags = 0,
202 .fflags = 0,
203 .data = 0,
204 .udata = @ptrToInt(&eventfd_node.data.base),
205 };
206246 }
207247
208248 // Pre-add so that we cannot get error.SystemResources
......@@ -215,31 +255,55 @@ pub const Loop = struct {
215255 .data = 0,
216256 .udata = @ptrToInt(&self.final_resume_node),
217257 };
218 const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent);
219 _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);
258 const final_kev_arr = (*[1]posix.Kevent)(&self.os_data.final_kevent);
259 _ = try os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null);
220260 self.os_data.final_kevent.flags = posix.EV_ENABLE;
221261 self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER;
222262
263 self.os_data.fs_kevent_wake = posix.Kevent{
264 .ident = 0,
265 .filter = posix.EVFILT_USER,
266 .flags = posix.EV_ADD | posix.EV_ENABLE,
267 .fflags = posix.NOTE_TRIGGER,
268 .data = 0,
269 .udata = undefined,
270 };
271
272 self.os_data.fs_kevent_wait = posix.Kevent{
273 .ident = 0,
274 .filter = posix.EVFILT_USER,
275 .flags = posix.EV_ADD | posix.EV_CLEAR,
276 .fflags = 0,
277 .data = 0,
278 .udata = undefined,
279 };
280
281 self.os_data.fs_thread = try os.spawnThread(self, posixFsRun);
282 errdefer {
283 self.posixFsRequest(&self.os_data.fs_end_request);
284 self.os_data.fs_thread.wait();
285 }
286
223287 var extra_thread_index: usize = 0;
224288 errdefer {
225 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch unreachable;
289 _ = os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null) catch unreachable;
226290 while (extra_thread_index != 0) {
227291 extra_thread_index -= 1;
228292 self.extra_threads[extra_thread_index].wait();
229293 }
230294 }
231295 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
232 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);
296 self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun);
233297 }
234298 },
235299 builtin.Os.windows => {
236 self.os_data.io_port = try std.os.windowsCreateIoCompletionPort(
300 self.os_data.io_port = try os.windowsCreateIoCompletionPort(
237301 windows.INVALID_HANDLE_VALUE,
238302 null,
239303 undefined,
240 undefined,
304 @maxValue(windows.DWORD),
241305 );
242 errdefer std.os.close(self.os_data.io_port);
306 errdefer os.close(self.os_data.io_port);
243307
244308 for (self.eventfd_resume_nodes) |*eventfd_node, i| {
245309 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
......@@ -262,7 +326,7 @@ pub const Loop = struct {
262326 while (i < extra_thread_index) : (i += 1) {
263327 while (true) {
264328 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
265 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
329 os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
266330 break;
267331 }
268332 }
......@@ -272,7 +336,7 @@ pub const Loop = struct {
272336 }
273337 }
274338 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
275 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);
339 self.extra_threads[extra_thread_index] = try os.spawnThread(self, workerRun);
276340 }
277341 },
278342 else => {},
......@@ -282,63 +346,113 @@ pub const Loop = struct {
282346 fn deinitOsData(self: *Loop) void {
283347 switch (builtin.os) {
284348 builtin.Os.linux => {
285 std.os.close(self.os_data.final_eventfd);
286 while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);
287 std.os.close(self.os_data.epollfd);
349 os.close(self.os_data.final_eventfd);
350 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
351 os.close(self.os_data.epollfd);
288352 self.allocator.free(self.eventfd_resume_nodes);
289353 },
290354 builtin.Os.macosx => {
291 self.allocator.free(self.os_data.kevents);
292 std.os.close(self.os_data.kqfd);
355 os.close(self.os_data.kqfd);
356 os.close(self.os_data.fs_kqfd);
293357 },
294358 builtin.Os.windows => {
295 std.os.close(self.os_data.io_port);
359 os.close(self.os_data.io_port);
296360 },
297361 else => {},
298362 }
299363 }
300364
301365 /// resume_node must live longer than the promise that it holds a reference to.
302 pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void {
303 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
304 errdefer {
305 self.finishOneEvent();
306 }
307 try self.modFd(
366 /// flags must contain EPOLLET
367 pub fn linuxAddFd(self: *Loop, fd: i32, resume_node: *ResumeNode, flags: u32) !void {
368 assert(flags & posix.EPOLLET == posix.EPOLLET);
369 self.beginOneEvent();
370 errdefer self.finishOneEvent();
371 try self.linuxModFd(
308372 fd,
309373 posix.EPOLL_CTL_ADD,
310 std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET,
374 flags,
311375 resume_node,
312376 );
313377 }
314378
315 pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void {
316 var ev = std.os.linux.epoll_event{
317 .events = events,
318 .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },
379 pub fn linuxModFd(self: *Loop, fd: i32, op: u32, flags: u32, resume_node: *ResumeNode) !void {
380 assert(flags & posix.EPOLLET == posix.EPOLLET);
381 var ev = os.linux.epoll_event{
382 .events = flags,
383 .data = os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },
319384 };
320 try std.os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);
385 try os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);
321386 }
322387
323 pub fn removeFd(self: *Loop, fd: i32) void {
324 self.removeFdNoCounter(fd);
388 pub fn linuxRemoveFd(self: *Loop, fd: i32) void {
389 os.linuxEpollCtl(self.os_data.epollfd, os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
325390 self.finishOneEvent();
326391 }
327392
328 fn removeFdNoCounter(self: *Loop, fd: i32) void {
329 std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
393 pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
394 defer self.linuxRemoveFd(fd);
395 suspend {
396 // TODO explicitly put this memory in the coroutine frame #1194
397 var resume_node = ResumeNode.Basic{
398 .base = ResumeNode{
399 .id = ResumeNode.Id.Basic,
400 .handle = @handle(),
401 },
402 };
403 try self.linuxAddFd(fd, &resume_node.base, flags);
404 }
330405 }
331406
332 pub async fn waitFd(self: *Loop, fd: i32) !void {
333 defer self.removeFd(fd);
407 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !posix.Kevent {
408 // TODO #1194
334409 suspend {
335 // TODO explicitly put this memory in the coroutine frame #1194
336 var resume_node = ResumeNode{
410 resume @handle();
411 }
412 var resume_node = ResumeNode.Basic{
413 .base = ResumeNode{
337414 .id = ResumeNode.Id.Basic,
338415 .handle = @handle(),
339 };
340 try self.addFd(fd, &resume_node);
416 },
417 .kev = undefined,
418 };
419 defer self.bsdRemoveKev(ident, filter);
420 suspend {
421 try self.bsdAddKev(&resume_node, ident, filter, fflags);
341422 }
423 return resume_node.kev;
424 }
425
426 /// resume_node must live longer than the promise that it holds a reference to.
427 pub fn bsdAddKev(self: *Loop, resume_node: *ResumeNode.Basic, ident: usize, filter: i16, fflags: u32) !void {
428 self.beginOneEvent();
429 errdefer self.finishOneEvent();
430 var kev = posix.Kevent{
431 .ident = ident,
432 .filter = filter,
433 .flags = posix.EV_ADD | posix.EV_ENABLE | posix.EV_CLEAR,
434 .fflags = fflags,
435 .data = 0,
436 .udata = @ptrToInt(&resume_node.base),
437 };
438 const kevent_array = (*[1]posix.Kevent)(&kev);
439 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
440 _ = try os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null);
441 }
442
443 pub fn bsdRemoveKev(self: *Loop, ident: usize, filter: i16) void {
444 var kev = posix.Kevent{
445 .ident = ident,
446 .filter = filter,
447 .flags = posix.EV_DELETE,
448 .fflags = 0,
449 .data = 0,
450 .udata = 0,
451 };
452 const kevent_array = (*[1]posix.Kevent)(&kev);
453 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
454 _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch undefined;
455 self.finishOneEvent();
342456 }
343457
344458 fn dispatch(self: *Loop) void {
......@@ -352,8 +466,8 @@ pub const Loop = struct {
352466 switch (builtin.os) {
353467 builtin.Os.macosx => {
354468 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);
355 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
356 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {
469 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
470 _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch {
357471 self.next_tick_queue.unget(next_tick_node);
358472 self.available_eventfd_resume_nodes.push(resume_stack_node);
359473 return;
......@@ -361,9 +475,9 @@ pub const Loop = struct {
361475 },
362476 builtin.Os.linux => {
363477 // the pending count is already accounted for
364 const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT |
365 std.os.linux.EPOLLET;
366 self.modFd(
478 const epoll_events = posix.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT |
479 os.linux.EPOLLET;
480 self.linuxModFd(
367481 eventfd_node.eventfd,
368482 eventfd_node.epoll_op,
369483 epoll_events,
......@@ -379,7 +493,7 @@ pub const Loop = struct {
379493 // the consumer code can decide whether to read the completion key.
380494 // it has to do this for normal I/O, so we match that behavior here.
381495 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
382 std.os.windowsPostQueuedCompletionStatus(
496 os.windowsPostQueuedCompletionStatus(
383497 self.os_data.io_port,
384498 undefined,
385499 eventfd_node.completion_key,
......@@ -397,15 +511,29 @@ pub const Loop = struct {
397511
398512 /// Bring your own linked list node. This means it can't fail.
399513 pub fn onNextTick(self: *Loop, node: *NextTickNode) void {
400 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
514 self.beginOneEvent(); // finished in dispatch()
401515 self.next_tick_queue.put(node);
402516 self.dispatch();
403517 }
404518
519 pub fn cancelOnNextTick(self: *Loop, node: *NextTickNode) void {
520 if (self.next_tick_queue.remove(node)) {
521 self.finishOneEvent();
522 }
523 }
524
405525 pub fn run(self: *Loop) void {
406526 self.finishOneEvent(); // the reference we start with
407527
408528 self.workerRun();
529
530 switch (builtin.os) {
531 builtin.Os.linux,
532 builtin.Os.macosx,
533 => self.os_data.fs_thread.wait(),
534 else => {},
535 }
536
409537 for (self.extra_threads) |extra_thread| {
410538 extra_thread.wait();
411539 }
......@@ -420,6 +548,7 @@ pub const Loop = struct {
420548 suspend {
421549 handle.* = @handle();
422550 var my_tick_node = Loop.NextTickNode{
551 .prev = undefined,
423552 .next = undefined,
424553 .data = @handle(),
425554 };
......@@ -441,6 +570,7 @@ pub const Loop = struct {
441570 pub async fn yield(self: *Loop) void {
442571 suspend {
443572 var my_tick_node = Loop.NextTickNode{
573 .prev = undefined,
444574 .next = undefined,
445575 .data = @handle(),
446576 };
......@@ -448,20 +578,28 @@ pub const Loop = struct {
448578 }
449579 }
450580
451 fn finishOneEvent(self: *Loop) void {
452 if (@atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) == 1) {
581 /// call finishOneEvent when done
582 pub fn beginOneEvent(self: *Loop) void {
583 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
584 }
585
586 pub fn finishOneEvent(self: *Loop) void {
587 const prev = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
588 if (prev == 1) {
453589 // cause all the threads to stop
454590 switch (builtin.os) {
455591 builtin.Os.linux => {
592 self.posixFsRequest(&self.os_data.fs_end_request);
456593 // writing 8 bytes to an eventfd cannot fail
457 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
594 os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
458595 return;
459596 },
460597 builtin.Os.macosx => {
598 self.posixFsRequest(&self.os_data.fs_end_request);
461599 const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);
462 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
600 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
463601 // cannot fail because we already added it and this just enables it
464 _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable;
602 _ = os.bsdKEvent(self.os_data.kqfd, final_kevent, empty_kevs, null) catch unreachable;
465603 return;
466604 },
467605 builtin.Os.windows => {
......@@ -469,7 +607,7 @@ pub const Loop = struct {
469607 while (i < self.extra_threads.len + 1) : (i += 1) {
470608 while (true) {
471609 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
472 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
610 os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
473611 break;
474612 }
475613 }
......@@ -492,8 +630,8 @@ pub const Loop = struct {
492630 switch (builtin.os) {
493631 builtin.Os.linux => {
494632 // only process 1 event so we don't steal from other threads
495 var events: [1]std.os.linux.epoll_event = undefined;
496 const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);
633 var events: [1]os.linux.epoll_event = undefined;
634 const count = os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);
497635 for (events[0..count]) |ev| {
498636 const resume_node = @intToPtr(*ResumeNode, ev.data.ptr);
499637 const handle = resume_node.handle;
......@@ -516,13 +654,17 @@ pub const Loop = struct {
516654 },
517655 builtin.Os.macosx => {
518656 var eventlist: [1]posix.Kevent = undefined;
519 const count = std.os.bsdKEvent(self.os_data.kqfd, self.os_data.kevents, eventlist[0..], null) catch unreachable;
657 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
658 const count = os.bsdKEvent(self.os_data.kqfd, empty_kevs, eventlist[0..], null) catch unreachable;
520659 for (eventlist[0..count]) |ev| {
521660 const resume_node = @intToPtr(*ResumeNode, ev.udata);
522661 const handle = resume_node.handle;
523662 const resume_node_id = resume_node.id;
524663 switch (resume_node_id) {
525 ResumeNode.Id.Basic => {},
664 ResumeNode.Id.Basic => {
665 const basic_node = @fieldParentPtr(ResumeNode.Basic, "base", resume_node);
666 basic_node.kev = ev;
667 },
526668 ResumeNode.Id.Stop => return,
527669 ResumeNode.Id.EventFd => {
528670 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);
......@@ -541,9 +683,10 @@ pub const Loop = struct {
541683 while (true) {
542684 var nbytes: windows.DWORD = undefined;
543685 var overlapped: ?*windows.OVERLAPPED = undefined;
544 switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) {
545 std.os.WindowsWaitResult.Aborted => return,
546 std.os.WindowsWaitResult.Normal => {},
686 switch (os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) {
687 os.WindowsWaitResult.Aborted => return,
688 os.WindowsWaitResult.Normal => {},
689 os.WindowsWaitResult.Cancelled => continue,
547690 }
548691 if (overlapped != null) break;
549692 }
......@@ -560,21 +703,101 @@ pub const Loop = struct {
560703 },
561704 }
562705 resume handle;
563 if (resume_node_id == ResumeNode.Id.EventFd) {
564 self.finishOneEvent();
565 }
706 self.finishOneEvent();
566707 },
567708 else => @compileError("unsupported OS"),
568709 }
569710 }
570711 }
571712
713 fn posixFsRequest(self: *Loop, request_node: *fs.RequestNode) void {
714 self.beginOneEvent(); // finished in posixFsRun after processing the msg
715 self.os_data.fs_queue.put(request_node);
716 switch (builtin.os) {
717 builtin.Os.macosx => {
718 const fs_kevs = (*[1]posix.Kevent)(&self.os_data.fs_kevent_wake);
719 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
720 _ = os.bsdKEvent(self.os_data.fs_kqfd, fs_kevs, empty_kevs, null) catch unreachable;
721 },
722 builtin.Os.linux => {
723 _ = @atomicRmw(u8, &self.os_data.fs_queue_item, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
724 const rc = os.linux.futex_wake(@ptrToInt(&self.os_data.fs_queue_item), os.linux.FUTEX_WAKE, 1);
725 switch (os.linux.getErrno(rc)) {
726 0 => {},
727 posix.EINVAL => unreachable,
728 else => unreachable,
729 }
730 },
731 else => @compileError("Unsupported OS"),
732 }
733 }
734
735 fn posixFsCancel(self: *Loop, request_node: *fs.RequestNode) void {
736 if (self.os_data.fs_queue.remove(request_node)) {
737 self.finishOneEvent();
738 }
739 }
740
741 fn posixFsRun(self: *Loop) void {
742 while (true) {
743 if (builtin.os == builtin.Os.linux) {
744 _ = @atomicRmw(u8, &self.os_data.fs_queue_item, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
745 }
746 while (self.os_data.fs_queue.get()) |node| {
747 switch (node.data.msg) {
748 @TagType(fs.Request.Msg).End => return,
749 @TagType(fs.Request.Msg).PWriteV => |*msg| {
750 msg.result = os.posix_pwritev(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset);
751 },
752 @TagType(fs.Request.Msg).PReadV => |*msg| {
753 msg.result = os.posix_preadv(msg.fd, msg.iov.ptr, msg.iov.len, msg.offset);
754 },
755 @TagType(fs.Request.Msg).Open => |*msg| {
756 msg.result = os.posixOpenC(msg.path.ptr, msg.flags, msg.mode);
757 },
758 @TagType(fs.Request.Msg).Close => |*msg| os.close(msg.fd),
759 @TagType(fs.Request.Msg).WriteFile => |*msg| blk: {
760 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT |
761 posix.O_CLOEXEC | posix.O_TRUNC;
762 const fd = os.posixOpenC(msg.path.ptr, flags, msg.mode) catch |err| {
763 msg.result = err;
764 break :blk;
765 };
766 defer os.close(fd);
767 msg.result = os.posixWrite(fd, msg.contents);
768 },
769 }
770 switch (node.data.finish) {
771 @TagType(fs.Request.Finish).TickNode => |*tick_node| self.onNextTick(tick_node),
772 @TagType(fs.Request.Finish).DeallocCloseOperation => |close_op| {
773 self.allocator.destroy(close_op);
774 },
775 @TagType(fs.Request.Finish).NoAction => {},
776 }
777 self.finishOneEvent();
778 }
779 switch (builtin.os) {
780 builtin.Os.linux => {
781 const rc = os.linux.futex_wait(@ptrToInt(&self.os_data.fs_queue_item), os.linux.FUTEX_WAIT, 0, null);
782 switch (os.linux.getErrno(rc)) {
783 0 => continue,
784 posix.EINTR => continue,
785 posix.EAGAIN => continue,
786 else => unreachable,
787 }
788 },
789 builtin.Os.macosx => {
790 const fs_kevs = (*[1]posix.Kevent)(&self.os_data.fs_kevent_wait);
791 var out_kevs: [1]posix.Kevent = undefined;
792 _ = os.bsdKEvent(self.os_data.fs_kqfd, fs_kevs, out_kevs[0..], null) catch unreachable;
793 },
794 else => @compileError("Unsupported OS"),
795 }
796 }
797 }
798
572799 const OsData = switch (builtin.os) {
573 builtin.Os.linux => struct {
574 epollfd: i32,
575 final_eventfd: i32,
576 final_eventfd_event: std.os.linux.epoll_event,
577 },
800 builtin.Os.linux => LinuxOsData,
578801 builtin.Os.macosx => MacOsData,
579802 builtin.Os.windows => struct {
580803 io_port: windows.HANDLE,
......@@ -586,7 +809,22 @@ pub const Loop = struct {
586809 const MacOsData = struct {
587810 kqfd: i32,
588811 final_kevent: posix.Kevent,
589 kevents: []posix.Kevent,
812 fs_kevent_wake: posix.Kevent,
813 fs_kevent_wait: posix.Kevent,
814 fs_thread: *os.Thread,
815 fs_kqfd: i32,
816 fs_queue: std.atomic.Queue(fs.Request),
817 fs_end_request: fs.RequestNode,
818 };
819
820 const LinuxOsData = struct {
821 epollfd: i32,
822 final_eventfd: i32,
823 final_eventfd_event: os.linux.epoll_event,
824 fs_thread: *os.Thread,
825 fs_queue_item: u8,
826 fs_queue: std.atomic.Queue(fs.Request),
827 fs_end_request: fs.RequestNode,
590828 };
591829};
592830
std/event/rwlock.zig created+296
......@@ -0,0 +1,296 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const mem = std.mem;
5const AtomicRmwOp = builtin.AtomicRmwOp;
6const AtomicOrder = builtin.AtomicOrder;
7const Loop = std.event.Loop;
8
9/// Thread-safe async/await lock.
10/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
11/// are resumed when the lock is released, in order.
12/// Many readers can hold the lock at the same time; however locking for writing is exclusive.
13/// When a read lock is held, it will not be released until the reader queue is empty.
14/// When a write lock is held, it will not be released until the writer queue is empty.
15pub const RwLock = struct {
16 loop: *Loop,
17 shared_state: u8, // TODO make this an enum
18 writer_queue: Queue,
19 reader_queue: Queue,
20 writer_queue_empty_bit: u8, // TODO make this a bool
21 reader_queue_empty_bit: u8, // TODO make this a bool
22 reader_lock_count: usize,
23
24 const State = struct {
25 const Unlocked = 0;
26 const WriteLock = 1;
27 const ReadLock = 2;
28 };
29
30 const Queue = std.atomic.Queue(promise);
31
32 pub const HeldRead = struct {
33 lock: *RwLock,
34
35 pub fn release(self: HeldRead) void {
36 // If other readers still hold the lock, we're done.
37 if (@atomicRmw(usize, &self.lock.reader_lock_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) != 1) {
38 return;
39 }
40
41 _ = @atomicRmw(u8, &self.lock.reader_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
42 if (@cmpxchgStrong(u8, &self.lock.shared_state, State.ReadLock, State.Unlocked, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {
43 // Didn't unlock. Someone else's problem.
44 return;
45 }
46
47 self.lock.commonPostUnlock();
48 }
49 };
50
51 pub const HeldWrite = struct {
52 lock: *RwLock,
53
54 pub fn release(self: HeldWrite) void {
55 // See if we can leave it locked for writing, and pass the lock to the next writer
56 // in the queue to grab the lock.
57 if (self.lock.writer_queue.get()) |node| {
58 self.lock.loop.onNextTick(node);
59 return;
60 }
61
62 // We need to release the write lock. Check if any readers are waiting to grab the lock.
63 if (@atomicLoad(u8, &self.lock.reader_queue_empty_bit, AtomicOrder.SeqCst) == 0) {
64 // Switch to a read lock.
65 _ = @atomicRmw(u8, &self.lock.shared_state, AtomicRmwOp.Xchg, State.ReadLock, AtomicOrder.SeqCst);
66 while (self.lock.reader_queue.get()) |node| {
67 self.lock.loop.onNextTick(node);
68 }
69 return;
70 }
71
72 _ = @atomicRmw(u8, &self.lock.writer_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
73 _ = @atomicRmw(u8, &self.lock.shared_state, AtomicRmwOp.Xchg, State.Unlocked, AtomicOrder.SeqCst);
74
75 self.lock.commonPostUnlock();
76 }
77 };
78
79 pub fn init(loop: *Loop) RwLock {
80 return RwLock{
81 .loop = loop,
82 .shared_state = State.Unlocked,
83 .writer_queue = Queue.init(),
84 .writer_queue_empty_bit = 1,
85 .reader_queue = Queue.init(),
86 .reader_queue_empty_bit = 1,
87 .reader_lock_count = 0,
88 };
89 }
90
91 /// Must be called when not locked. Not thread safe.
92 /// All calls to acquire() and release() must complete before calling deinit().
93 pub fn deinit(self: *RwLock) void {
94 assert(self.shared_state == State.Unlocked);
95 while (self.writer_queue.get()) |node| cancel node.data;
96 while (self.reader_queue.get()) |node| cancel node.data;
97 }
98
99 pub async fn acquireRead(self: *RwLock) HeldRead {
100 _ = @atomicRmw(usize, &self.reader_lock_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
101
102 suspend {
103 // TODO explicitly put this memory in the coroutine frame #1194
104 var my_tick_node = Loop.NextTickNode{
105 .data = @handle(),
106 .prev = undefined,
107 .next = undefined,
108 };
109
110 self.reader_queue.put(&my_tick_node);
111
112 // At this point, we are in the reader_queue, so we might have already been resumed and this coroutine
113 // frame might be destroyed. For the rest of the suspend block we cannot access the coroutine frame.
114
115 // We set this bit so that later we can rely on the fact, that if reader_queue_empty_bit is 1,
116 // some actor will attempt to grab the lock.
117 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
118
119 // Here we don't care if we are the one to do the locking or if it was already locked for reading.
120 const have_read_lock = if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |old_state| old_state == State.ReadLock else true;
121 if (have_read_lock) {
122 // Give out all the read locks.
123 if (self.reader_queue.get()) |first_node| {
124 while (self.reader_queue.get()) |node| {
125 self.loop.onNextTick(node);
126 }
127 resume first_node.data;
128 }
129 }
130 }
131 return HeldRead{ .lock = self };
132 }
133
134 pub async fn acquireWrite(self: *RwLock) HeldWrite {
135 suspend {
136 // TODO explicitly put this memory in the coroutine frame #1194
137 var my_tick_node = Loop.NextTickNode{
138 .data = @handle(),
139 .prev = undefined,
140 .next = undefined,
141 };
142
143 self.writer_queue.put(&my_tick_node);
144
145 // At this point, we are in the writer_queue, so we might have already been resumed and this coroutine
146 // frame might be destroyed. For the rest of the suspend block we cannot access the coroutine frame.
147
148 // We set this bit so that later we can rely on the fact, that if writer_queue_empty_bit is 1,
149 // some actor will attempt to grab the lock.
150 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
151
152 // Here we must be the one to acquire the write lock. It cannot already be locked.
153 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null) {
154 // We now have a write lock.
155 if (self.writer_queue.get()) |node| {
156 // Whether this node is us or someone else, we tail resume it.
157 resume node.data;
158 }
159 }
160 }
161 return HeldWrite{ .lock = self };
162 }
163
164 fn commonPostUnlock(self: *RwLock) void {
165 while (true) {
166 // There might be a writer_queue item or a reader_queue item
167 // If we check and both are empty, we can be done, because the other actors will try to
168 // obtain the lock.
169 // But if there's a writer_queue item or a reader_queue item,
170 // we are the actor which must loop and attempt to grab the lock again.
171 if (@atomicLoad(u8, &self.writer_queue_empty_bit, AtomicOrder.SeqCst) == 0) {
172 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.WriteLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {
173 // We did not obtain the lock. Great, the queues are someone else's problem.
174 return;
175 }
176 // If there's an item in the writer queue, give them the lock, and we're done.
177 if (self.writer_queue.get()) |node| {
178 self.loop.onNextTick(node);
179 return;
180 }
181 // Release the lock again.
182 _ = @atomicRmw(u8, &self.writer_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
183 _ = @atomicRmw(u8, &self.shared_state, AtomicRmwOp.Xchg, State.Unlocked, AtomicOrder.SeqCst);
184 continue;
185 }
186
187 if (@atomicLoad(u8, &self.reader_queue_empty_bit, AtomicOrder.SeqCst) == 0) {
188 if (@cmpxchgStrong(u8, &self.shared_state, State.Unlocked, State.ReadLock, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {
189 // We did not obtain the lock. Great, the queues are someone else's problem.
190 return;
191 }
192 // If there are any items in the reader queue, give out all the reader locks, and we're done.
193 if (self.reader_queue.get()) |first_node| {
194 self.loop.onNextTick(first_node);
195 while (self.reader_queue.get()) |node| {
196 self.loop.onNextTick(node);
197 }
198 return;
199 }
200 // Release the lock again.
201 _ = @atomicRmw(u8, &self.reader_queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
202 if (@cmpxchgStrong(u8, &self.shared_state, State.ReadLock, State.Unlocked, AtomicOrder.SeqCst, AtomicOrder.SeqCst) != null) {
203 // Didn't unlock. Someone else's problem.
204 return;
205 }
206 continue;
207 }
208 return;
209 }
210 }
211};
212
213test "std.event.RwLock" {
214 var da = std.heap.DirectAllocator.init();
215 defer da.deinit();
216
217 const allocator = &da.allocator;
218
219 var loop: Loop = undefined;
220 try loop.initMultiThreaded(allocator);
221 defer loop.deinit();
222
223 var lock = RwLock.init(&loop);
224 defer lock.deinit();
225
226 const handle = try async<allocator> testLock(&loop, &lock);
227 defer cancel handle;
228 loop.run();
229
230 const expected_result = [1]i32{shared_it_count * @intCast(i32, shared_test_data.len)} ** shared_test_data.len;
231 assert(mem.eql(i32, shared_test_data, expected_result));
232}
233
234async fn testLock(loop: *Loop, lock: *RwLock) void {
235 // TODO explicitly put next tick node memory in the coroutine frame #1194
236 suspend {
237 resume @handle();
238 }
239
240 var read_nodes: [100]Loop.NextTickNode = undefined;
241 for (read_nodes) |*read_node| {
242 read_node.data = async readRunner(lock) catch @panic("out of memory");
243 loop.onNextTick(read_node);
244 }
245
246 var write_nodes: [shared_it_count]Loop.NextTickNode = undefined;
247 for (write_nodes) |*write_node| {
248 write_node.data = async writeRunner(lock) catch @panic("out of memory");
249 loop.onNextTick(write_node);
250 }
251
252 for (write_nodes) |*write_node| {
253 await @ptrCast(promise->void, write_node.data);
254 }
255 for (read_nodes) |*read_node| {
256 await @ptrCast(promise->void, read_node.data);
257 }
258}
259
260const shared_it_count = 10;
261var shared_test_data = [1]i32{0} ** 10;
262var shared_test_index: usize = 0;
263var shared_count: usize = 0;
264
265async fn writeRunner(lock: *RwLock) void {
266 suspend; // resumed by onNextTick
267
268 var i: usize = 0;
269 while (i < shared_test_data.len) : (i += 1) {
270 std.os.time.sleep(0, 100000);
271 const lock_promise = async lock.acquireWrite() catch @panic("out of memory");
272 const handle = await lock_promise;
273 defer handle.release();
274
275 shared_count += 1;
276 while (shared_test_index < shared_test_data.len) : (shared_test_index += 1) {
277 shared_test_data[shared_test_index] = shared_test_data[shared_test_index] + 1;
278 }
279 shared_test_index = 0;
280 }
281}
282
283async fn readRunner(lock: *RwLock) void {
284 suspend; // resumed by onNextTick
285 std.os.time.sleep(0, 1);
286
287 var i: usize = 0;
288 while (i < shared_test_data.len) : (i += 1) {
289 const lock_promise = async lock.acquireRead() catch @panic("out of memory");
290 const handle = await lock_promise;
291 defer handle.release();
292
293 assert(shared_test_index == 0);
294 assert(shared_test_data[i] == @intCast(i32, shared_count));
295 }
296}
std/event/rwlocked.zig created+58
......@@ -0,0 +1,58 @@
1const std = @import("../index.zig");
2const RwLock = std.event.RwLock;
3const Loop = std.event.Loop;
4
5/// Thread-safe async/await RW lock that protects one piece of data.
6/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
7/// are resumed when the lock is released, in order.
8pub fn RwLocked(comptime T: type) type {
9 return struct {
10 lock: RwLock,
11 locked_data: T,
12
13 const Self = this;
14
15 pub const HeldReadLock = struct {
16 value: *const T,
17 held: RwLock.HeldRead,
18
19 pub fn release(self: HeldReadLock) void {
20 self.held.release();
21 }
22 };
23
24 pub const HeldWriteLock = struct {
25 value: *T,
26 held: RwLock.HeldWrite,
27
28 pub fn release(self: HeldWriteLock) void {
29 self.held.release();
30 }
31 };
32
33 pub fn init(loop: *Loop, data: T) Self {
34 return Self{
35 .lock = RwLock.init(loop),
36 .locked_data = data,
37 };
38 }
39
40 pub fn deinit(self: *Self) void {
41 self.lock.deinit();
42 }
43
44 pub async fn acquireRead(self: *Self) HeldReadLock {
45 return HeldReadLock{
46 .held = await (async self.lock.acquireRead() catch unreachable),
47 .value = &self.locked_data,
48 };
49 }
50
51 pub async fn acquireWrite(self: *Self) HeldWriteLock {
52 return HeldWriteLock{
53 .held = await (async self.lock.acquireWrite() catch unreachable),
54 .value = &self.locked_data,
55 };
56 }
57 };
58}
std/event/tcp.zig+3-4
......@@ -55,13 +55,13 @@ pub const Server = struct {
5555 errdefer cancel self.accept_coro.?;
5656
5757 self.listen_resume_node.handle = self.accept_coro.?;
58 try self.loop.addFd(sockfd, &self.listen_resume_node);
58 try self.loop.linuxAddFd(sockfd, &self.listen_resume_node, posix.EPOLLIN | posix.EPOLLOUT | posix.EPOLLET);
5959 errdefer self.loop.removeFd(sockfd);
6060 }
6161
6262 /// Stop listening
6363 pub fn close(self: *Server) void {
64 self.loop.removeFd(self.sockfd.?);
64 self.loop.linuxRemoveFd(self.sockfd.?);
6565 std.os.close(self.sockfd.?);
6666 }
6767
......@@ -116,7 +116,7 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File
116116 errdefer std.os.close(sockfd);
117117
118118 try std.os.posixConnectAsync(sockfd, &address.os_addr);
119 try await try async loop.waitFd(sockfd);
119 try await try async loop.linuxWaitFd(sockfd, posix.EPOLLIN | posix.EPOLLOUT | posix.EPOLLET);
120120 try std.os.posixGetSockOptConnectError(sockfd);
121121
122122 return std.os.File.openHandle(sockfd);
......@@ -181,4 +181,3 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Serv
181181 assert(mem.eql(u8, msg, "hello from server\n"));
182182 server.close();
183183}
184
std/fmt/errol/index.zig-4
......@@ -253,11 +253,7 @@ fn gethi(in: f64) f64 {
253253/// Normalize the number by factoring in the error.
254254/// @hp: The float pair.
255255fn hpNormalize(hp: *HP) void {
256 // Required to avoid segfaults causing buffer overrun during errol3 digit output termination.
257 @setFloatMode(this, @import("builtin").FloatMode.Strict);
258
259256 const val = hp.val;
260
261257 hp.val += hp.off;
262258 hp.off += val - hp.val;
263259}
std/fmt/index.zig+65-32
......@@ -146,6 +146,45 @@ pub fn formatType(
146146 builtin.TypeId.Promise => {
147147 return format(context, Errors, output, "promise@{x}", @ptrToInt(value));
148148 },
149 builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
150 const has_cust_fmt = comptime cf: {
151 const info = @typeInfo(T);
152 const defs = switch (info) {
153 builtin.TypeId.Struct => |s| s.defs,
154 builtin.TypeId.Union => |u| u.defs,
155 builtin.TypeId.Enum => |e| e.defs,
156 else => unreachable,
157 };
158
159 for (defs) |def| {
160 if (mem.eql(u8, def.name, "format")) {
161 break :cf true;
162 }
163 }
164 break :cf false;
165 };
166
167 if (has_cust_fmt) return value.format(fmt, context, Errors, output);
168 try output(context, @typeName(T));
169 if (comptime @typeId(T) == builtin.TypeId.Enum) {
170 try output(context, ".");
171 try formatType(@tagName(value), "", context, Errors, output);
172 return;
173 }
174 comptime var field_i = 0;
175 inline while (field_i < @memberCount(T)) : (field_i += 1) {
176 if (field_i == 0) {
177 try output(context, "{ .");
178 } else {
179 try output(context, ", .");
180 }
181 try output(context, @memberName(T, field_i));
182 try output(context, " = ");
183 try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output);
184 }
185 try output(context, " }");
186 return;
187 },
149188 builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) {
150189 builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) {
151190 builtin.TypeId.Array => |info| {
......@@ -155,31 +194,13 @@ pub fn formatType(
155194 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
156195 },
157196 builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
158 const has_cust_fmt = comptime cf: {
159 const info = @typeInfo(T.Child);
160 const defs = switch (info) {
161 builtin.TypeId.Struct => |s| s.defs,
162 builtin.TypeId.Union => |u| u.defs,
163 builtin.TypeId.Enum => |e| e.defs,
164 else => unreachable,
165 };
166
167 for (defs) |def| {
168 if (mem.eql(u8, def.name, "format")) {
169 break :cf true;
170 }
171 }
172 break :cf false;
173 };
174
175 if (has_cust_fmt) return value.format(fmt, context, Errors, output);
176 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
197 return formatType(value.*, fmt, context, Errors, output);
177198 },
178199 else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
179200 },
180201 builtin.TypeInfo.Pointer.Size.Many => {
181202 if (ptr_info.child == u8) {
182 if (fmt[0] == 's') {
203 if (fmt.len > 0 and fmt[0] == 's') {
183204 const len = std.cstr.len(value);
184205 return formatText(value[0..len], fmt, context, Errors, output);
185206 }
......@@ -911,14 +932,21 @@ test "fmt.format" {
911932 try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
912933 try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));
913934 {
914 // Dummy field because of https://github.com/ziglang/zig/issues/557.
915935 const Struct = struct {
916 unused: u8,
936 field: u8,
917937 };
918 var buf1: [32]u8 = undefined;
919 const value = Struct{ .unused = 42 };
920 const result = try bufPrint(buf1[0..], "pointer: {}\n", &value);
921 assert(mem.startsWith(u8, result, "pointer: Struct@"));
938 const value = Struct{ .field = 42 };
939 try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", value);
940 try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", &value);
941 }
942 {
943 const Enum = enum {
944 One,
945 Two,
946 };
947 const value = Enum.Two;
948 try testFmt("enum: Enum.Two\n", "enum: {}\n", value);
949 try testFmt("enum: Enum.Two\n", "enum: {}\n", &value);
922950 }
923951 {
924952 var buf1: [32]u8 = undefined;
......@@ -941,6 +969,7 @@ test "fmt.format" {
941969 {
942970 // This fails on release due to a minor rounding difference.
943971 // --release-fast outputs 9.999960000000001e-40 vs. the expected.
972 // TODO fix this, it should be the same in Debug and ReleaseFast
944973 if (builtin.mode == builtin.Mode.Debug) {
945974 var buf1: [32]u8 = undefined;
946975 const value: f64 = 9.999960e-40;
......@@ -1133,23 +1162,23 @@ test "fmt.format" {
11331162 y: f32,
11341163
11351164 pub fn format(
1136 self: *SelfType,
1165 self: SelfType,
11371166 comptime fmt: []const u8,
11381167 context: var,
11391168 comptime Errors: type,
11401169 output: fn (@typeOf(context), []const u8) Errors!void,
11411170 ) Errors!void {
1142 if (fmt.len > 0) {
1143 if (fmt.len > 1) unreachable;
1144 switch (fmt[0]) {
1171 switch (fmt.len) {
1172 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
1173 1 => switch (fmt[0]) {
11451174 //point format
11461175 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
11471176 //dimension format
11481177 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y),
11491178 else => unreachable,
1150 }
1179 },
1180 else => unreachable,
11511181 }
1152 return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y);
11531182 }
11541183 };
11551184
......@@ -1160,6 +1189,10 @@ test "fmt.format" {
11601189 };
11611190 try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value);
11621191 try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value);
1192
1193 // same thing but not passing a pointer
1194 try testFmt("point: (10.200,2.220)\n", "point: {}\n", value);
1195 try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value);
11631196 }
11641197}
11651198
std/hash_map.zig+273-57
......@@ -9,6 +9,10 @@ const builtin = @import("builtin");
99const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast;
1010const debug_u32 = if (want_modification_safety) u32 else void;
1111
12pub fn AutoHashMap(comptime K: type, comptime V: type) type {
13 return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K));
14}
15
1216pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type {
1317 return struct {
1418 entries: []Entry,
......@@ -20,13 +24,22 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
2024
2125 const Self = this;
2226
23 pub const Entry = struct {
24 used: bool,
25 distance_from_start_index: usize,
27 pub const KV = struct {
2628 key: K,
2729 value: V,
2830 };
2931
32 const Entry = struct {
33 used: bool,
34 distance_from_start_index: usize,
35 kv: KV,
36 };
37
38 pub const GetOrPutResult = struct {
39 kv: *KV,
40 found_existing: bool,
41 };
42
3043 pub const Iterator = struct {
3144 hm: *const Self,
3245 // how many items have we returned
......@@ -36,7 +49,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
3649 // used to detect concurrent modification
3750 initial_modification_count: debug_u32,
3851
39 pub fn next(it: *Iterator) ?*Entry {
52 pub fn next(it: *Iterator) ?*KV {
4053 if (want_modification_safety) {
4154 assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification
4255 }
......@@ -46,7 +59,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
4659 if (entry.used) {
4760 it.index += 1;
4861 it.count += 1;
49 return entry;
62 return &entry.kv;
5063 }
5164 }
5265 unreachable; // no next item
......@@ -71,7 +84,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
7184 };
7285 }
7386
74 pub fn deinit(hm: *const Self) void {
87 pub fn deinit(hm: Self) void {
7588 hm.allocator.free(hm.entries);
7689 }
7790
......@@ -84,34 +97,65 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
8497 hm.incrementModificationCount();
8598 }
8699
87 pub fn count(hm: *const Self) usize {
88 return hm.size;
100 pub fn count(self: Self) usize {
101 return self.size;
89102 }
90103
91 /// Returns the value that was already there.
92 pub fn put(hm: *Self, key: K, value: *const V) !?V {
93 if (hm.entries.len == 0) {
94 try hm.initCapacity(16);
104 /// If key exists this function cannot fail.
105 /// If there is an existing item with `key`, then the result
106 /// kv pointer points to it, and found_existing is true.
107 /// Otherwise, puts a new item with undefined value, and
108 /// the kv pointer points to it. Caller should then initialize
109 /// the data.
110 pub fn getOrPut(self: *Self, key: K) !GetOrPutResult {
111 // TODO this implementation can be improved - we should only
112 // have to hash once and find the entry once.
113 if (self.get(key)) |kv| {
114 return GetOrPutResult{
115 .kv = kv,
116 .found_existing = true,
117 };
118 }
119 self.incrementModificationCount();
120 try self.ensureCapacity();
121 const put_result = self.internalPut(key);
122 assert(put_result.old_kv == null);
123 return GetOrPutResult{
124 .kv = &put_result.new_entry.kv,
125 .found_existing = false,
126 };
127 }
128
129 fn ensureCapacity(self: *Self) !void {
130 if (self.entries.len == 0) {
131 return self.initCapacity(16);
95132 }
96 hm.incrementModificationCount();
97133
98134 // if we get too full (60%), double the capacity
99 if (hm.size * 5 >= hm.entries.len * 3) {
100 const old_entries = hm.entries;
101 try hm.initCapacity(hm.entries.len * 2);
135 if (self.size * 5 >= self.entries.len * 3) {
136 const old_entries = self.entries;
137 try self.initCapacity(self.entries.len * 2);
102138 // dump all of the old elements into the new table
103139 for (old_entries) |*old_entry| {
104140 if (old_entry.used) {
105 _ = hm.internalPut(old_entry.key, old_entry.value);
141 self.internalPut(old_entry.kv.key).new_entry.kv.value = old_entry.kv.value;
106142 }
107143 }
108 hm.allocator.free(old_entries);
144 self.allocator.free(old_entries);
109145 }
146 }
147
148 /// Returns the kv pair that was already there.
149 pub fn put(self: *Self, key: K, value: V) !?KV {
150 self.incrementModificationCount();
151 try self.ensureCapacity();
110152
111 return hm.internalPut(key, value);
153 const put_result = self.internalPut(key);
154 put_result.new_entry.kv.value = value;
155 return put_result.old_kv;
112156 }
113157
114 pub fn get(hm: *const Self, key: K) ?*Entry {
158 pub fn get(hm: *const Self, key: K) ?*KV {
115159 if (hm.entries.len == 0) {
116160 return null;
117161 }
......@@ -122,7 +166,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
122166 return hm.get(key) != null;
123167 }
124168
125 pub fn remove(hm: *Self, key: K) ?*Entry {
169 pub fn remove(hm: *Self, key: K) ?*KV {
126170 if (hm.entries.len == 0) return null;
127171 hm.incrementModificationCount();
128172 const start_index = hm.keyToIndex(key);
......@@ -134,7 +178,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
134178
135179 if (!entry.used) return null;
136180
137 if (!eql(entry.key, key)) continue;
181 if (!eql(entry.kv.key, key)) continue;
138182
139183 while (roll_over < hm.entries.len) : (roll_over += 1) {
140184 const next_index = (start_index + roll_over + 1) % hm.entries.len;
......@@ -142,7 +186,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
142186 if (!next_entry.used or next_entry.distance_from_start_index == 0) {
143187 entry.used = false;
144188 hm.size -= 1;
145 return entry;
189 return &entry.kv;
146190 }
147191 entry.* = next_entry.*;
148192 entry.distance_from_start_index -= 1;
......@@ -163,6 +207,16 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
163207 };
164208 }
165209
210 pub fn clone(self: Self) !Self {
211 var other = Self.init(self.allocator);
212 try other.initCapacity(self.entries.len);
213 var it = self.iterator();
214 while (it.next()) |entry| {
215 assert((try other.put(entry.key, entry.value)) == null);
216 }
217 return other;
218 }
219
166220 fn initCapacity(hm: *Self, capacity: usize) !void {
167221 hm.entries = try hm.allocator.alloc(Entry, capacity);
168222 hm.size = 0;
......@@ -178,60 +232,81 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
178232 }
179233 }
180234
181 /// Returns the value that was already there.
182 fn internalPut(hm: *Self, orig_key: K, orig_value: *const V) ?V {
235 const InternalPutResult = struct {
236 new_entry: *Entry,
237 old_kv: ?KV,
238 };
239
240 /// Returns a pointer to the new entry.
241 /// Asserts that there is enough space for the new item.
242 fn internalPut(self: *Self, orig_key: K) InternalPutResult {
183243 var key = orig_key;
184 var value = orig_value.*;
185 const start_index = hm.keyToIndex(key);
244 var value: V = undefined;
245 const start_index = self.keyToIndex(key);
186246 var roll_over: usize = 0;
187247 var distance_from_start_index: usize = 0;
188 while (roll_over < hm.entries.len) : ({
248 var got_result_entry = false;
249 var result = InternalPutResult{
250 .new_entry = undefined,
251 .old_kv = null,
252 };
253 while (roll_over < self.entries.len) : ({
189254 roll_over += 1;
190255 distance_from_start_index += 1;
191256 }) {
192 const index = (start_index + roll_over) % hm.entries.len;
193 const entry = &hm.entries[index];
257 const index = (start_index + roll_over) % self.entries.len;
258 const entry = &self.entries[index];
194259
195 if (entry.used and !eql(entry.key, key)) {
260 if (entry.used and !eql(entry.kv.key, key)) {
196261 if (entry.distance_from_start_index < distance_from_start_index) {
197262 // robin hood to the rescue
198263 const tmp = entry.*;
199 hm.max_distance_from_start_index = math.max(hm.max_distance_from_start_index, distance_from_start_index);
264 self.max_distance_from_start_index = math.max(self.max_distance_from_start_index, distance_from_start_index);
265 if (!got_result_entry) {
266 got_result_entry = true;
267 result.new_entry = entry;
268 }
200269 entry.* = Entry{
201270 .used = true,
202271 .distance_from_start_index = distance_from_start_index,
203 .key = key,
204 .value = value,
272 .kv = KV{
273 .key = key,
274 .value = value,
275 },
205276 };
206 key = tmp.key;
207 value = tmp.value;
277 key = tmp.kv.key;
278 value = tmp.kv.value;
208279 distance_from_start_index = tmp.distance_from_start_index;
209280 }
210281 continue;
211282 }
212283
213 var result: ?V = null;
214284 if (entry.used) {
215 result = entry.value;
285 result.old_kv = entry.kv;
216286 } else {
217287 // adding an entry. otherwise overwriting old value with
218288 // same key
219 hm.size += 1;
289 self.size += 1;
220290 }
221291
222 hm.max_distance_from_start_index = math.max(distance_from_start_index, hm.max_distance_from_start_index);
292 self.max_distance_from_start_index = math.max(distance_from_start_index, self.max_distance_from_start_index);
293 if (!got_result_entry) {
294 result.new_entry = entry;
295 }
223296 entry.* = Entry{
224297 .used = true,
225298 .distance_from_start_index = distance_from_start_index,
226 .key = key,
227 .value = value,
299 .kv = KV{
300 .key = key,
301 .value = value,
302 },
228303 };
229304 return result;
230305 }
231306 unreachable; // put into a full map
232307 }
233308
234 fn internalGet(hm: *const Self, key: K) ?*Entry {
309 fn internalGet(hm: Self, key: K) ?*KV {
235310 const start_index = hm.keyToIndex(key);
236311 {
237312 var roll_over: usize = 0;
......@@ -240,13 +315,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
240315 const entry = &hm.entries[index];
241316
242317 if (!entry.used) return null;
243 if (eql(entry.key, key)) return entry;
318 if (eql(entry.kv.key, key)) return &entry.kv;
244319 }
245320 }
246321 return null;
247322 }
248323
249 fn keyToIndex(hm: *const Self, key: K) usize {
324 fn keyToIndex(hm: Self, key: K) usize {
250325 return usize(hash(key)) % hm.entries.len;
251326 }
252327 };
......@@ -256,7 +331,7 @@ test "basic hash map usage" {
256331 var direct_allocator = std.heap.DirectAllocator.init();
257332 defer direct_allocator.deinit();
258333
259 var map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
334 var map = AutoHashMap(i32, i32).init(&direct_allocator.allocator);
260335 defer map.deinit();
261336
262337 assert((try map.put(1, 11)) == null);
......@@ -265,8 +340,19 @@ test "basic hash map usage" {
265340 assert((try map.put(4, 44)) == null);
266341 assert((try map.put(5, 55)) == null);
267342
268 assert((try map.put(5, 66)).? == 55);
269 assert((try map.put(5, 55)).? == 66);
343 assert((try map.put(5, 66)).?.value == 55);
344 assert((try map.put(5, 55)).?.value == 66);
345
346 const gop1 = try map.getOrPut(5);
347 assert(gop1.found_existing == true);
348 assert(gop1.kv.value == 55);
349 gop1.kv.value = 77;
350 assert(map.get(5).?.value == 77);
351
352 const gop2 = try map.getOrPut(99);
353 assert(gop2.found_existing == false);
354 gop2.kv.value = 42;
355 assert(map.get(99).?.value == 42);
270356
271357 assert(map.contains(2));
272358 assert(map.get(2).?.value == 22);
......@@ -279,7 +365,7 @@ test "iterator hash map" {
279365 var direct_allocator = std.heap.DirectAllocator.init();
280366 defer direct_allocator.deinit();
281367
282 var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
368 var reset_map = AutoHashMap(i32, i32).init(&direct_allocator.allocator);
283369 defer reset_map.deinit();
284370
285371 assert((try reset_map.put(1, 11)) == null);
......@@ -287,14 +373,14 @@ test "iterator hash map" {
287373 assert((try reset_map.put(3, 33)) == null);
288374
289375 var keys = []i32{
290 1,
291 2,
292376 3,
377 2,
378 1,
293379 };
294380 var values = []i32{
295 11,
296 22,
297381 33,
382 22,
383 11,
298384 };
299385
300386 var it = reset_map.iterator();
......@@ -322,10 +408,140 @@ test "iterator hash map" {
322408 assert(entry.value == values[0]);
323409}
324410
325fn hash_i32(x: i32) u32 {
326 return @bitCast(u32, x);
411pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) {
412 return struct {
413 fn hash(key: K) u32 {
414 return getAutoHashFn(usize)(@ptrToInt(key));
415 }
416 }.hash;
327417}
328418
329fn eql_i32(a: i32, b: i32) bool {
330 return a == b;
419pub fn getTrivialEqlFn(comptime K: type) (fn (K, K) bool) {
420 return struct {
421 fn eql(a: K, b: K) bool {
422 return a == b;
423 }
424 }.eql;
425}
426
427pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
428 return struct {
429 fn hash(key: K) u32 {
430 comptime var rng = comptime std.rand.DefaultPrng.init(0);
431 return autoHash(key, &rng.random, u32);
432 }
433 }.hash;
434}
435
436pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
437 return struct {
438 fn eql(a: K, b: K) bool {
439 return autoEql(a, b);
440 }
441 }.eql;
442}
443
444// TODO improve these hash functions
445pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type) HashInt {
446 switch (@typeInfo(@typeOf(key))) {
447 builtin.TypeId.NoReturn,
448 builtin.TypeId.Opaque,
449 builtin.TypeId.Undefined,
450 builtin.TypeId.ArgTuple,
451 => @compileError("cannot hash this type"),
452
453 builtin.TypeId.Void,
454 builtin.TypeId.Null,
455 => return 0,
456
457 builtin.TypeId.Int => |info| {
458 const unsigned_x = @bitCast(@IntType(false, info.bits), key);
459 if (info.bits <= HashInt.bit_count) {
460 return HashInt(unsigned_x) ^ comptime rng.scalar(HashInt);
461 } else {
462 return @truncate(HashInt, unsigned_x ^ comptime rng.scalar(@typeOf(unsigned_x)));
463 }
464 },
465
466 builtin.TypeId.Float => |info| {
467 return autoHash(@bitCast(@IntType(false, info.bits), key), rng);
468 },
469 builtin.TypeId.Bool => return autoHash(@boolToInt(key), rng),
470 builtin.TypeId.Enum => return autoHash(@enumToInt(key), rng),
471 builtin.TypeId.ErrorSet => return autoHash(@errorToInt(key), rng),
472 builtin.TypeId.Promise, builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng),
473
474 builtin.TypeId.Namespace,
475 builtin.TypeId.Block,
476 builtin.TypeId.BoundFn,
477 builtin.TypeId.ComptimeFloat,
478 builtin.TypeId.ComptimeInt,
479 builtin.TypeId.Type,
480 => return 0,
481
482 builtin.TypeId.Pointer => |info| switch (info.size) {
483 builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto hash for single item pointers"),
484 builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto hash for many item pointers"),
485 builtin.TypeInfo.Pointer.Size.Slice => {
486 const interval = std.math.max(1, key.len / 256);
487 var i: usize = 0;
488 var h = comptime rng.scalar(HashInt);
489 while (i < key.len) : (i += interval) {
490 h ^= autoHash(key[i], rng, HashInt);
491 }
492 return h;
493 },
494 },
495
496 builtin.TypeId.Optional => @compileError("TODO auto hash for optionals"),
497 builtin.TypeId.Array => @compileError("TODO auto hash for arrays"),
498 builtin.TypeId.Struct => @compileError("TODO auto hash for structs"),
499 builtin.TypeId.Union => @compileError("TODO auto hash for unions"),
500 builtin.TypeId.ErrorUnion => @compileError("TODO auto hash for unions"),
501 }
502}
503
504pub fn autoEql(a: var, b: @typeOf(a)) bool {
505 switch (@typeInfo(@typeOf(a))) {
506 builtin.TypeId.NoReturn,
507 builtin.TypeId.Opaque,
508 builtin.TypeId.Undefined,
509 builtin.TypeId.ArgTuple,
510 => @compileError("cannot test equality of this type"),
511 builtin.TypeId.Void,
512 builtin.TypeId.Null,
513 => return true,
514 builtin.TypeId.Bool,
515 builtin.TypeId.Int,
516 builtin.TypeId.Float,
517 builtin.TypeId.ComptimeFloat,
518 builtin.TypeId.ComptimeInt,
519 builtin.TypeId.Namespace,
520 builtin.TypeId.Block,
521 builtin.TypeId.Promise,
522 builtin.TypeId.Enum,
523 builtin.TypeId.BoundFn,
524 builtin.TypeId.Fn,
525 builtin.TypeId.ErrorSet,
526 builtin.TypeId.Type,
527 => return a == b,
528
529 builtin.TypeId.Pointer => |info| switch (info.size) {
530 builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto eql for single item pointers"),
531 builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto eql for many item pointers"),
532 builtin.TypeInfo.Pointer.Size.Slice => {
533 if (a.len != b.len) return false;
534 for (a) |a_item, i| {
535 if (!autoEql(a_item, b[i])) return false;
536 }
537 return true;
538 },
539 },
540
541 builtin.TypeId.Optional => @compileError("TODO auto eql for optionals"),
542 builtin.TypeId.Array => @compileError("TODO auto eql for arrays"),
543 builtin.TypeId.Struct => @compileError("TODO auto eql for structs"),
544 builtin.TypeId.Union => @compileError("TODO auto eql for unions"),
545 builtin.TypeId.ErrorUnion => @compileError("TODO auto eql for unions"),
546 }
331547}
std/index.zig+5-1
......@@ -5,10 +5,11 @@ pub const BufSet = @import("buf_set.zig").BufSet;
55pub const Buffer = @import("buffer.zig").Buffer;
66pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
77pub const HashMap = @import("hash_map.zig").HashMap;
8pub const AutoHashMap = @import("hash_map.zig").AutoHashMap;
89pub const LinkedList = @import("linked_list.zig").LinkedList;
9pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
1010pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
1111pub const DynLib = @import("dynamic_library.zig").DynLib;
12pub const Mutex = @import("mutex.zig").Mutex;
1213
1314pub const atomic = @import("atomic/index.zig");
1415pub const base64 = @import("base64.zig");
......@@ -23,6 +24,7 @@ pub const empty_import = @import("empty.zig");
2324pub const event = @import("event.zig");
2425pub const fmt = @import("fmt/index.zig");
2526pub const hash = @import("hash/index.zig");
27pub const hash_map = @import("hash_map.zig");
2628pub const heap = @import("heap.zig");
2729pub const io = @import("io.zig");
2830pub const json = @import("json.zig");
......@@ -32,6 +34,7 @@ pub const mem = @import("mem.zig");
3234pub const net = @import("net.zig");
3335pub const os = @import("os/index.zig");
3436pub const rand = @import("rand/index.zig");
37pub const rb = @import("rb.zig");
3538pub const sort = @import("sort.zig");
3639pub const unicode = @import("unicode.zig");
3740pub const zig = @import("zig/index.zig");
......@@ -48,6 +51,7 @@ test "std" {
4851 _ = @import("hash_map.zig");
4952 _ = @import("linked_list.zig");
5053 _ = @import("segmented_list.zig");
54 _ = @import("mutex.zig");
5155
5256 _ = @import("base64.zig");
5357 _ = @import("build.zig");
std/io.zig+16-13
......@@ -207,6 +207,12 @@ pub fn InStream(comptime ReadError: type) type {
207207 _ = try self.readByte();
208208 }
209209 }
210
211 pub fn readStruct(self: *Self, comptime T: type, ptr: *T) !void {
212 // Only extern and packed structs have defined in-memory layout.
213 assert(@typeInfo(T).Struct.layout != builtin.TypeInfo.ContainerLayout.Auto);
214 return self.readNoEof(@sliceToBytes((*[1]T)(ptr)[0..]));
215 }
210216 };
211217}
212218
......@@ -254,9 +260,8 @@ pub fn OutStream(comptime WriteError: type) type {
254260 };
255261}
256262
257/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
258pub fn writeFile(allocator: *mem.Allocator, path: []const u8, data: []const u8) !void {
259 var file = try File.openWrite(allocator, path);
263pub fn writeFile(path: []const u8, data: []const u8) !void {
264 var file = try File.openWrite(path);
260265 defer file.close();
261266 try file.write(data);
262267}
......@@ -268,7 +273,7 @@ pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
268273
269274/// On success, caller owns returned buffer.
270275pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 {
271 var file = try File.openRead(allocator, path);
276 var file = try File.openRead(path);
272277 defer file.close();
273278
274279 const size = try file.getEndPos();
......@@ -415,13 +420,12 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ
415420 self.at_end = (read < left);
416421 return pos + read;
417422 }
418
419423 };
420424}
421425
422426pub const SliceInStream = struct {
423427 const Self = this;
424 pub const Error = error { };
428 pub const Error = error{};
425429 pub const Stream = InStream(Error);
426430
427431 pub stream: Stream,
......@@ -481,13 +485,12 @@ pub const SliceOutStream = struct {
481485
482486 assert(self.pos <= self.slice.len);
483487
484 const n =
485 if (self.pos + bytes.len <= self.slice.len)
486 bytes.len
487 else
488 self.slice.len - self.pos;
488 const n = if (self.pos + bytes.len <= self.slice.len)
489 bytes.len
490 else
491 self.slice.len - self.pos;
489492
490 std.mem.copy(u8, self.slice[self.pos..self.pos + n], bytes[0..n]);
493 std.mem.copy(u8, self.slice[self.pos .. self.pos + n], bytes[0..n]);
491494 self.pos += n;
492495
493496 if (n < bytes.len) {
......@@ -586,7 +589,7 @@ pub const BufferedAtomicFile = struct {
586589 });
587590 errdefer allocator.destroy(self);
588591
589 self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.default_file_mode);
592 self.atomic_file = try os.AtomicFile.init(allocator, dest_path, os.File.default_mode);
590593 errdefer self.atomic_file.deinit();
591594
592595 self.file_stream = FileOutStream.init(&self.atomic_file.file);
std/io_test.zig+5-5
......@@ -16,7 +16,7 @@ test "write a file, read it, then delete it" {
1616 prng.random.bytes(data[0..]);
1717 const tmp_file_name = "temp_test_file.txt";
1818 {
19 var file = try os.File.openWrite(allocator, tmp_file_name);
19 var file = try os.File.openWrite(tmp_file_name);
2020 defer file.close();
2121
2222 var file_out_stream = io.FileOutStream.init(&file);
......@@ -28,7 +28,7 @@ test "write a file, read it, then delete it" {
2828 try buf_stream.flush();
2929 }
3030 {
31 var file = try os.File.openRead(allocator, tmp_file_name);
31 var file = try os.File.openRead(tmp_file_name);
3232 defer file.close();
3333
3434 const file_size = try file.getEndPos();
......@@ -45,7 +45,7 @@ test "write a file, read it, then delete it" {
4545 assert(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data));
4646 assert(mem.eql(u8, contents[contents.len - "end".len ..], "end"));
4747 }
48 try os.deleteFile(allocator, tmp_file_name);
48 try os.deleteFile(tmp_file_name);
4949}
5050
5151test "BufferOutStream" {
......@@ -63,7 +63,7 @@ test "BufferOutStream" {
6363}
6464
6565test "SliceInStream" {
66 const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7 };
66 const bytes = []const u8{ 1, 2, 3, 4, 5, 6, 7 };
6767 var ss = io.SliceInStream.init(bytes);
6868
6969 var dest: [4]u8 = undefined;
......@@ -81,7 +81,7 @@ test "SliceInStream" {
8181}
8282
8383test "PeekStream" {
84 const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7, 8 };
84 const bytes = []const u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
8585 var ss = io.SliceInStream.init(bytes);
8686 var ps = io.PeekStream(2, io.SliceInStream.Error).init(&ss.stream);
8787
std/json.zig+1-1
......@@ -1318,7 +1318,7 @@ pub const Parser = struct {
13181318 _ = p.stack.pop();
13191319
13201320 var object = &p.stack.items[p.stack.len - 1].Object;
1321 _ = try object.put(key, value);
1321 _ = try object.put(key, value.*);
13221322 p.state = State.ObjectKey;
13231323 },
13241324 // Array Parent -> [ ..., <array>, value ]
std/linked_list.zig+4-97
......@@ -4,18 +4,8 @@ const assert = debug.assert;
44const mem = std.mem;
55const Allocator = mem.Allocator;
66
7/// Generic non-intrusive doubly linked list.
8pub fn LinkedList(comptime T: type) type {
9 return BaseLinkedList(T, void, "");
10}
11
12/// Generic intrusive doubly linked list.
13pub fn IntrusiveLinkedList(comptime ParentType: type, comptime field_name: []const u8) type {
14 return BaseLinkedList(void, ParentType, field_name);
15}
16
177/// Generic doubly linked list.
18fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_name: []const u8) type {
8pub fn LinkedList(comptime T: type) type {
199 return struct {
2010 const Self = this;
2111
......@@ -25,23 +15,13 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
2515 next: ?*Node,
2616 data: T,
2717
28 pub fn init(value: *const T) Node {
18 pub fn init(data: T) Node {
2919 return Node{
3020 .prev = null,
3121 .next = null,
32 .data = value.*,
22 .data = data,
3323 };
3424 }
35
36 pub fn initIntrusive() Node {
37 // TODO: when #678 is solved this can become `init`.
38 return Node.init({});
39 }
40
41 pub fn toData(node: *Node) *ParentType {
42 comptime assert(isIntrusive());
43 return @fieldParentPtr(ParentType, field_name, node);
44 }
4525 };
4626
4727 first: ?*Node,
......@@ -60,10 +40,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
6040 };
6141 }
6242
63 fn isIntrusive() bool {
64 return ParentType != void or field_name.len != 0;
65 }
66
6743 /// Insert a new node after an existing one.
6844 ///
6945 /// Arguments:
......@@ -192,7 +168,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
192168 /// Returns:
193169 /// A pointer to the new node.
194170 pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {
195 comptime assert(!isIntrusive());
196171 return allocator.create(Node(undefined));
197172 }
198173
......@@ -202,7 +177,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
202177 /// node: Pointer to the node to deallocate.
203178 /// allocator: Dynamic memory allocator.
204179 pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {
205 comptime assert(!isIntrusive());
206180 allocator.destroy(node);
207181 }
208182
......@@ -214,8 +188,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
214188 ///
215189 /// Returns:
216190 /// A pointer to the new node.
217 pub fn createNode(list: *Self, data: *const T, allocator: *Allocator) !*Node {
218 comptime assert(!isIntrusive());
191 pub fn createNode(list: *Self, data: T, allocator: *Allocator) !*Node {
219192 var node = try list.allocateNode(allocator);
220193 node.* = Node.init(data);
221194 return node;
......@@ -274,69 +247,3 @@ test "basic linked list test" {
274247 assert(list.last.?.data == 4);
275248 assert(list.len == 2);
276249}
277
278const ElementList = IntrusiveLinkedList(Element, "link");
279const Element = struct {
280 value: u32,
281 link: IntrusiveLinkedList(Element, "link").Node,
282};
283
284test "basic intrusive linked list test" {
285 const allocator = debug.global_allocator;
286 var list = ElementList.init();
287
288 var one = Element{
289 .value = 1,
290 .link = ElementList.Node.initIntrusive(),
291 };
292 var two = Element{
293 .value = 2,
294 .link = ElementList.Node.initIntrusive(),
295 };
296 var three = Element{
297 .value = 3,
298 .link = ElementList.Node.initIntrusive(),
299 };
300 var four = Element{
301 .value = 4,
302 .link = ElementList.Node.initIntrusive(),
303 };
304 var five = Element{
305 .value = 5,
306 .link = ElementList.Node.initIntrusive(),
307 };
308
309 list.append(&two.link); // {2}
310 list.append(&five.link); // {2, 5}
311 list.prepend(&one.link); // {1, 2, 5}
312 list.insertBefore(&five.link, &four.link); // {1, 2, 4, 5}
313 list.insertAfter(&two.link, &three.link); // {1, 2, 3, 4, 5}
314
315 // Traverse forwards.
316 {
317 var it = list.first;
318 var index: u32 = 1;
319 while (it) |node| : (it = node.next) {
320 assert(node.toData().value == index);
321 index += 1;
322 }
323 }
324
325 // Traverse backwards.
326 {
327 var it = list.last;
328 var index: u32 = 1;
329 while (it) |node| : (it = node.prev) {
330 assert(node.toData().value == (6 - index));
331 index += 1;
332 }
333 }
334
335 var first = list.popFirst(); // {2, 3, 4, 5}
336 var last = list.pop(); // {2, 3, 4}
337 list.remove(&three.link); // {2, 4}
338
339 assert(list.first.?.toData().value == 2);
340 assert(list.last.?.toData().value == 4);
341 assert(list.len == 2);
342}
std/macho.zig+322-146
......@@ -1,16 +1,18 @@
1const builtin = @import("builtin");
2const std = @import("index.zig");
3const io = std.io;
4const mem = std.mem;
51
6const MH_MAGIC_64 = 0xFEEDFACF;
7const MH_PIE = 0x200000;
8const LC_SYMTAB = 2;
2pub const mach_header = extern struct {
3 magic: u32,
4 cputype: cpu_type_t,
5 cpusubtype: cpu_subtype_t,
6 filetype: u32,
7 ncmds: u32,
8 sizeofcmds: u32,
9 flags: u32,
10};
911
10const MachHeader64 = packed struct {
12pub const mach_header_64 = extern struct {
1113 magic: u32,
12 cputype: u32,
13 cpusubtype: u32,
14 cputype: cpu_type_t,
15 cpusubtype: cpu_subtype_t,
1416 filetype: u32,
1517 ncmds: u32,
1618 sizeofcmds: u32,
......@@ -18,19 +20,138 @@ const MachHeader64 = packed struct {
1820 reserved: u32,
1921};
2022
21const LoadCommand = packed struct {
23pub const load_command = extern struct {
2224 cmd: u32,
2325 cmdsize: u32,
2426};
2527
26const SymtabCommand = packed struct {
27 symoff: u32,
28 nsyms: u32,
29 stroff: u32,
30 strsize: u32,
28
29/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
30/// "stab" style symbol table information as described in the header files
31/// <nlist.h> and <stab.h>.
32pub const symtab_command = extern struct {
33 cmd: u32, /// LC_SYMTAB
34 cmdsize: u32, /// sizeof(struct symtab_command)
35 symoff: u32, /// symbol table offset
36 nsyms: u32, /// number of symbol table entries
37 stroff: u32, /// string table offset
38 strsize: u32, /// string table size in bytes
39};
40
41/// The linkedit_data_command contains the offsets and sizes of a blob
42/// of data in the __LINKEDIT segment.
43const linkedit_data_command = extern struct {
44 cmd: u32,/// LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS or LC_LINKER_OPTIMIZATION_HINT.
45 cmdsize: u32, /// sizeof(struct linkedit_data_command)
46 dataoff: u32 , /// file offset of data in __LINKEDIT segment
47 datasize: u32 , /// file size of data in __LINKEDIT segment
48};
49
50/// The segment load command indicates that a part of this file is to be
51/// mapped into the task's address space. The size of this segment in memory,
52/// vmsize, maybe equal to or larger than the amount to map from this file,
53/// filesize. The file is mapped starting at fileoff to the beginning of
54/// the segment in memory, vmaddr. The rest of the memory of the segment,
55/// if any, is allocated zero fill on demand. The segment's maximum virtual
56/// memory protection and initial virtual memory protection are specified
57/// by the maxprot and initprot fields. If the segment has sections then the
58/// section structures directly follow the segment command and their size is
59/// reflected in cmdsize.
60pub const segment_command = extern struct {
61 cmd: u32,/// LC_SEGMENT
62 cmdsize: u32,/// includes sizeof section structs
63 segname: [16]u8,/// segment name
64 vmaddr: u32,/// memory address of this segment
65 vmsize: u32,/// memory size of this segment
66 fileoff: u32,/// file offset of this segment
67 filesize: u32,/// amount to map from the file
68 maxprot: vm_prot_t,/// maximum VM protection
69 initprot: vm_prot_t,/// initial VM protection
70 nsects: u32,/// number of sections in segment
71 flags: u32,
72};
73
74/// The 64-bit segment load command indicates that a part of this file is to be
75/// mapped into a 64-bit task's address space. If the 64-bit segment has
76/// sections then section_64 structures directly follow the 64-bit segment
77/// command and their size is reflected in cmdsize.
78pub const segment_command_64 = extern struct {
79 cmd: u32, /// LC_SEGMENT_64
80 cmdsize: u32, /// includes sizeof section_64 structs
81 segname: [16]u8, /// segment name
82 vmaddr: u64, /// memory address of this segment
83 vmsize: u64, /// memory size of this segment
84 fileoff: u64, /// file offset of this segment
85 filesize: u64, /// amount to map from the file
86 maxprot: vm_prot_t, /// maximum VM protection
87 initprot: vm_prot_t, /// initial VM protection
88 nsects: u32, /// number of sections in segment
89 flags: u32,
90};
91
92/// A segment is made up of zero or more sections. Non-MH_OBJECT files have
93/// all of their segments with the proper sections in each, and padded to the
94/// specified segment alignment when produced by the link editor. The first
95/// segment of a MH_EXECUTE and MH_FVMLIB format file contains the mach_header
96/// and load commands of the object file before its first section. The zero
97/// fill sections are always last in their segment (in all formats). This
98/// allows the zeroed segment padding to be mapped into memory where zero fill
99/// sections might be. The gigabyte zero fill sections, those with the section
100/// type S_GB_ZEROFILL, can only be in a segment with sections of this type.
101/// These segments are then placed after all other segments.
102///
103/// The MH_OBJECT format has all of its sections in one segment for
104/// compactness. There is no padding to a specified segment boundary and the
105/// mach_header and load commands are not part of the segment.
106///
107/// Sections with the same section name, sectname, going into the same segment,
108/// segname, are combined by the link editor. The resulting section is aligned
109/// to the maximum alignment of the combined sections and is the new section's
110/// alignment. The combined sections are aligned to their original alignment in
111/// the combined section. Any padded bytes to get the specified alignment are
112/// zeroed.
113///
114/// The format of the relocation entries referenced by the reloff and nreloc
115/// fields of the section structure for mach object files is described in the
116/// header file <reloc.h>.
117pub const @"section" = extern struct {
118 sectname: [16]u8, /// name of this section
119 segname: [16]u8, /// segment this section goes in
120 addr: u32, /// memory address of this section
121 size: u32, /// size in bytes of this section
122 offset: u32, /// file offset of this section
123 @"align": u32, /// section alignment (power of 2)
124 reloff: u32, /// file offset of relocation entries
125 nreloc: u32, /// number of relocation entries
126 flags: u32, /// flags (section type and attributes
127 reserved1: u32, /// reserved (for offset or index)
128 reserved2: u32, /// reserved (for count or sizeof)
129};
130
131pub const section_64 = extern struct {
132 sectname: [16]u8, /// name of this section
133 segname: [16]u8, /// segment this section goes in
134 addr: u64, /// memory address of this section
135 size: u64, /// size in bytes of this section
136 offset: u32, /// file offset of this section
137 @"align": u32, /// section alignment (power of 2)
138 reloff: u32, /// file offset of relocation entries
139 nreloc: u32, /// number of relocation entries
140 flags: u32, /// flags (section type and attributes
141 reserved1: u32, /// reserved (for offset or index)
142 reserved2: u32, /// reserved (for count or sizeof)
143 reserved3: u32, /// reserved
144};
145
146pub const nlist = extern struct {
147 n_strx: u32,
148 n_type: u8,
149 n_sect: u8,
150 n_desc: i16,
151 n_value: u32,
31152};
32153
33const Nlist64 = packed struct {
154pub const nlist_64 = extern struct {
34155 n_strx: u32,
35156 n_type: u8,
36157 n_sect: u8,
......@@ -38,135 +159,190 @@ const Nlist64 = packed struct {
38159 n_value: u64,
39160};
40161
41pub const Symbol = struct {
42 name: []const u8,
43 address: u64,
162/// After MacOS X 10.1 when a new load command is added that is required to be
163/// understood by the dynamic linker for the image to execute properly the
164/// LC_REQ_DYLD bit will be or'ed into the load command constant. If the dynamic
165/// linker sees such a load command it it does not understand will issue a
166/// "unknown load command required for execution" error and refuse to use the
167/// image. Other load commands without this bit that are not understood will
168/// simply be ignored.
169pub const LC_REQ_DYLD = 0x80000000;
44170
45 fn addressLessThan(lhs: Symbol, rhs: Symbol) bool {
46 return lhs.address < rhs.address;
47 }
48};
171pub const LC_SEGMENT = 0x1; /// segment of this file to be mapped
172pub const LC_SYMTAB = 0x2; /// link-edit stab symbol table info
173pub const LC_SYMSEG = 0x3; /// link-edit gdb symbol table info (obsolete)
174pub const LC_THREAD = 0x4; /// thread
175pub const LC_UNIXTHREAD = 0x5; /// unix thread (includes a stack)
176pub const LC_LOADFVMLIB = 0x6; /// load a specified fixed VM shared library
177pub const LC_IDFVMLIB = 0x7; /// fixed VM shared library identification
178pub const LC_IDENT = 0x8; /// object identification info (obsolete)
179pub const LC_FVMFILE = 0x9; /// fixed VM file inclusion (internal use)
180pub const LC_PREPAGE = 0xa; /// prepage command (internal use)
181pub const LC_DYSYMTAB = 0xb; /// dynamic link-edit symbol table info
182pub const LC_LOAD_DYLIB = 0xc; /// load a dynamically linked shared library
183pub const LC_ID_DYLIB = 0xd; /// dynamically linked shared lib ident
184pub const LC_LOAD_DYLINKER = 0xe; /// load a dynamic linker
185pub const LC_ID_DYLINKER = 0xf; /// dynamic linker identification
186pub const LC_PREBOUND_DYLIB = 0x10; /// modules prebound for a dynamically
187pub const LC_ROUTINES = 0x11; /// image routines
188pub const LC_SUB_FRAMEWORK = 0x12; /// sub framework
189pub const LC_SUB_UMBRELLA = 0x13; /// sub umbrella
190pub const LC_SUB_CLIENT = 0x14; /// sub client
191pub const LC_SUB_LIBRARY = 0x15; /// sub library
192pub const LC_TWOLEVEL_HINTS = 0x16; /// two-level namespace lookup hints
193pub const LC_PREBIND_CKSUM = 0x17; /// prebind checksum
49194
50pub const SymbolTable = struct {
51 allocator: *mem.Allocator,
52 symbols: []const Symbol,
53 strings: []const u8,
54
55 // Doubles as an eyecatcher to calculate the PIE slide, see loadSymbols().
56 // Ideally we'd use _mh_execute_header because it's always at 0x100000000
57 // in the image but as it's located in a different section than executable
58 // code, its displacement is different.
59 pub fn deinit(self: *SymbolTable) void {
60 self.allocator.free(self.symbols);
61 self.symbols = []const Symbol{};
62
63 self.allocator.free(self.strings);
64 self.strings = []const u8{};
65 }
66
67 pub fn search(self: *const SymbolTable, address: usize) ?*const Symbol {
68 var min: usize = 0;
69 var max: usize = self.symbols.len - 1; // Exclude sentinel.
70 while (min < max) {
71 const mid = min + (max - min) / 2;
72 const curr = &self.symbols[mid];
73 const next = &self.symbols[mid + 1];
74 if (address >= next.address) {
75 min = mid + 1;
76 } else if (address < curr.address) {
77 max = mid;
78 } else {
79 return curr;
80 }
81 }
82 return null;
83 }
84};
195/// load a dynamically linked shared library that is allowed to be missing
196/// (all symbols are weak imported).
197pub const LC_LOAD_WEAK_DYLIB = (0x18 | LC_REQ_DYLD);
198
199pub const LC_SEGMENT_64 = 0x19; /// 64-bit segment of this file to be mapped
200pub const LC_ROUTINES_64 = 0x1a; /// 64-bit image routines
201pub const LC_UUID = 0x1b; /// the uuid
202pub const LC_RPATH = (0x1c | LC_REQ_DYLD); /// runpath additions
203pub const LC_CODE_SIGNATURE = 0x1d; /// local of code signature
204pub const LC_SEGMENT_SPLIT_INFO = 0x1e; /// local of info to split segments
205pub const LC_REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD); /// load and re-export dylib
206pub const LC_LAZY_LOAD_DYLIB = 0x20; /// delay load of dylib until first use
207pub const LC_ENCRYPTION_INFO = 0x21; /// encrypted segment information
208pub const LC_DYLD_INFO = 0x22; /// compressed dyld information
209pub const LC_DYLD_INFO_ONLY = (0x22|LC_REQ_DYLD); /// compressed dyld information only
210pub const LC_LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD); /// load upward dylib
211pub const LC_VERSION_MIN_MACOSX = 0x24; /// build for MacOSX min OS version
212pub const LC_VERSION_MIN_IPHONEOS = 0x25; /// build for iPhoneOS min OS version
213pub const LC_FUNCTION_STARTS = 0x26; /// compressed table of function start addresses
214pub const LC_DYLD_ENVIRONMENT = 0x27; /// string for dyld to treat like environment variable
215pub const LC_MAIN = (0x28|LC_REQ_DYLD); /// replacement for LC_UNIXTHREAD
216pub const LC_DATA_IN_CODE = 0x29; /// table of non-instructions in __text
217pub const LC_SOURCE_VERSION = 0x2A; /// source version used to build binary
218pub const LC_DYLIB_CODE_SIGN_DRS = 0x2B; /// Code signing DRs copied from linked dylibs
219pub const LC_ENCRYPTION_INFO_64 = 0x2C; /// 64-bit encrypted segment information
220pub const LC_LINKER_OPTION = 0x2D; /// linker options in MH_OBJECT files
221pub const LC_LINKER_OPTIMIZATION_HINT = 0x2E; /// optimization hints in MH_OBJECT files
222pub const LC_VERSION_MIN_TVOS = 0x2F; /// build for AppleTV min OS version
223pub const LC_VERSION_MIN_WATCHOS = 0x30; /// build for Watch min OS version
224pub const LC_NOTE = 0x31; /// arbitrary data included within a Mach-O file
225pub const LC_BUILD_VERSION = 0x32; /// build for platform min OS version
226
227pub const MH_MAGIC = 0xfeedface; /// the mach magic number
228pub const MH_CIGAM = 0xcefaedfe; /// NXSwapInt(MH_MAGIC)
229
230pub const MH_MAGIC_64 = 0xfeedfacf; /// the 64-bit mach magic number
231pub const MH_CIGAM_64 = 0xcffaedfe; /// NXSwapInt(MH_MAGIC_64)
232
233pub const MH_OBJECT = 0x1; /// relocatable object file
234pub const MH_EXECUTE = 0x2; /// demand paged executable file
235pub const MH_FVMLIB = 0x3; /// fixed VM shared library file
236pub const MH_CORE = 0x4; /// core file
237pub const MH_PRELOAD = 0x5; /// preloaded executable file
238pub const MH_DYLIB = 0x6; /// dynamically bound shared library
239pub const MH_DYLINKER = 0x7; /// dynamic link editor
240pub const MH_BUNDLE = 0x8; /// dynamically bound bundle file
241pub const MH_DYLIB_STUB = 0x9; /// shared library stub for static linking only, no section contents
242pub const MH_DSYM = 0xa; /// companion file with only debug sections
243pub const MH_KEXT_BUNDLE = 0xb; /// x86_64 kexts
244
245// Constants for the flags field of the mach_header
246
247pub const MH_NOUNDEFS = 0x1; /// the object file has no undefined references
248pub const MH_INCRLINK = 0x2; /// the object file is the output of an incremental link against a base file and can't be link edited again
249pub const MH_DYLDLINK = 0x4; /// the object file is input for the dynamic linker and can't be staticly link edited again
250pub const MH_BINDATLOAD = 0x8; /// the object file's undefined references are bound by the dynamic linker when loaded.
251pub const MH_PREBOUND = 0x10; /// the file has its dynamic undefined references prebound.
252pub const MH_SPLIT_SEGS = 0x20; /// the file has its read-only and read-write segments split
253pub const MH_LAZY_INIT = 0x40; /// the shared library init routine is to be run lazily via catching memory faults to its writeable segments (obsolete)
254pub const MH_TWOLEVEL = 0x80; /// the image is using two-level name space bindings
255pub const MH_FORCE_FLAT = 0x100; /// the executable is forcing all images to use flat name space bindings
256pub const MH_NOMULTIDEFS = 0x200; /// this umbrella guarantees no multiple defintions of symbols in its sub-images so the two-level namespace hints can always be used.
257pub const MH_NOFIXPREBINDING = 0x400; /// do not have dyld notify the prebinding agent about this executable
258pub const MH_PREBINDABLE = 0x800; /// the binary is not prebound but can have its prebinding redone. only used when MH_PREBOUND is not set.
259pub const MH_ALLMODSBOUND = 0x1000; /// indicates that this binary binds to all two-level namespace modules of its dependent libraries. only used when MH_PREBINDABLE and MH_TWOLEVEL are both set.
260pub const MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000;/// safe to divide up the sections into sub-sections via symbols for dead code stripping
261pub const MH_CANONICAL = 0x4000; /// the binary has been canonicalized via the unprebind operation
262pub const MH_WEAK_DEFINES = 0x8000; /// the final linked image contains external weak symbols
263pub const MH_BINDS_TO_WEAK = 0x10000; /// the final linked image uses weak symbols
264
265pub const MH_ALLOW_STACK_EXECUTION = 0x20000;/// When this bit is set, all stacks in the task will be given stack execution privilege. Only used in MH_EXECUTE filetypes.
266pub const MH_ROOT_SAFE = 0x40000; /// When this bit is set, the binary declares it is safe for use in processes with uid zero
267
268pub const MH_SETUID_SAFE = 0x80000; /// When this bit is set, the binary declares it is safe for use in processes when issetugid() is true
269
270pub const MH_NO_REEXPORTED_DYLIBS = 0x100000; /// When this bit is set on a dylib, the static linker does not need to examine dependent dylibs to see if any are re-exported
271pub const MH_PIE = 0x200000; /// When this bit is set, the OS will load the main executable at a random address. Only used in MH_EXECUTE filetypes.
272pub const MH_DEAD_STRIPPABLE_DYLIB = 0x400000; /// Only for use on dylibs. When linking against a dylib that has this bit set, the static linker will automatically not create a LC_LOAD_DYLIB load command to the dylib if no symbols are being referenced from the dylib.
273pub const MH_HAS_TLV_DESCRIPTORS = 0x800000; /// Contains a section of type S_THREAD_LOCAL_VARIABLES
274
275pub const MH_NO_HEAP_EXECUTION = 0x1000000; /// When this bit is set, the OS will run the main executable with a non-executable heap even on platforms (e.g. i386) that don't require it. Only used in MH_EXECUTE filetypes.
276
277pub const MH_APP_EXTENSION_SAFE = 0x02000000; /// The code was linked for use in an application extension.
278
279pub const MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000; /// The external symbols listed in the nlist symbol table do not include all the symbols listed in the dyld info.
280
281
282/// The flags field of a section structure is separated into two parts a section
283/// type and section attributes. The section types are mutually exclusive (it
284/// can only have one type) but the section attributes are not (it may have more
285/// than one attribute).
286/// 256 section types
287pub const SECTION_TYPE = 0x000000ff;
288pub const SECTION_ATTRIBUTES = 0xffffff00; /// 24 section attributes
289
290pub const S_REGULAR = 0x0; /// regular section
291pub const S_ZEROFILL = 0x1; /// zero fill on demand section
292pub const S_CSTRING_LITERALS = 0x2; /// section with only literal C string
293pub const S_4BYTE_LITERALS = 0x3; /// section with only 4 byte literals
294pub const S_8BYTE_LITERALS = 0x4; /// section with only 8 byte literals
295pub const S_LITERAL_POINTERS = 0x5; /// section with only pointers to
296
297
298pub const N_STAB = 0xe0; /// if any of these bits set, a symbolic debugging entry
299pub const N_PEXT = 0x10; /// private external symbol bit
300pub const N_TYPE = 0x0e; /// mask for the type bits
301pub const N_EXT = 0x01; /// external symbol bit, set for external symbols
302
303
304pub const N_GSYM = 0x20; /// global symbol: name,,NO_SECT,type,0
305pub const N_FNAME = 0x22; /// procedure name (f77 kludge): name,,NO_SECT,0,0
306pub const N_FUN = 0x24; /// procedure: name,,n_sect,linenumber,address
307pub const N_STSYM = 0x26; /// static symbol: name,,n_sect,type,address
308pub const N_LCSYM = 0x28; /// .lcomm symbol: name,,n_sect,type,address
309pub const N_BNSYM = 0x2e; /// begin nsect sym: 0,,n_sect,0,address
310pub const N_AST = 0x32; /// AST file path: name,,NO_SECT,0,0
311pub const N_OPT = 0x3c; /// emitted with gcc2_compiled and in gcc source
312pub const N_RSYM = 0x40; /// register sym: name,,NO_SECT,type,register
313pub const N_SLINE = 0x44; /// src line: 0,,n_sect,linenumber,address
314pub const N_ENSYM = 0x4e; /// end nsect sym: 0,,n_sect,0,address
315pub const N_SSYM = 0x60; /// structure elt: name,,NO_SECT,type,struct_offset
316pub const N_SO = 0x64; /// source file name: name,,n_sect,0,address
317pub const N_OSO = 0x66; /// object file name: name,,0,0,st_mtime
318pub const N_LSYM = 0x80; /// local sym: name,,NO_SECT,type,offset
319pub const N_BINCL = 0x82; /// include file beginning: name,,NO_SECT,0,sum
320pub const N_SOL = 0x84; /// #included file name: name,,n_sect,0,address
321pub const N_PARAMS = 0x86; /// compiler parameters: name,,NO_SECT,0,0
322pub const N_VERSION = 0x88; /// compiler version: name,,NO_SECT,0,0
323pub const N_OLEVEL = 0x8A; /// compiler -O level: name,,NO_SECT,0,0
324pub const N_PSYM = 0xa0; /// parameter: name,,NO_SECT,type,offset
325pub const N_EINCL = 0xa2; /// include file end: name,,NO_SECT,0,0
326pub const N_ENTRY = 0xa4; /// alternate entry: name,,n_sect,linenumber,address
327pub const N_LBRAC = 0xc0; /// left bracket: 0,,NO_SECT,nesting level,address
328pub const N_EXCL = 0xc2; /// deleted include file: name,,NO_SECT,0,sum
329pub const N_RBRAC = 0xe0; /// right bracket: 0,,NO_SECT,nesting level,address
330pub const N_BCOMM = 0xe2; /// begin common: name,,NO_SECT,0,0
331pub const N_ECOMM = 0xe4; /// end common: name,,n_sect,0,0
332pub const N_ECOML = 0xe8; /// end common (local name): 0,,n_sect,0,address
333pub const N_LENG = 0xfe; /// second stab entry with length information
334
335/// If a segment contains any sections marked with S_ATTR_DEBUG then all
336/// sections in that segment must have this attribute. No section other than
337/// a section marked with this attribute may reference the contents of this
338/// section. A section with this attribute may contain no symbols and must have
339/// a section type S_REGULAR. The static linker will not copy section contents
340/// from sections with this attribute into its output file. These sections
341/// generally contain DWARF debugging info.
342pub const S_ATTR_DEBUG = 0x02000000; /// a debug section
343
344pub const cpu_type_t = integer_t;
345pub const cpu_subtype_t = integer_t;
346pub const integer_t = c_int;
347pub const vm_prot_t = c_int;
85348
86pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable {
87 var file = in.file;
88 try file.seekTo(0);
89
90 var hdr: MachHeader64 = undefined;
91 try readOneNoEof(in, MachHeader64, &hdr);
92 if (hdr.magic != MH_MAGIC_64) return error.MissingDebugInfo;
93 const is_pie = MH_PIE == (hdr.flags & MH_PIE);
94
95 var pos: usize = @sizeOf(@typeOf(hdr));
96 var ncmd: u32 = hdr.ncmds;
97 while (ncmd != 0) : (ncmd -= 1) {
98 try file.seekTo(pos);
99 var lc: LoadCommand = undefined;
100 try readOneNoEof(in, LoadCommand, &lc);
101 if (lc.cmd == LC_SYMTAB) break;
102 pos += lc.cmdsize;
103 } else {
104 return error.MissingDebugInfo;
105 }
106
107 var cmd: SymtabCommand = undefined;
108 try readOneNoEof(in, SymtabCommand, &cmd);
109
110 try file.seekTo(cmd.symoff);
111 var syms = try allocator.alloc(Nlist64, cmd.nsyms);
112 defer allocator.free(syms);
113 try readNoEof(in, Nlist64, syms);
114
115 try file.seekTo(cmd.stroff);
116 var strings = try allocator.alloc(u8, cmd.strsize);
117 errdefer allocator.free(strings);
118 try in.stream.readNoEof(strings);
119
120 var nsyms: usize = 0;
121 for (syms) |sym|
122 if (isSymbol(sym)) nsyms += 1;
123 if (nsyms == 0) return error.MissingDebugInfo;
124
125 var symbols = try allocator.alloc(Symbol, nsyms + 1); // Room for sentinel.
126 errdefer allocator.free(symbols);
127
128 var pie_slide: usize = 0;
129 var nsym: usize = 0;
130 for (syms) |sym| {
131 if (!isSymbol(sym)) continue;
132 const start = sym.n_strx;
133 const end = mem.indexOfScalarPos(u8, strings, start, 0).?;
134 const name = strings[start..end];
135 const address = sym.n_value;
136 symbols[nsym] = Symbol{ .name = name, .address = address };
137 nsym += 1;
138 if (is_pie and mem.eql(u8, name, "_SymbolTable_deinit")) {
139 pie_slide = @ptrToInt(SymbolTable.deinit) - address;
140 }
141 }
142
143 // Effectively a no-op, lld emits symbols in ascending order.
144 std.sort.sort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);
145
146 // Insert the sentinel. Since we don't know where the last function ends,
147 // we arbitrarily limit it to the start address + 4 KB.
148 const top = symbols[nsyms - 1].address + 4096;
149 symbols[nsyms] = Symbol{ .name = "", .address = top };
150
151 if (pie_slide != 0) {
152 for (symbols) |*symbol|
153 symbol.address += pie_slide;
154 }
155
156 return SymbolTable{
157 .allocator = allocator,
158 .symbols = symbols,
159 .strings = strings,
160 };
161}
162
163fn readNoEof(in: *io.FileInStream, comptime T: type, result: []T) !void {
164 return in.stream.readNoEof(@sliceToBytes(result));
165}
166fn readOneNoEof(in: *io.FileInStream, comptime T: type, result: *T) !void {
167 return readNoEof(in, T, (*[1]T)(result)[0..]);
168}
169
170fn isSymbol(sym: *const Nlist64) bool {
171 return sym.n_value != 0 and sym.n_desc == 0;
172}
std/math/ceil.zig-2
......@@ -61,10 +61,8 @@ fn ceil64(x: f64) f64 {
6161 }
6262
6363 if (u >> 63 != 0) {
64 @setFloatMode(this, builtin.FloatMode.Strict);
6564 y = x - math.f64_toint + math.f64_toint - x;
6665 } else {
67 @setFloatMode(this, builtin.FloatMode.Strict);
6866 y = x + math.f64_toint - math.f64_toint - x;
6967 }
7068
std/math/complex/exp.zig-2
......@@ -17,8 +17,6 @@ pub fn exp(z: var) @typeOf(z) {
1717}
1818
1919fn exp32(z: Complex(f32)) Complex(f32) {
20 @setFloatMode(this, @import("builtin").FloatMode.Strict);
21
2220 const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955
2321 const cexp_overflow = 0x43400074; // (max_exp - min_denom_exp) * ln2
2422
std/math/cos.zig-2
......@@ -37,8 +37,6 @@ const C5 = 4.16666666666665929218E-2;
3737//
3838// This may have slight differences on some edge cases and may need to replaced if so.
3939fn cos32(x_: f32) f32 {
40 @setFloatMode(this, @import("builtin").FloatMode.Strict);
41
4240 const pi4a = 7.85398125648498535156e-1;
4341 const pi4b = 3.77489470793079817668E-8;
4442 const pi4c = 2.69515142907905952645E-15;
std/math/exp.zig-4
......@@ -18,8 +18,6 @@ pub fn exp(x: var) @typeOf(x) {
1818}
1919
2020fn exp32(x_: f32) f32 {
21 @setFloatMode(this, builtin.FloatMode.Strict);
22
2321 const half = []f32{ 0.5, -0.5 };
2422 const ln2hi = 6.9314575195e-1;
2523 const ln2lo = 1.4286067653e-6;
......@@ -95,8 +93,6 @@ fn exp32(x_: f32) f32 {
9593}
9694
9795fn exp64(x_: f64) f64 {
98 @setFloatMode(this, builtin.FloatMode.Strict);
99
10096 const half = []const f64{ 0.5, -0.5 };
10197 const ln2hi: f64 = 6.93147180369123816490e-01;
10298 const ln2lo: f64 = 1.90821492927058770002e-10;
std/math/exp2.zig-4
......@@ -36,8 +36,6 @@ const exp2ft = []const f64{
3636};
3737
3838fn exp2_32(x: f32) f32 {
39 @setFloatMode(this, @import("builtin").FloatMode.Strict);
40
4139 const tblsiz = @intCast(u32, exp2ft.len);
4240 const redux: f32 = 0x1.8p23 / @intToFloat(f32, tblsiz);
4341 const P1: f32 = 0x1.62e430p-1;
......@@ -353,8 +351,6 @@ const exp2dt = []f64{
353351};
354352
355353fn exp2_64(x: f64) f64 {
356 @setFloatMode(this, @import("builtin").FloatMode.Strict);
357
358354 const tblsiz = @intCast(u32, exp2dt.len / 2);
359355 const redux: f64 = 0x1.8p52 / @intToFloat(f64, tblsiz);
360356 const P1: f64 = 0x1.62e42fefa39efp-1;
std/math/expm1.zig-4
......@@ -19,8 +19,6 @@ pub fn expm1(x: var) @typeOf(x) {
1919}
2020
2121fn expm1_32(x_: f32) f32 {
22 @setFloatMode(this, builtin.FloatMode.Strict);
23
2422 if (math.isNan(x_))
2523 return math.nan(f32);
2624
......@@ -149,8 +147,6 @@ fn expm1_32(x_: f32) f32 {
149147}
150148
151149fn expm1_64(x_: f64) f64 {
152 @setFloatMode(this, builtin.FloatMode.Strict);
153
154150 if (math.isNan(x_))
155151 return math.nan(f64);
156152
std/math/floor.zig-2
......@@ -97,10 +97,8 @@ fn floor64(x: f64) f64 {
9797 }
9898
9999 if (u >> 63 != 0) {
100 @setFloatMode(this, builtin.FloatMode.Strict);
101100 y = x - math.f64_toint + math.f64_toint - x;
102101 } else {
103 @setFloatMode(this, builtin.FloatMode.Strict);
104102 y = x + math.f64_toint - math.f64_toint - x;
105103 }
106104
std/math/ln.zig-4
......@@ -35,8 +35,6 @@ pub fn ln(x: var) @typeOf(x) {
3535}
3636
3737pub fn ln_32(x_: f32) f32 {
38 @setFloatMode(this, @import("builtin").FloatMode.Strict);
39
4038 const ln2_hi: f32 = 6.9313812256e-01;
4139 const ln2_lo: f32 = 9.0580006145e-06;
4240 const Lg1: f32 = 0xaaaaaa.0p-24;
......@@ -89,8 +87,6 @@ pub fn ln_32(x_: f32) f32 {
8987}
9088
9189pub fn ln_64(x_: f64) f64 {
92 @setFloatMode(this, @import("builtin").FloatMode.Strict);
93
9490 const ln2_hi: f64 = 6.93147180369123816490e-01;
9591 const ln2_lo: f64 = 1.90821492927058770002e-10;
9692 const Lg1: f64 = 6.666666666666735130e-01;
std/math/pow.zig-2
......@@ -28,8 +28,6 @@ const assert = std.debug.assert;
2828
2929// This implementation is taken from the go stlib, musl is a bit more complex.
3030pub fn pow(comptime T: type, x: T, y: T) T {
31 @setFloatMode(this, @import("builtin").FloatMode.Strict);
32
3331 if (T != f32 and T != f64) {
3432 @compileError("pow not implemented for " ++ @typeName(T));
3533 }
std/math/round.zig+2-10
......@@ -35,11 +35,7 @@ fn round32(x_: f32) f32 {
3535 return 0 * @bitCast(f32, u);
3636 }
3737
38 {
39 @setFloatMode(this, builtin.FloatMode.Strict);
40 y = x + math.f32_toint - math.f32_toint - x;
41 }
42
38 y = x + math.f32_toint - math.f32_toint - x;
4339 if (y > 0.5) {
4440 y = y + x - 1;
4541 } else if (y <= -0.5) {
......@@ -72,11 +68,7 @@ fn round64(x_: f64) f64 {
7268 return 0 * @bitCast(f64, u);
7369 }
7470
75 {
76 @setFloatMode(this, builtin.FloatMode.Strict);
77 y = x + math.f64_toint - math.f64_toint - x;
78 }
79
71 y = x + math.f64_toint - math.f64_toint - x;
8072 if (y > 0.5) {
8173 y = y + x - 1;
8274 } else if (y <= -0.5) {
std/math/sin.zig-2
......@@ -38,8 +38,6 @@ const C5 = 4.16666666666665929218E-2;
3838//
3939// This may have slight differences on some edge cases and may need to replaced if so.
4040fn sin32(x_: f32) f32 {
41 @setFloatMode(this, @import("builtin").FloatMode.Strict);
42
4341 const pi4a = 7.85398125648498535156e-1;
4442 const pi4b = 3.77489470793079817668E-8;
4543 const pi4c = 2.69515142907905952645E-15;
std/math/sinh.zig-2
......@@ -54,8 +54,6 @@ fn sinh32(x: f32) f32 {
5454}
5555
5656fn sinh64(x: f64) f64 {
57 @setFloatMode(this, @import("builtin").FloatMode.Strict);
58
5957 const u = @bitCast(u64, x);
6058 const w = @intCast(u32, u >> 32);
6159 const ax = @bitCast(f64, u & (@maxValue(u64) >> 1));
std/math/tan.zig-2
......@@ -31,8 +31,6 @@ const Tq4 = -5.38695755929454629881E7;
3131//
3232// This may have slight differences on some edge cases and may need to replaced if so.
3333fn tan32(x_: f32) f32 {
34 @setFloatMode(this, @import("builtin").FloatMode.Strict);
35
3634 const pi4a = 7.85398125648498535156e-1;
3735 const pi4b = 3.77489470793079817668E-8;
3836 const pi4c = 2.69515142907905952645E-15;
std/mem.zig+116-7
......@@ -135,6 +135,12 @@ pub const Allocator = struct {
135135 }
136136};
137137
138pub const Compare = enum {
139 LessThan,
140 Equal,
141 GreaterThan,
142};
143
138144/// Copy all of source into dest at position 0.
139145/// dest.len must be >= source.len.
140146/// dest.ptr must be <= src.ptr.
......@@ -169,16 +175,64 @@ pub fn set(comptime T: type, dest: []T, value: T) void {
169175 d.* = value;
170176}
171177
172/// Returns true if lhs < rhs, false otherwise
173pub fn lessThan(comptime T: type, lhs: []const T, rhs: []const T) bool {
178pub fn secureZero(comptime T: type, s: []T) void {
179 // NOTE: We do not use a volatile slice cast here since LLVM cannot
180 // see that it can be replaced by a memset.
181 const ptr = @ptrCast([*]volatile u8, s.ptr);
182 const length = s.len * @sizeOf(T);
183 @memset(ptr, 0, length);
184}
185
186test "mem.secureZero" {
187 var a = []u8{0xfe} ** 8;
188 var b = []u8{0xfe} ** 8;
189
190 set(u8, a[0..], 0);
191 secureZero(u8, b[0..]);
192
193 assert(eql(u8, a[0..], b[0..]));
194}
195
196pub fn compare(comptime T: type, lhs: []const T, rhs: []const T) Compare {
174197 const n = math.min(lhs.len, rhs.len);
175198 var i: usize = 0;
176199 while (i < n) : (i += 1) {
177 if (lhs[i] == rhs[i]) continue;
178 return lhs[i] < rhs[i];
200 if (lhs[i] == rhs[i]) {
201 continue;
202 } else if (lhs[i] < rhs[i]) {
203 return Compare.LessThan;
204 } else if (lhs[i] > rhs[i]) {
205 return Compare.GreaterThan;
206 } else {
207 unreachable;
208 }
179209 }
180210
181 return lhs.len < rhs.len;
211 if (lhs.len == rhs.len) {
212 return Compare.Equal;
213 } else if (lhs.len < rhs.len) {
214 return Compare.LessThan;
215 } else if (lhs.len > rhs.len) {
216 return Compare.GreaterThan;
217 }
218 unreachable;
219}
220
221test "mem.compare" {
222 assert(compare(u8, "abcd", "bee") == Compare.LessThan);
223 assert(compare(u8, "abc", "abc") == Compare.Equal);
224 assert(compare(u8, "abc", "abc0") == Compare.LessThan);
225 assert(compare(u8, "", "") == Compare.Equal);
226 assert(compare(u8, "", "a") == Compare.LessThan);
227}
228
229/// Returns true if lhs < rhs, false otherwise
230pub fn lessThan(comptime T: type, lhs: []const T, rhs: []const T) bool {
231 var result = compare(T, lhs, rhs);
232 if (result == Compare.LessThan) {
233 return true;
234 } else
235 return false;
182236}
183237
184238test "mem.lessThan" {
......@@ -198,6 +252,20 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
198252 return true;
199253}
200254
255pub fn len(comptime T: type, ptr: [*]const T) usize {
256 var count: usize = 0;
257 while (ptr[count] != 0) : (count += 1) {}
258 return count;
259}
260
261pub fn toSliceConst(comptime T: type, ptr: [*]const T) []const T {
262 return ptr[0..len(T, ptr)];
263}
264
265pub fn toSlice(comptime T: type, ptr: [*]T) []T {
266 return ptr[0..len(T, ptr)];
267}
268
201269/// Returns true if all elements in a slice are equal to the scalar value provided
202270pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool {
203271 for (slice) |item| {
......@@ -541,7 +609,7 @@ pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {
541609 }
542610 }
543611
544 return buf[0..buf_index];
612 return allocator.shrink(u8, buf, buf_index);
545613}
546614
547615test "mem.join" {
......@@ -611,10 +679,38 @@ test "testWriteInt" {
611679 comptime testWriteIntImpl();
612680}
613681fn testWriteIntImpl() void {
614 var bytes: [4]u8 = undefined;
682 var bytes: [8]u8 = undefined;
683
684 writeInt(bytes[0..], u64(0x12345678CAFEBABE), builtin.Endian.Big);
685 assert(eql(u8, bytes, []u8{
686 0x12,
687 0x34,
688 0x56,
689 0x78,
690 0xCA,
691 0xFE,
692 0xBA,
693 0xBE,
694 }));
695
696 writeInt(bytes[0..], u64(0xBEBAFECA78563412), builtin.Endian.Little);
697 assert(eql(u8, bytes, []u8{
698 0x12,
699 0x34,
700 0x56,
701 0x78,
702 0xCA,
703 0xFE,
704 0xBA,
705 0xBE,
706 }));
615707
616708 writeInt(bytes[0..], u32(0x12345678), builtin.Endian.Big);
617709 assert(eql(u8, bytes, []u8{
710 0x00,
711 0x00,
712 0x00,
713 0x00,
618714 0x12,
619715 0x34,
620716 0x56,
......@@ -627,10 +723,18 @@ fn testWriteIntImpl() void {
627723 0x34,
628724 0x56,
629725 0x78,
726 0x00,
727 0x00,
728 0x00,
729 0x00,
630730 }));
631731
632732 writeInt(bytes[0..], u16(0x1234), builtin.Endian.Big);
633733 assert(eql(u8, bytes, []u8{
734 0x00,
735 0x00,
736 0x00,
737 0x00,
634738 0x00,
635739 0x00,
636740 0x12,
......@@ -643,6 +747,10 @@ fn testWriteIntImpl() void {
643747 0x12,
644748 0x00,
645749 0x00,
750 0x00,
751 0x00,
752 0x00,
753 0x00,
646754 }));
647755}
648756
......@@ -755,3 +863,4 @@ pub fn endianSwap(comptime T: type, x: T) T {
755863test "std.mem.endianSwap" {
756864 assert(endianSwap(u32, 0xDEADBEEF) == 0xEFBEADDE);
757865}
866
std/mutex.zig created+27
......@@ -0,0 +1,27 @@
1const std = @import("index.zig");
2const builtin = @import("builtin");
3const AtomicOrder = builtin.AtomicOrder;
4const AtomicRmwOp = builtin.AtomicRmwOp;
5const assert = std.debug.assert;
6
7/// TODO use syscalls instead of a spinlock
8pub const Mutex = struct {
9 lock: u8, // TODO use a bool
10
11 pub const Held = struct {
12 mutex: *Mutex,
13
14 pub fn release(self: Held) void {
15 assert(@atomicRmw(u8, &self.mutex.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);
16 }
17 };
18
19 pub fn init() Mutex {
20 return Mutex{ .lock = 0 };
21 }
22
23 pub fn acquire(self: *Mutex) Held {
24 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}
25 return Held{ .mutex = self };
26 }
27};
std/os/child_process.zig+2-12
......@@ -349,14 +349,7 @@ pub const ChildProcess = struct {
349349 };
350350
351351 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
352 const dev_null_fd = if (any_ignore) blk: {
353 const dev_null_path = "/dev/null";
354 var fixed_buffer_mem: [dev_null_path.len + 1]u8 = undefined;
355 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
356 break :blk try os.posixOpen(&fixed_allocator.allocator, "/dev/null", posix.O_RDWR, 0);
357 } else blk: {
358 break :blk undefined;
359 };
352 const dev_null_fd = if (any_ignore) try os.posixOpenC(c"/dev/null", posix.O_RDWR, 0) else undefined;
360353 defer {
361354 if (any_ignore) os.close(dev_null_fd);
362355 }
......@@ -453,10 +446,7 @@ pub const ChildProcess = struct {
453446 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
454447
455448 const nul_handle = if (any_ignore) blk: {
456 const nul_file_path = "NUL";
457 var fixed_buffer_mem: [nul_file_path.len + 1]u8 = undefined;
458 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
459 break :blk try os.windowsOpen(&fixed_allocator.allocator, "NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL);
449 break :blk try os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL);
460450 } else blk: {
461451 break :blk undefined;
462452 };
std/os/darwin.zig+124-85
......@@ -482,91 +482,98 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;
482482/// data is mach absolute time units
483483pub const NOTE_MACHTIME = 0x00000100;
484484
485pub const AF_UNSPEC: c_int = 0;
486pub const AF_LOCAL: c_int = 1;
487pub const AF_UNIX: c_int = AF_LOCAL;
488pub const AF_INET: c_int = 2;
489pub const AF_SYS_CONTROL: c_int = 2;
490pub const AF_IMPLINK: c_int = 3;
491pub const AF_PUP: c_int = 4;
492pub const AF_CHAOS: c_int = 5;
493pub const AF_NS: c_int = 6;
494pub const AF_ISO: c_int = 7;
495pub const AF_OSI: c_int = AF_ISO;
496pub const AF_ECMA: c_int = 8;
497pub const AF_DATAKIT: c_int = 9;
498pub const AF_CCITT: c_int = 10;
499pub const AF_SNA: c_int = 11;
500pub const AF_DECnet: c_int = 12;
501pub const AF_DLI: c_int = 13;
502pub const AF_LAT: c_int = 14;
503pub const AF_HYLINK: c_int = 15;
504pub const AF_APPLETALK: c_int = 16;
505pub const AF_ROUTE: c_int = 17;
506pub const AF_LINK: c_int = 18;
507pub const AF_XTP: c_int = 19;
508pub const AF_COIP: c_int = 20;
509pub const AF_CNT: c_int = 21;
510pub const AF_RTIP: c_int = 22;
511pub const AF_IPX: c_int = 23;
512pub const AF_SIP: c_int = 24;
513pub const AF_PIP: c_int = 25;
514pub const AF_ISDN: c_int = 28;
515pub const AF_E164: c_int = AF_ISDN;
516pub const AF_KEY: c_int = 29;
517pub const AF_INET6: c_int = 30;
518pub const AF_NATM: c_int = 31;
519pub const AF_SYSTEM: c_int = 32;
520pub const AF_NETBIOS: c_int = 33;
521pub const AF_PPP: c_int = 34;
522pub const AF_MAX: c_int = 40;
523
524pub const PF_UNSPEC: c_int = AF_UNSPEC;
525pub const PF_LOCAL: c_int = AF_LOCAL;
526pub const PF_UNIX: c_int = PF_LOCAL;
527pub const PF_INET: c_int = AF_INET;
528pub const PF_IMPLINK: c_int = AF_IMPLINK;
529pub const PF_PUP: c_int = AF_PUP;
530pub const PF_CHAOS: c_int = AF_CHAOS;
531pub const PF_NS: c_int = AF_NS;
532pub const PF_ISO: c_int = AF_ISO;
533pub const PF_OSI: c_int = AF_ISO;
534pub const PF_ECMA: c_int = AF_ECMA;
535pub const PF_DATAKIT: c_int = AF_DATAKIT;
536pub const PF_CCITT: c_int = AF_CCITT;
537pub const PF_SNA: c_int = AF_SNA;
538pub const PF_DECnet: c_int = AF_DECnet;
539pub const PF_DLI: c_int = AF_DLI;
540pub const PF_LAT: c_int = AF_LAT;
541pub const PF_HYLINK: c_int = AF_HYLINK;
542pub const PF_APPLETALK: c_int = AF_APPLETALK;
543pub const PF_ROUTE: c_int = AF_ROUTE;
544pub const PF_LINK: c_int = AF_LINK;
545pub const PF_XTP: c_int = AF_XTP;
546pub const PF_COIP: c_int = AF_COIP;
547pub const PF_CNT: c_int = AF_CNT;
548pub const PF_SIP: c_int = AF_SIP;
549pub const PF_IPX: c_int = AF_IPX;
550pub const PF_RTIP: c_int = AF_RTIP;
551pub const PF_PIP: c_int = AF_PIP;
552pub const PF_ISDN: c_int = AF_ISDN;
553pub const PF_KEY: c_int = AF_KEY;
554pub const PF_INET6: c_int = AF_INET6;
555pub const PF_NATM: c_int = AF_NATM;
556pub const PF_SYSTEM: c_int = AF_SYSTEM;
557pub const PF_NETBIOS: c_int = AF_NETBIOS;
558pub const PF_PPP: c_int = AF_PPP;
559pub const PF_MAX: c_int = AF_MAX;
560
561pub const SYSPROTO_EVENT: c_int = 1;
562pub const SYSPROTO_CONTROL: c_int = 2;
563
564pub const SOCK_STREAM: c_int = 1;
565pub const SOCK_DGRAM: c_int = 2;
566pub const SOCK_RAW: c_int = 3;
567pub const SOCK_RDM: c_int = 4;
568pub const SOCK_SEQPACKET: c_int = 5;
569pub const SOCK_MAXADDRLEN: c_int = 255;
485pub const AF_UNSPEC = 0;
486pub const AF_LOCAL = 1;
487pub const AF_UNIX = AF_LOCAL;
488pub const AF_INET = 2;
489pub const AF_SYS_CONTROL = 2;
490pub const AF_IMPLINK = 3;
491pub const AF_PUP = 4;
492pub const AF_CHAOS = 5;
493pub const AF_NS = 6;
494pub const AF_ISO = 7;
495pub const AF_OSI = AF_ISO;
496pub const AF_ECMA = 8;
497pub const AF_DATAKIT = 9;
498pub const AF_CCITT = 10;
499pub const AF_SNA = 11;
500pub const AF_DECnet = 12;
501pub const AF_DLI = 13;
502pub const AF_LAT = 14;
503pub const AF_HYLINK = 15;
504pub const AF_APPLETALK = 16;
505pub const AF_ROUTE = 17;
506pub const AF_LINK = 18;
507pub const AF_XTP = 19;
508pub const AF_COIP = 20;
509pub const AF_CNT = 21;
510pub const AF_RTIP = 22;
511pub const AF_IPX = 23;
512pub const AF_SIP = 24;
513pub const AF_PIP = 25;
514pub const AF_ISDN = 28;
515pub const AF_E164 = AF_ISDN;
516pub const AF_KEY = 29;
517pub const AF_INET6 = 30;
518pub const AF_NATM = 31;
519pub const AF_SYSTEM = 32;
520pub const AF_NETBIOS = 33;
521pub const AF_PPP = 34;
522pub const AF_MAX = 40;
523
524pub const PF_UNSPEC = AF_UNSPEC;
525pub const PF_LOCAL = AF_LOCAL;
526pub const PF_UNIX = PF_LOCAL;
527pub const PF_INET = AF_INET;
528pub const PF_IMPLINK = AF_IMPLINK;
529pub const PF_PUP = AF_PUP;
530pub const PF_CHAOS = AF_CHAOS;
531pub const PF_NS = AF_NS;
532pub const PF_ISO = AF_ISO;
533pub const PF_OSI = AF_ISO;
534pub const PF_ECMA = AF_ECMA;
535pub const PF_DATAKIT = AF_DATAKIT;
536pub const PF_CCITT = AF_CCITT;
537pub const PF_SNA = AF_SNA;
538pub const PF_DECnet = AF_DECnet;
539pub const PF_DLI = AF_DLI;
540pub const PF_LAT = AF_LAT;
541pub const PF_HYLINK = AF_HYLINK;
542pub const PF_APPLETALK = AF_APPLETALK;
543pub const PF_ROUTE = AF_ROUTE;
544pub const PF_LINK = AF_LINK;
545pub const PF_XTP = AF_XTP;
546pub const PF_COIP = AF_COIP;
547pub const PF_CNT = AF_CNT;
548pub const PF_SIP = AF_SIP;
549pub const PF_IPX = AF_IPX;
550pub const PF_RTIP = AF_RTIP;
551pub const PF_PIP = AF_PIP;
552pub const PF_ISDN = AF_ISDN;
553pub const PF_KEY = AF_KEY;
554pub const PF_INET6 = AF_INET6;
555pub const PF_NATM = AF_NATM;
556pub const PF_SYSTEM = AF_SYSTEM;
557pub const PF_NETBIOS = AF_NETBIOS;
558pub const PF_PPP = AF_PPP;
559pub const PF_MAX = AF_MAX;
560
561pub const SYSPROTO_EVENT = 1;
562pub const SYSPROTO_CONTROL = 2;
563
564pub const SOCK_STREAM = 1;
565pub const SOCK_DGRAM = 2;
566pub const SOCK_RAW = 3;
567pub const SOCK_RDM = 4;
568pub const SOCK_SEQPACKET = 5;
569pub const SOCK_MAXADDRLEN = 255;
570
571pub const IPPROTO_ICMP = 1;
572pub const IPPROTO_ICMPV6 = 58;
573pub const IPPROTO_TCP = 6;
574pub const IPPROTO_UDP = 17;
575pub const IPPROTO_IP = 0;
576pub const IPPROTO_IPV6 = 41;
570577
571578fn wstatus(x: i32) i32 {
572579 return x & 0o177;
......@@ -605,6 +612,11 @@ pub fn abort() noreturn {
605612 c.abort();
606613}
607614
615// bind(int socket, const struct sockaddr *address, socklen_t address_len)
616pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize {
617 return errnoWrap(c.bind(@bitCast(c_int, fd), addr, len));
618}
619
608620pub fn exit(code: i32) noreturn {
609621 c.exit(code);
610622}
......@@ -634,6 +646,10 @@ pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize {
634646 return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte));
635647}
636648
649pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize {
650 return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset));
651}
652
637653pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {
638654 return errnoWrap(c.stat(path, buf));
639655}
......@@ -642,6 +658,10 @@ pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize {
642658 return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte));
643659}
644660
661pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize {
662 return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset));
663}
664
645665pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
646666 const ptr_result = c.mmap(
647667 @ptrCast(*c_void, address),
......@@ -805,6 +825,20 @@ pub fn sigaction(sig: u5, noalias act: *const Sigaction, noalias oact: ?*Sigacti
805825 return result;
806826}
807827
828pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
829 return errnoWrap(c.socket(@bitCast(c_int, domain), @bitCast(c_int, socket_type), @bitCast(c_int, protocol)));
830}
831
832pub const iovec = extern struct {
833 iov_base: [*]u8,
834 iov_len: usize,
835};
836
837pub const iovec_const = extern struct {
838 iov_base: [*]const u8,
839 iov_len: usize,
840};
841
808842pub const sigset_t = c.sigset_t;
809843pub const empty_sigset = sigset_t(0);
810844
......@@ -812,8 +846,13 @@ pub const timespec = c.timespec;
812846pub const Stat = c.Stat;
813847pub const dirent = c.dirent;
814848
849pub const in_port_t = c.in_port_t;
815850pub const sa_family_t = c.sa_family_t;
851pub const socklen_t = c.socklen_t;
852
816853pub const sockaddr = c.sockaddr;
854pub const sockaddr_in = c.sockaddr_in;
855pub const sockaddr_in6 = c.sockaddr_in6;
817856
818857/// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall.
819858pub const Kevent = c.Kevent;
std/os/file.zig+111-61
......@@ -7,6 +7,7 @@ const assert = std.debug.assert;
77const posix = os.posix;
88const windows = os.windows;
99const Os = builtin.Os;
10const windows_util = @import("windows/util.zig");
1011
1112const is_posix = builtin.os != builtin.Os.windows;
1213const is_windows = builtin.os == builtin.Os.windows;
......@@ -15,18 +16,39 @@ pub const File = struct {
1516 /// The OS-specific file descriptor or file handle.
1617 handle: os.FileHandle,
1718
19 pub const Mode = switch (builtin.os) {
20 Os.windows => void,
21 else => u32,
22 };
23
24 pub const default_mode = switch (builtin.os) {
25 Os.windows => {},
26 else => 0o666,
27 };
28
1829 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;
1930
20 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
21 /// Call close to clean up.
22 pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File {
31 /// `openRead` except with a null terminated path
32 pub fn openReadC(path: [*]const u8) OpenError!File {
2333 if (is_posix) {
2434 const flags = posix.O_LARGEFILE | posix.O_RDONLY;
25 const fd = try os.posixOpen(allocator, path, flags, 0);
35 const fd = try os.posixOpenC(path, flags, 0);
2636 return openHandle(fd);
27 } else if (is_windows) {
37 }
38 if (is_windows) {
39 return openRead(mem.toSliceConst(u8, path));
40 }
41 @compileError("Unsupported OS");
42 }
43
44 /// Call close to clean up.
45 pub fn openRead(path: []const u8) OpenError!File {
46 if (is_posix) {
47 const path_c = try os.toPosixPath(path);
48 return openReadC(&path_c);
49 }
50 if (is_windows) {
2851 const handle = try os.windowsOpen(
29 allocator,
3052 path,
3153 windows.GENERIC_READ,
3254 windows.FILE_SHARE_READ,
......@@ -34,28 +56,25 @@ pub const File = struct {
3456 windows.FILE_ATTRIBUTE_NORMAL,
3557 );
3658 return openHandle(handle);
37 } else {
38 @compileError("TODO implement openRead for this OS");
3959 }
60 @compileError("Unsupported OS");
4061 }
4162
42 /// Calls `openWriteMode` with os.default_file_mode for the mode.
43 pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {
44 return openWriteMode(allocator, path, os.default_file_mode);
63 /// Calls `openWriteMode` with os.File.default_mode for the mode.
64 pub fn openWrite(path: []const u8) OpenError!File {
65 return openWriteMode(path, os.File.default_mode);
4566 }
4667
4768 /// If the path does not exist it will be created.
4869 /// If a file already exists in the destination it will be truncated.
49 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
5070 /// Call close to clean up.
51 pub fn openWriteMode(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {
71 pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File {
5272 if (is_posix) {
5373 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;
54 const fd = try os.posixOpen(allocator, path, flags, file_mode);
74 const fd = try os.posixOpen(path, flags, file_mode);
5575 return openHandle(fd);
5676 } else if (is_windows) {
5777 const handle = try os.windowsOpen(
58 allocator,
5978 path,
6079 windows.GENERIC_WRITE,
6180 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
......@@ -70,16 +89,14 @@ pub const File = struct {
7089
7190 /// If the path does not exist it will be created.
7291 /// If a file already exists in the destination this returns OpenError.PathAlreadyExists
73 /// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
7492 /// Call close to clean up.
75 pub fn openWriteNoClobber(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) OpenError!File {
93 pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File {
7694 if (is_posix) {
7795 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;
78 const fd = try os.posixOpen(allocator, path, flags, file_mode);
96 const fd = try os.posixOpen(path, flags, file_mode);
7997 return openHandle(fd);
8098 } else if (is_windows) {
8199 const handle = try os.windowsOpen(
82 allocator,
83100 path,
84101 windows.GENERIC_WRITE,
85102 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
......@@ -98,23 +115,43 @@ pub const File = struct {
98115
99116 pub const AccessError = error{
100117 PermissionDenied,
101 NotFound,
118 FileNotFound,
102119 NameTooLong,
103 BadMode,
104 BadPathName,
105 Io,
120 InputOutput,
106121 SystemResources,
107 OutOfMemory,
122 BadPathName,
123
124 /// On Windows, file paths must be valid Unicode.
125 InvalidUtf8,
108126
109127 Unexpected,
110128 };
111129
112 pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void {
113 const path_with_null = try std.cstr.addNullByte(allocator, path);
114 defer allocator.free(path_with_null);
130 /// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
131 /// Otherwise use `access` or `accessC`.
132 pub fn accessW(path: [*]const u16) AccessError!void {
133 if (os.windows.GetFileAttributesW(path) != os.windows.INVALID_FILE_ATTRIBUTES) {
134 return;
135 }
136
137 const err = windows.GetLastError();
138 switch (err) {
139 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
140 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
141 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
142 else => return os.unexpectedErrorWindows(err),
143 }
144 }
115145
146 /// Call if you have a UTF-8 encoded, null-terminated string.
147 /// Otherwise use `access` or `accessW`.
148 pub fn accessC(path: [*]const u8) AccessError!void {
149 if (is_windows) {
150 const path_w = try windows_util.cStrToPrefixedFileW(path);
151 return accessW(&path_w);
152 }
116153 if (is_posix) {
117 const result = posix.access(path_with_null.ptr, posix.F_OK);
154 const result = posix.access(path, posix.F_OK);
118155 const err = posix.getErrno(result);
119156 switch (err) {
120157 0 => return,
......@@ -122,32 +159,33 @@ pub const File = struct {
122159 posix.EROFS => return error.PermissionDenied,
123160 posix.ELOOP => return error.PermissionDenied,
124161 posix.ETXTBSY => return error.PermissionDenied,
125 posix.ENOTDIR => return error.NotFound,
126 posix.ENOENT => return error.NotFound,
162 posix.ENOTDIR => return error.FileNotFound,
163 posix.ENOENT => return error.FileNotFound,
127164
128165 posix.ENAMETOOLONG => return error.NameTooLong,
129166 posix.EINVAL => unreachable,
130 posix.EFAULT => return error.BadPathName,
131 posix.EIO => return error.Io,
167 posix.EFAULT => unreachable,
168 posix.EIO => return error.InputOutput,
132169 posix.ENOMEM => return error.SystemResources,
133170 else => return os.unexpectedErrorPosix(err),
134171 }
135 } else if (is_windows) {
136 if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
137 return;
138 }
172 }
173 @compileError("Unsupported OS");
174 }
139175
140 const err = windows.GetLastError();
141 switch (err) {
142 windows.ERROR.FILE_NOT_FOUND,
143 windows.ERROR.PATH_NOT_FOUND,
144 => return error.NotFound,
145 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
146 else => return os.unexpectedErrorWindows(err),
147 }
148 } else {
149 @compileError("TODO implement access for this OS");
176 pub fn access(path: []const u8) AccessError!void {
177 if (is_windows) {
178 const path_w = try windows_util.sliceToPrefixedFileW(path);
179 return accessW(&path_w);
180 }
181 if (is_posix) {
182 var path_with_null: [posix.PATH_MAX]u8 = undefined;
183 if (path.len >= posix.PATH_MAX) return error.NameTooLong;
184 mem.copy(u8, path_with_null[0..], path);
185 path_with_null[path.len] = 0;
186 return accessC(&path_with_null);
150187 }
188 @compileError("Unsupported OS");
151189 }
152190
153191 /// Upon success, the stream is in an uninitialized state. To continue using it,
......@@ -169,7 +207,9 @@ pub const File = struct {
169207 const err = posix.getErrno(result);
170208 if (err > 0) {
171209 return switch (err) {
172 posix.EBADF => error.BadFd,
210 // We do not make this an error code because if you get EBADF it's always a bug,
211 // since the fd could have been reused.
212 posix.EBADF => unreachable,
173213 posix.EINVAL => error.Unseekable,
174214 posix.EOVERFLOW => error.Unseekable,
175215 posix.ESPIPE => error.Unseekable,
......@@ -182,7 +222,7 @@ pub const File = struct {
182222 if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) {
183223 const err = windows.GetLastError();
184224 return switch (err) {
185 windows.ERROR.INVALID_PARAMETER => error.BadFd,
225 windows.ERROR.INVALID_PARAMETER => unreachable,
186226 else => os.unexpectedErrorWindows(err),
187227 };
188228 }
......@@ -199,7 +239,9 @@ pub const File = struct {
199239 const err = posix.getErrno(result);
200240 if (err > 0) {
201241 return switch (err) {
202 posix.EBADF => error.BadFd,
242 // We do not make this an error code because if you get EBADF it's always a bug,
243 // since the fd could have been reused.
244 posix.EBADF => unreachable,
203245 posix.EINVAL => error.Unseekable,
204246 posix.EOVERFLOW => error.Unseekable,
205247 posix.ESPIPE => error.Unseekable,
......@@ -213,7 +255,7 @@ pub const File = struct {
213255 if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) {
214256 const err = windows.GetLastError();
215257 return switch (err) {
216 windows.ERROR.INVALID_PARAMETER => error.BadFd,
258 windows.ERROR.INVALID_PARAMETER => unreachable,
217259 else => os.unexpectedErrorWindows(err),
218260 };
219261 }
......@@ -229,7 +271,9 @@ pub const File = struct {
229271 const err = posix.getErrno(result);
230272 if (err > 0) {
231273 return switch (err) {
232 posix.EBADF => error.BadFd,
274 // We do not make this an error code because if you get EBADF it's always a bug,
275 // since the fd could have been reused.
276 posix.EBADF => unreachable,
233277 posix.EINVAL => error.Unseekable,
234278 posix.EOVERFLOW => error.Unseekable,
235279 posix.ESPIPE => error.Unseekable,
......@@ -244,7 +288,7 @@ pub const File = struct {
244288 if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {
245289 const err = windows.GetLastError();
246290 return switch (err) {
247 windows.ERROR.INVALID_PARAMETER => error.BadFd,
291 windows.ERROR.INVALID_PARAMETER => unreachable,
248292 else => os.unexpectedErrorWindows(err),
249293 };
250294 }
......@@ -277,18 +321,19 @@ pub const File = struct {
277321 }
278322
279323 pub const ModeError = error{
280 BadFd,
281324 SystemResources,
282325 Unexpected,
283326 };
284327
285 pub fn mode(self: *File) ModeError!os.FileMode {
328 pub fn mode(self: *File) ModeError!Mode {
286329 if (is_posix) {
287330 var stat: posix.Stat = undefined;
288331 const err = posix.getErrno(posix.fstat(self.handle, &stat));
289332 if (err > 0) {
290333 return switch (err) {
291 posix.EBADF => error.BadFd,
334 // We do not make this an error code because if you get EBADF it's always a bug,
335 // since the fd could have been reused.
336 posix.EBADF => unreachable,
292337 posix.ENOMEM => error.SystemResources,
293338 else => os.unexpectedErrorPosix(err),
294339 };
......@@ -296,7 +341,7 @@ pub const File = struct {
296341
297342 // TODO: we should be able to cast u16 to ModeError!u32, making this
298343 // explicit cast not necessary
299 return os.FileMode(stat.mode);
344 return Mode(stat.mode);
300345 } else if (is_windows) {
301346 return {};
302347 } else {
......@@ -305,9 +350,11 @@ pub const File = struct {
305350 }
306351
307352 pub const ReadError = error{
308 BadFd,
309 Io,
353 FileClosed,
354 InputOutput,
310355 IsDir,
356 WouldBlock,
357 SystemResources,
311358
312359 Unexpected,
313360 };
......@@ -323,9 +370,12 @@ pub const File = struct {
323370 posix.EINTR => continue,
324371 posix.EINVAL => unreachable,
325372 posix.EFAULT => unreachable,
326 posix.EBADF => return error.BadFd,
327 posix.EIO => return error.Io,
373 posix.EAGAIN => return error.WouldBlock,
374 posix.EBADF => return error.FileClosed,
375 posix.EIO => return error.InputOutput,
328376 posix.EISDIR => return error.IsDir,
377 posix.ENOBUFS => return error.SystemResources,
378 posix.ENOMEM => return error.SystemResources,
329379 else => return os.unexpectedErrorPosix(read_err),
330380 }
331381 }
......@@ -338,7 +388,7 @@ pub const File = struct {
338388 while (index < buffer.len) {
339389 const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));
340390 var amt_read: windows.DWORD = undefined;
341 if (windows.ReadFile(self.handle, @ptrCast(*c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) {
391 if (windows.ReadFile(self.handle, buffer.ptr + index, want_read_count, &amt_read, null) == 0) {
342392 const err = windows.GetLastError();
343393 return switch (err) {
344394 windows.ERROR.OPERATION_ABORTED => continue,
std/os/get_app_data_dir.zig+2-1
......@@ -10,6 +10,7 @@ pub const GetAppDataDirError = error{
1010};
1111
1212/// Caller owns returned memory.
13/// TODO determine if we can remove the allocator requirement
1314pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
1415 switch (builtin.os) {
1516 builtin.Os.windows => {
......@@ -22,7 +23,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
2223 )) {
2324 os.windows.S_OK => {
2425 defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));
25 const global_dir = unicode.utf16leToUtf8(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) {
26 const global_dir = unicode.utf16leToUtf8Alloc(allocator, utf16lePtrSlice(dir_path_ptr)) catch |err| switch (err) {
2627 error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
2728 error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
2829 error.DanglingSurrogateHalf => return error.AppDataDirUnavailable,
std/os/index.zig+544-267
......@@ -38,17 +38,16 @@ pub const path = @import("path.zig");
3838pub const File = @import("file.zig").File;
3939pub const time = @import("time.zig");
4040
41pub const FileMode = switch (builtin.os) {
42 Os.windows => void,
43 else => u32,
44};
45
46pub const default_file_mode = switch (builtin.os) {
47 Os.windows => {},
48 else => 0o666,
49};
50
5141pub const page_size = 4 * 1024;
42pub const MAX_PATH_BYTES = switch (builtin.os) {
43 Os.linux, Os.macosx, Os.ios => posix.PATH_MAX,
44 // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
45 // If it would require 4 UTF-8 bytes, then there would be a surrogate
46 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
47 // +1 for the null byte at the end, which can be encoded in 1 byte.
48 Os.windows => windows_util.PATH_MAX_WIDE * 3 + 1,
49 else => @compileError("Unsupported OS"),
50};
5251
5352pub const UserInfo = @import("get_user_id.zig").UserInfo;
5453pub const getUserInfo = @import("get_user_id.zig").getUserInfo;
......@@ -160,7 +159,7 @@ test "os.getRandomBytes" {
160159 try getRandomBytes(buf_b[0..]);
161160
162161 // Check if random (not 100% conclusive)
163 assert( !mem.eql(u8, buf_a, buf_b) );
162 assert(!mem.eql(u8, buf_a, buf_b));
164163}
165164
166165/// Raises a signal in the current kernel thread, ending its execution.
......@@ -256,6 +255,67 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
256255 }
257256}
258257
258/// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
259pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u64) !usize {
260 switch (builtin.os) {
261 builtin.Os.macosx => {
262 // Darwin does not have preadv but it does have pread.
263 var off: usize = 0;
264 var iov_i: usize = 0;
265 var inner_off: usize = 0;
266 while (true) {
267 const v = iov[iov_i];
268 const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off);
269 const err = darwin.getErrno(rc);
270 switch (err) {
271 0 => {
272 off += rc;
273 inner_off += rc;
274 if (inner_off == v.iov_len) {
275 iov_i += 1;
276 inner_off = 0;
277 if (iov_i == count) {
278 return off;
279 }
280 }
281 if (rc == 0) return off; // EOF
282 continue;
283 },
284 posix.EINTR => continue,
285 posix.EINVAL => unreachable,
286 posix.EFAULT => unreachable,
287 posix.ESPIPE => unreachable, // fd is not seekable
288 posix.EAGAIN => return error.WouldBlock,
289 posix.EBADF => return error.FileClosed,
290 posix.EIO => return error.InputOutput,
291 posix.EISDIR => return error.IsDir,
292 posix.ENOBUFS => return error.SystemResources,
293 posix.ENOMEM => return error.SystemResources,
294 else => return unexpectedErrorPosix(err),
295 }
296 }
297 },
298 builtin.Os.linux, builtin.Os.freebsd => while (true) {
299 const rc = posix.preadv(fd, iov, count, offset);
300 const err = posix.getErrno(rc);
301 switch (err) {
302 0 => return rc,
303 posix.EINTR => continue,
304 posix.EINVAL => unreachable,
305 posix.EFAULT => unreachable,
306 posix.EAGAIN => return error.WouldBlock,
307 posix.EBADF => return error.FileClosed,
308 posix.EIO => return error.InputOutput,
309 posix.EISDIR => return error.IsDir,
310 posix.ENOBUFS => return error.SystemResources,
311 posix.ENOMEM => return error.SystemResources,
312 else => return unexpectedErrorPosix(err),
313 }
314 },
315 else => @compileError("Unsupported OS"),
316 }
317}
318
259319pub const PosixWriteError = error{
260320 WouldBlock,
261321 FileClosed,
......@@ -266,6 +326,8 @@ pub const PosixWriteError = error{
266326 NoSpaceLeft,
267327 AccessDenied,
268328 BrokenPipe,
329
330 /// See https://github.com/ziglang/zig/issues/1396
269331 Unexpected,
270332};
271333
......@@ -300,8 +362,72 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
300362 }
301363}
302364
365pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, offset: u64) PosixWriteError!void {
366 switch (builtin.os) {
367 builtin.Os.macosx => {
368 // Darwin does not have pwritev but it does have pwrite.
369 var off: usize = 0;
370 var iov_i: usize = 0;
371 var inner_off: usize = 0;
372 while (true) {
373 const v = iov[iov_i];
374 const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off);
375 const err = darwin.getErrno(rc);
376 switch (err) {
377 0 => {
378 off += rc;
379 inner_off += rc;
380 if (inner_off == v.iov_len) {
381 iov_i += 1;
382 inner_off = 0;
383 if (iov_i == count) {
384 return;
385 }
386 }
387 continue;
388 },
389 posix.EINTR => continue,
390 posix.ESPIPE => unreachable, // fd is not seekable
391 posix.EINVAL => unreachable,
392 posix.EFAULT => unreachable,
393 posix.EAGAIN => return PosixWriteError.WouldBlock,
394 posix.EBADF => return PosixWriteError.FileClosed,
395 posix.EDESTADDRREQ => return PosixWriteError.DestinationAddressRequired,
396 posix.EDQUOT => return PosixWriteError.DiskQuota,
397 posix.EFBIG => return PosixWriteError.FileTooBig,
398 posix.EIO => return PosixWriteError.InputOutput,
399 posix.ENOSPC => return PosixWriteError.NoSpaceLeft,
400 posix.EPERM => return PosixWriteError.AccessDenied,
401 posix.EPIPE => return PosixWriteError.BrokenPipe,
402 else => return unexpectedErrorPosix(err),
403 }
404 }
405 },
406 builtin.Os.linux => while (true) {
407 const rc = posix.pwritev(fd, iov, count, offset);
408 const err = posix.getErrno(rc);
409 switch (err) {
410 0 => return,
411 posix.EINTR => continue,
412 posix.EINVAL => unreachable,
413 posix.EFAULT => unreachable,
414 posix.EAGAIN => return PosixWriteError.WouldBlock,
415 posix.EBADF => return PosixWriteError.FileClosed,
416 posix.EDESTADDRREQ => return PosixWriteError.DestinationAddressRequired,
417 posix.EDQUOT => return PosixWriteError.DiskQuota,
418 posix.EFBIG => return PosixWriteError.FileTooBig,
419 posix.EIO => return PosixWriteError.InputOutput,
420 posix.ENOSPC => return PosixWriteError.NoSpaceLeft,
421 posix.EPERM => return PosixWriteError.AccessDenied,
422 posix.EPIPE => return PosixWriteError.BrokenPipe,
423 else => return unexpectedErrorPosix(err),
424 }
425 },
426 else => @compileError("Unsupported OS"),
427 }
428}
429
303430pub const PosixOpenError = error{
304 OutOfMemory,
305431 AccessDenied,
306432 FileTooBig,
307433 IsDir,
......@@ -310,22 +436,22 @@ pub const PosixOpenError = error{
310436 NameTooLong,
311437 SystemFdQuotaExceeded,
312438 NoDevice,
313 PathNotFound,
439 FileNotFound,
314440 SystemResources,
315441 NoSpaceLeft,
316442 NotDir,
317443 PathAlreadyExists,
444
445 /// See https://github.com/ziglang/zig/issues/1396
318446 Unexpected,
319447};
320448
321449/// ::file_path needs to be copied in memory to add a null terminating byte.
322450/// Calls POSIX open, keeps trying if it gets interrupted, and translates
323451/// the return value into zig errors.
324pub fn posixOpen(allocator: *Allocator, file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 {
325 const path_with_null = try cstr.addNullByte(allocator, file_path);
326 defer allocator.free(path_with_null);
327
328 return posixOpenC(path_with_null.ptr, flags, perm);
452pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 {
453 const file_path_c = try toPosixPath(file_path);
454 return posixOpenC(&file_path_c, flags, perm);
329455}
330456
331457// TODO https://github.com/ziglang/zig/issues/265
......@@ -347,7 +473,7 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 {
347473 posix.ENAMETOOLONG => return PosixOpenError.NameTooLong,
348474 posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded,
349475 posix.ENODEV => return PosixOpenError.NoDevice,
350 posix.ENOENT => return PosixOpenError.PathNotFound,
476 posix.ENOENT => return PosixOpenError.FileNotFound,
351477 posix.ENOMEM => return PosixOpenError.SystemResources,
352478 posix.ENOSPC => return PosixOpenError.NoSpaceLeft,
353479 posix.ENOTDIR => return PosixOpenError.NotDir,
......@@ -360,6 +486,16 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 {
360486 }
361487}
362488
489/// Used to convert a slice to a null terminated slice on the stack.
490/// TODO well defined copy elision
491pub fn toPosixPath(file_path: []const u8) ![posix.PATH_MAX]u8 {
492 var path_with_null: [posix.PATH_MAX]u8 = undefined;
493 if (file_path.len >= posix.PATH_MAX) return error.NameTooLong;
494 mem.copy(u8, path_with_null[0..], file_path);
495 path_with_null[file_path.len] = 0;
496 return path_with_null;
497}
498
363499pub fn posixDup2(old_fd: i32, new_fd: i32) !void {
364500 while (true) {
365501 const err = posix.getErrno(posix.dup2(old_fd, new_fd));
......@@ -475,6 +611,8 @@ pub const PosixExecveError = error{
475611 FileNotFound,
476612 NotDir,
477613 FileBusy,
614
615 /// See https://github.com/ziglang/zig/issues/1396
478616 Unexpected,
479617};
480618
......@@ -497,6 +635,35 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
497635pub var linux_aux_raw = []usize{0} ** 38;
498636pub var posix_environ_raw: [][*]u8 = undefined;
499637
638/// See std.elf for the constants.
639pub fn linuxGetAuxVal(index: usize) usize {
640 if (builtin.link_libc) {
641 return usize(std.c.getauxval(index));
642 } else {
643 return linux_aux_raw[index];
644 }
645}
646
647pub fn getBaseAddress() usize {
648 switch (builtin.os) {
649 builtin.Os.linux => {
650 const base = linuxGetAuxVal(std.elf.AT_BASE);
651 if (base != 0) {
652 return base;
653 }
654 const phdr = linuxGetAuxVal(std.elf.AT_PHDR);
655 const ElfHeader = switch (@sizeOf(usize)) {
656 4 => std.elf.Elf32_Ehdr,
657 8 => std.elf.Elf64_Ehdr,
658 else => @compileError("Unsupported architecture"),
659 };
660 return phdr - @sizeOf(ElfHeader);
661 },
662 builtin.Os.macosx => return @ptrToInt(&std.c._mh_execute_header),
663 else => @compileError("Unsupported OS"),
664 }
665}
666
500667/// Caller must free result when done.
501668/// TODO make this go through libc when we have it
502669pub fn getEnvMap(allocator: *Allocator) !BufMap {
......@@ -603,43 +770,39 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned
603770}
604771
605772/// Caller must free the returned memory.
606pub fn getCwd(allocator: *Allocator) ![]u8 {
607 switch (builtin.os) {
608 Os.windows => {
609 var buf = try allocator.alloc(u8, 256);
610 errdefer allocator.free(buf);
611
612 while (true) {
613 const result = windows.GetCurrentDirectoryA(@intCast(windows.WORD, buf.len), buf.ptr);
773pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
774 var buf: [MAX_PATH_BYTES]u8 = undefined;
775 return mem.dupe(allocator, u8, try getCwd(&buf));
776}
614777
615 if (result == 0) {
616 const err = windows.GetLastError();
617 return switch (err) {
618 else => unexpectedErrorWindows(err),
619 };
620 }
778pub const GetCwdError = error{Unexpected};
621779
622 if (result > buf.len) {
623 buf = try allocator.realloc(u8, buf, result);
624 continue;
780/// The result is a slice of out_buffer.
781pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 {
782 switch (builtin.os) {
783 Os.windows => {
784 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
785 const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
786 const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast
787 const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr);
788 if (result == 0) {
789 const err = windows.GetLastError();
790 switch (err) {
791 else => return unexpectedErrorWindows(err),
625792 }
626
627 return allocator.shrink(u8, buf, result);
628793 }
794 assert(result <= utf16le_buf.len);
795 const utf16le_slice = utf16le_buf[0..result];
796 // Trust that Windows gives us valid UTF-16LE.
797 const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable;
798 return out_buffer[0..end_index];
629799 },
630800 else => {
631 var buf = try allocator.alloc(u8, 1024);
632 errdefer allocator.free(buf);
633 while (true) {
634 const err = posix.getErrno(posix.getcwd(buf.ptr, buf.len));
635 if (err == posix.ERANGE) {
636 buf = try allocator.realloc(u8, buf, buf.len * 2);
637 continue;
638 } else if (err > 0) {
639 return unexpectedErrorPosix(err);
640 }
641
642 return allocator.shrink(u8, buf, cstr.len(buf.ptr));
801 const err = posix.getErrno(posix.getcwd(out_buffer, out_buffer.len));
802 switch (err) {
803 0 => return cstr.toSlice(out_buffer),
804 posix.ERANGE => unreachable,
805 else => return unexpectedErrorPosix(err),
643806 }
644807 },
645808 }
......@@ -647,7 +810,9 @@ pub fn getCwd(allocator: *Allocator) ![]u8 {
647810
648811test "os.getCwd" {
649812 // at least call it so it gets compiled
650 _ = getCwd(debug.global_allocator);
813 _ = getCwdAlloc(debug.global_allocator);
814 var buf: [MAX_PATH_BYTES]u8 = undefined;
815 _ = getCwd(&buf);
651816}
652817
653818pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;
......@@ -662,6 +827,8 @@ pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []con
662827
663828pub const WindowsSymLinkError = error{
664829 OutOfMemory,
830
831 /// See https://github.com/ziglang/zig/issues/1396
665832 Unexpected,
666833};
667834
......@@ -692,6 +859,8 @@ pub const PosixSymLinkError = error{
692859 NoSpaceLeft,
693860 ReadOnlyFileSystem,
694861 NotDir,
862
863 /// See https://github.com/ziglang/zig/issues/1396
695864 Unexpected,
696865};
697866
......@@ -750,7 +919,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
750919 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
751920
752921 if (symLink(allocator, existing_path, tmp_path)) {
753 return rename(allocator, tmp_path, new_path);
922 return rename(tmp_path, new_path);
754923 } else |err| switch (err) {
755924 error.PathAlreadyExists => continue,
756925 else => return err, // TODO zig should know this set does not include PathAlreadyExists
......@@ -769,70 +938,75 @@ pub const DeleteFileError = error{
769938 NotDir,
770939 SystemResources,
771940 ReadOnlyFileSystem,
772 OutOfMemory,
773941
942 /// On Windows, file paths must be valid Unicode.
943 InvalidUtf8,
944
945 /// On Windows, file paths cannot contain these characters:
946 /// '/', '*', '?', '"', '<', '>', '|'
947 BadPathName,
948
949 /// See https://github.com/ziglang/zig/issues/1396
774950 Unexpected,
775951};
776952
777pub fn deleteFile(allocator: *Allocator, file_path: []const u8) DeleteFileError!void {
953pub fn deleteFile(file_path: []const u8) DeleteFileError!void {
778954 if (builtin.os == Os.windows) {
779 return deleteFileWindows(allocator, file_path);
955 return deleteFileWindows(file_path);
780956 } else {
781 return deleteFilePosix(allocator, file_path);
957 return deleteFilePosix(file_path);
782958 }
783959}
784960
785pub fn deleteFileWindows(allocator: *Allocator, file_path: []const u8) !void {
786 const buf = try allocator.alloc(u8, file_path.len + 1);
787 defer allocator.free(buf);
961pub fn deleteFileWindows(file_path: []const u8) !void {
962 const file_path_w = try windows_util.sliceToPrefixedFileW(file_path);
788963
789 mem.copy(u8, buf, file_path);
790 buf[file_path.len] = 0;
791
792 if (windows.DeleteFileA(buf.ptr) == 0) {
964 if (windows.DeleteFileW(&file_path_w) == 0) {
793965 const err = windows.GetLastError();
794 return switch (err) {
795 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
796 windows.ERROR.ACCESS_DENIED => error.AccessDenied,
797 windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
798 else => unexpectedErrorWindows(err),
799 };
966 switch (err) {
967 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
968 windows.ERROR.ACCESS_DENIED => return error.AccessDenied,
969 windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
970 windows.ERROR.INVALID_PARAMETER => return error.NameTooLong,
971 else => return unexpectedErrorWindows(err),
972 }
800973 }
801974}
802975
803pub fn deleteFilePosix(allocator: *Allocator, file_path: []const u8) !void {
804 const buf = try allocator.alloc(u8, file_path.len + 1);
805 defer allocator.free(buf);
806
807 mem.copy(u8, buf, file_path);
808 buf[file_path.len] = 0;
809
810 const err = posix.getErrno(posix.unlink(buf.ptr));
811 if (err > 0) {
812 return switch (err) {
813 posix.EACCES, posix.EPERM => error.AccessDenied,
814 posix.EBUSY => error.FileBusy,
815 posix.EFAULT, posix.EINVAL => unreachable,
816 posix.EIO => error.FileSystem,
817 posix.EISDIR => error.IsDir,
818 posix.ELOOP => error.SymLinkLoop,
819 posix.ENAMETOOLONG => error.NameTooLong,
820 posix.ENOENT => error.FileNotFound,
821 posix.ENOTDIR => error.NotDir,
822 posix.ENOMEM => error.SystemResources,
823 posix.EROFS => error.ReadOnlyFileSystem,
824 else => unexpectedErrorPosix(err),
825 };
976pub fn deleteFilePosixC(file_path: [*]const u8) !void {
977 const err = posix.getErrno(posix.unlink(file_path));
978 switch (err) {
979 0 => return,
980 posix.EACCES => return error.AccessDenied,
981 posix.EPERM => return error.AccessDenied,
982 posix.EBUSY => return error.FileBusy,
983 posix.EFAULT => unreachable,
984 posix.EINVAL => unreachable,
985 posix.EIO => return error.FileSystem,
986 posix.EISDIR => return error.IsDir,
987 posix.ELOOP => return error.SymLinkLoop,
988 posix.ENAMETOOLONG => return error.NameTooLong,
989 posix.ENOENT => return error.FileNotFound,
990 posix.ENOTDIR => return error.NotDir,
991 posix.ENOMEM => return error.SystemResources,
992 posix.EROFS => return error.ReadOnlyFileSystem,
993 else => return unexpectedErrorPosix(err),
826994 }
827995}
828996
997pub fn deleteFilePosix(file_path: []const u8) !void {
998 const file_path_c = try toPosixPath(file_path);
999 return deleteFilePosixC(&file_path_c);
1000}
1001
8291002/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
8301003/// merged and readily available,
8311004/// there is a possibility of power loss or application termination leaving temporary files present
8321005/// in the same directory as dest_path.
8331006/// Destination file will have the same mode as the source file.
1007/// TODO investigate if this can work with no allocator
8341008pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []const u8) !void {
835 var in_file = try os.File.openRead(allocator, source_path);
1009 var in_file = try os.File.openRead(source_path);
8361010 defer in_file.close();
8371011
8381012 const mode = try in_file.mode();
......@@ -853,8 +1027,9 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con
8531027/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
8541028/// merged and readily available,
8551029/// there is a possibility of power loss or application termination leaving temporary files present
856pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: FileMode) !void {
857 var in_file = try os.File.openRead(allocator, source_path);
1030/// TODO investigate if this can work with no allocator
1031pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
1032 var in_file = try os.File.openRead(source_path);
8581033 defer in_file.close();
8591034
8601035 var atomic_file = try AtomicFile.init(allocator, dest_path, mode);
......@@ -871,6 +1046,7 @@ pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: [
8711046}
8721047
8731048pub const AtomicFile = struct {
1049 /// TODO investigate if we can make this work with no allocator
8741050 allocator: *Allocator,
8751051 file: os.File,
8761052 tmp_path: []u8,
......@@ -879,7 +1055,7 @@ pub const AtomicFile = struct {
8791055
8801056 /// dest_path must remain valid for the lifetime of AtomicFile
8811057 /// call finish to atomically replace dest_path with contents
882 pub fn init(allocator: *Allocator, dest_path: []const u8, mode: FileMode) !AtomicFile {
1058 pub fn init(allocator: *Allocator, dest_path: []const u8, mode: File.Mode) !AtomicFile {
8831059 const dirname = os.path.dirname(dest_path);
8841060
8851061 var rand_buf: [12]u8 = undefined;
......@@ -898,7 +1074,7 @@ pub const AtomicFile = struct {
8981074 try getRandomBytes(rand_buf[0..]);
8991075 b64_fs_encoder.encode(tmp_path[dirname_component_len..], rand_buf);
9001076
901 const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) {
1077 const file = os.File.openWriteNoClobber(tmp_path, mode) catch |err| switch (err) {
9021078 error.PathAlreadyExists => continue,
9031079 // TODO zig should figure out that this error set does not include PathAlreadyExists since
9041080 // it is handled in the above switch
......@@ -919,7 +1095,7 @@ pub const AtomicFile = struct {
9191095 pub fn deinit(self: *AtomicFile) void {
9201096 if (!self.finished) {
9211097 self.file.close();
922 deleteFile(self.allocator, self.tmp_path) catch {};
1098 deleteFile(self.tmp_path) catch {};
9231099 self.allocator.free(self.tmp_path);
9241100 self.finished = true;
9251101 }
......@@ -928,70 +1104,72 @@ pub const AtomicFile = struct {
9281104 pub fn finish(self: *AtomicFile) !void {
9291105 assert(!self.finished);
9301106 self.file.close();
931 try rename(self.allocator, self.tmp_path, self.dest_path);
1107 try rename(self.tmp_path, self.dest_path);
9321108 self.allocator.free(self.tmp_path);
9331109 self.finished = true;
9341110 }
9351111};
9361112
937pub fn rename(allocator: *Allocator, old_path: []const u8, new_path: []const u8) !void {
938 const full_buf = try allocator.alloc(u8, old_path.len + new_path.len + 2);
939 defer allocator.free(full_buf);
940
941 const old_buf = full_buf;
942 mem.copy(u8, old_buf, old_path);
943 old_buf[old_path.len] = 0;
944
945 const new_buf = full_buf[old_path.len + 1 ..];
946 mem.copy(u8, new_buf, new_path);
947 new_buf[new_path.len] = 0;
1113pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void {
1114 if (is_windows) {
1115 @compileError("TODO implement for windows");
1116 } else {
1117 const err = posix.getErrno(posix.rename(old_path, new_path));
1118 switch (err) {
1119 0 => return,
1120 posix.EACCES => return error.AccessDenied,
1121 posix.EPERM => return error.AccessDenied,
1122 posix.EBUSY => return error.FileBusy,
1123 posix.EDQUOT => return error.DiskQuota,
1124 posix.EFAULT => unreachable,
1125 posix.EINVAL => unreachable,
1126 posix.EISDIR => return error.IsDir,
1127 posix.ELOOP => return error.SymLinkLoop,
1128 posix.EMLINK => return error.LinkQuotaExceeded,
1129 posix.ENAMETOOLONG => return error.NameTooLong,
1130 posix.ENOENT => return error.FileNotFound,
1131 posix.ENOTDIR => return error.NotDir,
1132 posix.ENOMEM => return error.SystemResources,
1133 posix.ENOSPC => return error.NoSpaceLeft,
1134 posix.EEXIST => return error.PathAlreadyExists,
1135 posix.ENOTEMPTY => return error.PathAlreadyExists,
1136 posix.EROFS => return error.ReadOnlyFileSystem,
1137 posix.EXDEV => return error.RenameAcrossMountPoints,
1138 else => return unexpectedErrorPosix(err),
1139 }
1140 }
1141}
9481142
1143pub fn rename(old_path: []const u8, new_path: []const u8) !void {
9491144 if (is_windows) {
9501145 const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
951 if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) {
1146 const old_path_w = try windows_util.sliceToPrefixedFileW(old_path);
1147 const new_path_w = try windows_util.sliceToPrefixedFileW(new_path);
1148 if (windows.MoveFileExW(&old_path_w, &new_path_w, flags) == 0) {
9521149 const err = windows.GetLastError();
953 return switch (err) {
954 else => unexpectedErrorWindows(err),
955 };
1150 switch (err) {
1151 else => return unexpectedErrorWindows(err),
1152 }
9561153 }
9571154 } else {
958 const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));
959 if (err > 0) {
960 return switch (err) {
961 posix.EACCES, posix.EPERM => error.AccessDenied,
962 posix.EBUSY => error.FileBusy,
963 posix.EDQUOT => error.DiskQuota,
964 posix.EFAULT, posix.EINVAL => unreachable,
965 posix.EISDIR => error.IsDir,
966 posix.ELOOP => error.SymLinkLoop,
967 posix.EMLINK => error.LinkQuotaExceeded,
968 posix.ENAMETOOLONG => error.NameTooLong,
969 posix.ENOENT => error.FileNotFound,
970 posix.ENOTDIR => error.NotDir,
971 posix.ENOMEM => error.SystemResources,
972 posix.ENOSPC => error.NoSpaceLeft,
973 posix.EEXIST, posix.ENOTEMPTY => error.PathAlreadyExists,
974 posix.EROFS => error.ReadOnlyFileSystem,
975 posix.EXDEV => error.RenameAcrossMountPoints,
976 else => unexpectedErrorPosix(err),
977 };
978 }
1155 const old_path_c = try toPosixPath(old_path);
1156 const new_path_c = try toPosixPath(new_path);
1157 return renameC(&old_path_c, &new_path_c);
9791158 }
9801159}
9811160
982pub fn makeDir(allocator: *Allocator, dir_path: []const u8) !void {
1161pub fn makeDir(dir_path: []const u8) !void {
9831162 if (is_windows) {
984 return makeDirWindows(allocator, dir_path);
1163 return makeDirWindows(dir_path);
9851164 } else {
986 return makeDirPosix(allocator, dir_path);
1165 return makeDirPosix(dir_path);
9871166 }
9881167}
9891168
990pub fn makeDirWindows(allocator: *Allocator, dir_path: []const u8) !void {
991 const path_buf = try cstr.addNullByte(allocator, dir_path);
992 defer allocator.free(path_buf);
1169pub fn makeDirWindows(dir_path: []const u8) !void {
1170 const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path);
9931171
994 if (windows.CreateDirectoryA(path_buf.ptr, null) == 0) {
1172 if (windows.CreateDirectoryW(&dir_path_w, null) == 0) {
9951173 const err = windows.GetLastError();
9961174 return switch (err) {
9971175 windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists,
......@@ -1001,54 +1179,57 @@ pub fn makeDirWindows(allocator: *Allocator, dir_path: []const u8) !void {
10011179 }
10021180}
10031181
1004pub fn makeDirPosix(allocator: *Allocator, dir_path: []const u8) !void {
1005 const path_buf = try cstr.addNullByte(allocator, dir_path);
1006 defer allocator.free(path_buf);
1007
1008 const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));
1009 if (err > 0) {
1010 return switch (err) {
1011 posix.EACCES, posix.EPERM => error.AccessDenied,
1012 posix.EDQUOT => error.DiskQuota,
1013 posix.EEXIST => error.PathAlreadyExists,
1014 posix.EFAULT => unreachable,
1015 posix.ELOOP => error.SymLinkLoop,
1016 posix.EMLINK => error.LinkQuotaExceeded,
1017 posix.ENAMETOOLONG => error.NameTooLong,
1018 posix.ENOENT => error.FileNotFound,
1019 posix.ENOMEM => error.SystemResources,
1020 posix.ENOSPC => error.NoSpaceLeft,
1021 posix.ENOTDIR => error.NotDir,
1022 posix.EROFS => error.ReadOnlyFileSystem,
1023 else => unexpectedErrorPosix(err),
1024 };
1182pub fn makeDirPosixC(dir_path: [*]const u8) !void {
1183 const err = posix.getErrno(posix.mkdir(dir_path, 0o755));
1184 switch (err) {
1185 0 => return,
1186 posix.EACCES => return error.AccessDenied,
1187 posix.EPERM => return error.AccessDenied,
1188 posix.EDQUOT => return error.DiskQuota,
1189 posix.EEXIST => return error.PathAlreadyExists,
1190 posix.EFAULT => unreachable,
1191 posix.ELOOP => return error.SymLinkLoop,
1192 posix.EMLINK => return error.LinkQuotaExceeded,
1193 posix.ENAMETOOLONG => return error.NameTooLong,
1194 posix.ENOENT => return error.FileNotFound,
1195 posix.ENOMEM => return error.SystemResources,
1196 posix.ENOSPC => return error.NoSpaceLeft,
1197 posix.ENOTDIR => return error.NotDir,
1198 posix.EROFS => return error.ReadOnlyFileSystem,
1199 else => return unexpectedErrorPosix(err),
10251200 }
10261201}
10271202
1203pub fn makeDirPosix(dir_path: []const u8) !void {
1204 const dir_path_c = try toPosixPath(dir_path);
1205 return makeDirPosixC(&dir_path_c);
1206}
1207
10281208/// Calls makeDir recursively to make an entire path. Returns success if the path
10291209/// already exists and is a directory.
1210/// TODO determine if we can remove the allocator requirement from this function
10301211pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
10311212 const resolved_path = try path.resolve(allocator, full_path);
10321213 defer allocator.free(resolved_path);
10331214
10341215 var end_index: usize = resolved_path.len;
10351216 while (true) {
1036 makeDir(allocator, resolved_path[0..end_index]) catch |err| {
1037 if (err == error.PathAlreadyExists) {
1217 makeDir(resolved_path[0..end_index]) catch |err| switch (err) {
1218 error.PathAlreadyExists => {
10381219 // TODO stat the file and return an error if it's not a directory
10391220 // this is important because otherwise a dangling symlink
10401221 // could cause an infinite loop
10411222 if (end_index == resolved_path.len) return;
1042 } else if (err == error.FileNotFound) {
1223 },
1224 error.FileNotFound => {
10431225 // march end_index backward until next path component
10441226 while (true) {
10451227 end_index -= 1;
10461228 if (os.path.isSep(resolved_path[end_index])) break;
10471229 }
10481230 continue;
1049 } else {
1050 return err;
1051 }
1231 },
1232 else => return err,
10521233 };
10531234 if (end_index == resolved_path.len) return;
10541235 // march end_index forward until next path component
......@@ -1071,6 +1252,7 @@ pub const DeleteDirError = error{
10711252 ReadOnlyFileSystem,
10721253 OutOfMemory,
10731254
1255 /// See https://github.com/ziglang/zig/issues/1396
10741256 Unexpected,
10751257};
10761258
......@@ -1129,7 +1311,6 @@ const DeleteTreeError = error{
11291311 NameTooLong,
11301312 SystemFdQuotaExceeded,
11311313 NoDevice,
1132 PathNotFound,
11331314 SystemResources,
11341315 NoSpaceLeft,
11351316 PathAlreadyExists,
......@@ -1139,20 +1320,30 @@ const DeleteTreeError = error{
11391320 FileSystem,
11401321 FileBusy,
11411322 DirNotEmpty,
1323
1324 /// On Windows, file paths must be valid Unicode.
1325 InvalidUtf8,
1326
1327 /// On Windows, file paths cannot contain these characters:
1328 /// '/', '*', '?', '"', '<', '>', '|'
1329 BadPathName,
1330
1331 /// See https://github.com/ziglang/zig/issues/1396
11421332 Unexpected,
11431333};
1334
1335/// TODO determine if we can remove the allocator requirement
11441336pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void {
11451337 start_over: while (true) {
11461338 var got_access_denied = false;
11471339 // First, try deleting the item as a file. This way we don't follow sym links.
1148 if (deleteFile(allocator, full_path)) {
1340 if (deleteFile(full_path)) {
11491341 return;
11501342 } else |err| switch (err) {
11511343 error.FileNotFound => return,
11521344 error.IsDir => {},
11531345 error.AccessDenied => got_access_denied = true,
11541346
1155 error.OutOfMemory,
11561347 error.SymLinkLoop,
11571348 error.NameTooLong,
11581349 error.SystemResources,
......@@ -1160,6 +1351,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
11601351 error.NotDir,
11611352 error.FileSystem,
11621353 error.FileBusy,
1354 error.InvalidUtf8,
1355 error.BadPathName,
11631356 error.Unexpected,
11641357 => return err,
11651358 }
......@@ -1181,7 +1374,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
11811374 error.NameTooLong,
11821375 error.SystemFdQuotaExceeded,
11831376 error.NoDevice,
1184 error.PathNotFound,
1377 error.FileNotFound,
11851378 error.SystemResources,
11861379 error.NoSpaceLeft,
11871380 error.PathAlreadyExists,
......@@ -1251,7 +1444,7 @@ pub const Dir = struct {
12511444 };
12521445
12531446 pub const OpenError = error{
1254 PathNotFound,
1447 FileNotFound,
12551448 NotDir,
12561449 AccessDenied,
12571450 FileTooBig,
......@@ -1266,9 +1459,11 @@ pub const Dir = struct {
12661459 PathAlreadyExists,
12671460 OutOfMemory,
12681461
1462 /// See https://github.com/ziglang/zig/issues/1396
12691463 Unexpected,
12701464 };
12711465
1466 /// TODO remove the allocator requirement from this API
12721467 pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
12731468 return Dir{
12741469 .allocator = allocator,
......@@ -1284,7 +1479,6 @@ pub const Dir = struct {
12841479 },
12851480 Os.macosx, Os.ios => Handle{
12861481 .fd = try posixOpen(
1287 allocator,
12881482 dir_path,
12891483 posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,
12901484 0,
......@@ -1296,7 +1490,6 @@ pub const Dir = struct {
12961490 },
12971491 Os.linux => Handle{
12981492 .fd = try posixOpen(
1299 allocator,
13001493 dir_path,
13011494 posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC,
13021495 0,
......@@ -1493,39 +1686,32 @@ pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void {
14931686}
14941687
14951688/// Read value of a symbolic link.
1496pub fn readLink(allocator: *Allocator, pathname: []const u8) ![]u8 {
1497 const path_buf = try allocator.alloc(u8, pathname.len + 1);
1498 defer allocator.free(path_buf);
1499
1500 mem.copy(u8, path_buf, pathname);
1501 path_buf[pathname.len] = 0;
1502
1503 var result_buf = try allocator.alloc(u8, 1024);
1504 errdefer allocator.free(result_buf);
1505 while (true) {
1506 const ret_val = posix.readlink(path_buf.ptr, result_buf.ptr, result_buf.len);
1507 const err = posix.getErrno(ret_val);
1508 if (err > 0) {
1509 return switch (err) {
1510 posix.EACCES => error.AccessDenied,
1511 posix.EFAULT, posix.EINVAL => unreachable,
1512 posix.EIO => error.FileSystem,
1513 posix.ELOOP => error.SymLinkLoop,
1514 posix.ENAMETOOLONG => error.NameTooLong,
1515 posix.ENOENT => error.FileNotFound,
1516 posix.ENOMEM => error.SystemResources,
1517 posix.ENOTDIR => error.NotDir,
1518 else => unexpectedErrorPosix(err),
1519 };
1520 }
1521 if (ret_val == result_buf.len) {
1522 result_buf = try allocator.realloc(u8, result_buf, result_buf.len * 2);
1523 continue;
1524 }
1525 return allocator.shrink(u8, result_buf, ret_val);
1689/// The return value is a slice of out_buffer.
1690pub fn readLinkC(out_buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 {
1691 const rc = posix.readlink(pathname, out_buffer, out_buffer.len);
1692 const err = posix.getErrno(rc);
1693 switch (err) {
1694 0 => return out_buffer[0..rc],
1695 posix.EACCES => return error.AccessDenied,
1696 posix.EFAULT => unreachable,
1697 posix.EINVAL => unreachable,
1698 posix.EIO => return error.FileSystem,
1699 posix.ELOOP => return error.SymLinkLoop,
1700 posix.ENAMETOOLONG => unreachable, // out_buffer is at least PATH_MAX
1701 posix.ENOENT => return error.FileNotFound,
1702 posix.ENOMEM => return error.SystemResources,
1703 posix.ENOTDIR => return error.NotDir,
1704 else => return unexpectedErrorPosix(err),
15261705 }
15271706}
15281707
1708/// Read value of a symbolic link.
1709/// The return value is a slice of out_buffer.
1710pub fn readLink(out_buffer: *[posix.PATH_MAX]u8, file_path: []const u8) ![]u8 {
1711 const file_path_c = try toPosixPath(file_path);
1712 return readLinkC(out_buffer, &file_path_c);
1713}
1714
15291715pub fn posix_setuid(uid: u32) !void {
15301716 const err = posix.getErrno(posix.setuid(uid));
15311717 if (err == 0) return;
......@@ -1572,6 +1758,8 @@ pub fn posix_setregid(rgid: u32, egid: u32) !void {
15721758
15731759pub const WindowsGetStdHandleErrs = error{
15741760 NoStdHandles,
1761
1762 /// See https://github.com/ziglang/zig/issues/1396
15751763 Unexpected,
15761764};
15771765
......@@ -1899,7 +2087,7 @@ pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {
18992087/// Call this when you made a windows DLL call or something that does SetLastError
19002088/// and you get an unexpected error.
19012089pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
1902 if (unexpected_error_tracing) {
2090 if (true) {
19032091 debug.warn("unexpected GetLastError(): {}\n", err);
19042092 debug.dumpCurrentStackTrace(null);
19052093 }
......@@ -1908,17 +2096,12 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
19082096
19092097pub fn openSelfExe() !os.File {
19102098 switch (builtin.os) {
1911 Os.linux => {
1912 const proc_file_path = "/proc/self/exe";
1913 var fixed_buffer_mem: [proc_file_path.len + 1]u8 = undefined;
1914 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1915 return os.File.openRead(&fixed_allocator.allocator, proc_file_path);
1916 },
2099 Os.linux => return os.File.openReadC(c"/proc/self/exe"),
19172100 Os.macosx, Os.ios => {
1918 var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;
1919 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1920 const self_exe_path = try selfExePath(&fixed_allocator.allocator);
1921 return os.File.openRead(&fixed_allocator.allocator, self_exe_path);
2101 var buf: [MAX_PATH_BYTES]u8 = undefined;
2102 const self_exe_path = try selfExePath(&buf);
2103 buf[self_exe_path.len] = 0;
2104 return os.File.openReadC(self_exe_path.ptr);
19222105 },
19232106 else => @compileError("Unsupported OS"),
19242107 }
......@@ -1927,7 +2110,7 @@ pub fn openSelfExe() !os.File {
19272110test "openSelfExe" {
19282111 switch (builtin.os) {
19292112 Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
1930 else => return, // Unsupported OS.
2113 else => return error.SkipZigTest, // Unsupported OS
19312114 }
19322115}
19332116
......@@ -1936,69 +2119,68 @@ test "openSelfExe" {
19362119/// If you only want an open file handle, use openSelfExe.
19372120/// This function may return an error if the current executable
19382121/// was deleted after spawning.
1939/// Caller owns returned memory.
1940pub fn selfExePath(allocator: *mem.Allocator) ![]u8 {
2122/// Returned value is a slice of out_buffer.
2123///
2124/// On Linux, depends on procfs being mounted. If the currently executing binary has
2125/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`.
2126/// TODO make the return type of this a null terminated pointer
2127pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
19412128 switch (builtin.os) {
1942 Os.linux => {
1943 // If the currently executing binary has been deleted,
1944 // the file path looks something like `/a/b/c/exe (deleted)`
1945 return readLink(allocator, "/proc/self/exe");
1946 },
2129 Os.linux => return readLink(out_buffer, "/proc/self/exe"),
19472130 Os.windows => {
1948 var out_path = try Buffer.initSize(allocator, 0xff);
1949 errdefer out_path.deinit();
1950 while (true) {
1951 const dword_len = try math.cast(windows.DWORD, out_path.len());
1952 const copied_amt = windows.GetModuleFileNameA(null, out_path.ptr(), dword_len);
1953 if (copied_amt <= 0) {
1954 const err = windows.GetLastError();
1955 return switch (err) {
1956 else => unexpectedErrorWindows(err),
1957 };
1958 }
1959 if (copied_amt < out_path.len()) {
1960 out_path.shrink(copied_amt);
1961 return out_path.toOwnedSlice();
2131 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
2132 const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
2133 const rc = windows.GetModuleFileNameW(null, &utf16le_buf, casted_len);
2134 assert(rc <= utf16le_buf.len);
2135 if (rc == 0) {
2136 const err = windows.GetLastError();
2137 switch (err) {
2138 else => return unexpectedErrorWindows(err),
19622139 }
1963 const new_len = (out_path.len() << 1) | 0b1;
1964 try out_path.resize(new_len);
19652140 }
2141 const utf16le_slice = utf16le_buf[0..rc];
2142 // Trust that Windows gives us valid UTF-16LE.
2143 const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable;
2144 return out_buffer[0..end_index];
19662145 },
19672146 Os.macosx, Os.ios => {
1968 var u32_len: u32 = 0;
1969 const ret1 = c._NSGetExecutablePath(undefined, &u32_len);
1970 assert(ret1 != 0);
1971 const bytes = try allocator.alloc(u8, u32_len);
1972 errdefer allocator.free(bytes);
1973 const ret2 = c._NSGetExecutablePath(bytes.ptr, &u32_len);
1974 assert(ret2 == 0);
1975 return bytes;
2147 var u32_len: u32 = @intCast(u32, out_buffer.len); // TODO shouldn't need this cast
2148 const rc = c._NSGetExecutablePath(out_buffer, &u32_len);
2149 if (rc != 0) return error.NameTooLong;
2150 return mem.toSlice(u8, out_buffer);
19762151 },
19772152 else => @compileError("Unsupported OS"),
19782153 }
19792154}
19802155
1981/// Get the directory path that contains the current executable.
2156/// `selfExeDirPath` except allocates the result on the heap.
19822157/// Caller owns returned memory.
1983pub fn selfExeDirPath(allocator: *mem.Allocator) ![]u8 {
2158pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 {
2159 var buf: [MAX_PATH_BYTES]u8 = undefined;
2160 return mem.dupe(allocator, u8, try selfExeDirPath(&buf));
2161}
2162
2163/// Get the directory path that contains the current executable.
2164/// Returned value is a slice of out_buffer.
2165pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 {
19842166 switch (builtin.os) {
19852167 Os.linux => {
19862168 // If the currently executing binary has been deleted,
19872169 // the file path looks something like `/a/b/c/exe (deleted)`
19882170 // This path cannot be opened, but it's valid for determining the directory
19892171 // the executable was in when it was run.
1990 const full_exe_path = try readLink(allocator, "/proc/self/exe");
1991 errdefer allocator.free(full_exe_path);
1992 const dir = path.dirname(full_exe_path) orelse ".";
1993 return allocator.shrink(u8, full_exe_path, dir.len);
2172 const full_exe_path = try readLinkC(out_buffer, c"/proc/self/exe");
2173 // Assume that /proc/self/exe has an absolute path, and therefore dirname
2174 // will not return null.
2175 return path.dirname(full_exe_path).?;
19942176 },
19952177 Os.windows, Os.macosx, Os.ios => {
1996 const self_exe_path = try selfExePath(allocator);
1997 errdefer allocator.free(self_exe_path);
1998 const dirname = os.path.dirname(self_exe_path) orelse ".";
1999 return allocator.shrink(u8, self_exe_path, dirname.len);
2178 const self_exe_path = try selfExePath(out_buffer);
2179 // Assume that the OS APIs return absolute paths, and therefore dirname
2180 // will not return null.
2181 return path.dirname(self_exe_path).?;
20002182 },
2001 else => @compileError("unimplemented: std.os.selfExeDirPath for " ++ @tagName(builtin.os)),
2183 else => @compileError("Unsupported OS"),
20022184 }
20032185}
20042186
......@@ -2102,6 +2284,7 @@ pub const PosixBindError = error{
21022284 /// The socket inode would reside on a read-only filesystem.
21032285 ReadOnlyFileSystem,
21042286
2287 /// See https://github.com/ziglang/zig/issues/1396
21052288 Unexpected,
21062289};
21072290
......@@ -2145,6 +2328,7 @@ const PosixListenError = error{
21452328 /// The socket is not of a type that supports the listen() operation.
21462329 OperationNotSupported,
21472330
2331 /// See https://github.com/ziglang/zig/issues/1396
21482332 Unexpected,
21492333};
21502334
......@@ -2198,6 +2382,7 @@ pub const PosixAcceptError = error{
21982382 /// Firewall rules forbid connection.
21992383 BlockedByFirewall,
22002384
2385 /// See https://github.com/ziglang/zig/issues/1396
22012386 Unexpected,
22022387};
22032388
......@@ -2243,6 +2428,7 @@ pub const LinuxEpollCreateError = error{
22432428 /// There was insufficient memory to create the kernel object.
22442429 SystemResources,
22452430
2431 /// See https://github.com/ziglang/zig/issues/1396
22462432 Unexpected,
22472433};
22482434
......@@ -2297,6 +2483,7 @@ pub const LinuxEpollCtlError = error{
22972483 /// for example, a regular file or a directory.
22982484 FileDescriptorIncompatibleWithEpoll,
22992485
2486 /// See https://github.com/ziglang/zig/issues/1396
23002487 Unexpected,
23012488};
23022489
......@@ -2339,6 +2526,7 @@ pub const LinuxEventFdError = error{
23392526 ProcessFdQuotaExceeded,
23402527 SystemFdQuotaExceeded,
23412528
2529 /// See https://github.com/ziglang/zig/issues/1396
23422530 Unexpected,
23432531};
23442532
......@@ -2361,6 +2549,7 @@ pub const PosixGetSockNameError = error{
23612549 /// Insufficient resources were available in the system to perform the operation.
23622550 SystemResources,
23632551
2552 /// See https://github.com/ziglang/zig/issues/1396
23642553 Unexpected,
23652554};
23662555
......@@ -2414,6 +2603,7 @@ pub const PosixConnectError = error{
24142603 /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
24152604 ConnectionTimedOut,
24162605
2606 /// See https://github.com/ziglang/zig/issues/1396
24172607 Unexpected,
24182608};
24192609
......@@ -2516,26 +2706,66 @@ pub const Thread = struct {
25162706 data: Data,
25172707
25182708 pub const use_pthreads = is_posix and builtin.link_libc;
2709
2710 /// Represents a kernel thread handle.
2711 /// May be an integer or a pointer depending on the platform.
2712 /// On Linux and POSIX, this is the same as Id.
2713 pub const Handle = if (use_pthreads)
2714 c.pthread_t
2715 else switch (builtin.os) {
2716 builtin.Os.linux => i32,
2717 builtin.Os.windows => windows.HANDLE,
2718 else => @compileError("Unsupported OS"),
2719 };
2720
2721 /// Represents a unique ID per thread.
2722 /// May be an integer or pointer depending on the platform.
2723 /// On Linux and POSIX, this is the same as Handle.
2724 pub const Id = switch (builtin.os) {
2725 builtin.Os.windows => windows.DWORD,
2726 else => Handle,
2727 };
2728
25192729 pub const Data = if (use_pthreads)
25202730 struct {
2521 handle: c.pthread_t,
2731 handle: Thread.Handle,
25222732 stack_addr: usize,
25232733 stack_len: usize,
25242734 }
25252735 else switch (builtin.os) {
25262736 builtin.Os.linux => struct {
2527 pid: i32,
2737 handle: Thread.Handle,
25282738 stack_addr: usize,
25292739 stack_len: usize,
25302740 },
25312741 builtin.Os.windows => struct {
2532 handle: windows.HANDLE,
2742 handle: Thread.Handle,
25332743 alloc_start: *c_void,
25342744 heap_handle: windows.HANDLE,
25352745 },
25362746 else => @compileError("Unsupported OS"),
25372747 };
25382748
2749 /// Returns the ID of the calling thread.
2750 /// Makes a syscall every time the function is called.
2751 /// On Linux and POSIX, this Id is the same as a Handle.
2752 pub fn getCurrentId() Id {
2753 if (use_pthreads) {
2754 return c.pthread_self();
2755 } else
2756 return switch (builtin.os) {
2757 builtin.Os.linux => linux.gettid(),
2758 builtin.Os.windows => windows.GetCurrentThreadId(),
2759 else => @compileError("Unsupported OS"),
2760 };
2761 }
2762
2763 /// Returns the handle of this thread.
2764 /// On Linux and POSIX, this is the same as Id.
2765 pub fn handle(self: Thread) Handle {
2766 return self.data.handle;
2767 }
2768
25392769 pub fn wait(self: *const Thread) void {
25402770 if (use_pthreads) {
25412771 const err = c.pthread_join(self.data.handle, null);
......@@ -2550,9 +2780,9 @@ pub const Thread = struct {
25502780 } else switch (builtin.os) {
25512781 builtin.Os.linux => {
25522782 while (true) {
2553 const pid_value = @atomicLoad(i32, &self.data.pid, builtin.AtomicOrder.SeqCst);
2783 const pid_value = @atomicLoad(i32, &self.data.handle, builtin.AtomicOrder.SeqCst);
25542784 if (pid_value == 0) break;
2555 const rc = linux.futex_wait(@ptrToInt(&self.data.pid), linux.FUTEX_WAIT, pid_value, null);
2785 const rc = linux.futex_wait(@ptrToInt(&self.data.handle), linux.FUTEX_WAIT, pid_value, null);
25562786 switch (linux.getErrno(rc)) {
25572787 0 => continue,
25582788 posix.EINTR => continue,
......@@ -2595,6 +2825,7 @@ pub const SpawnThreadError = error{
25952825 /// Not enough userland memory to spawn the thread.
25962826 OutOfMemory,
25972827
2828 /// See https://github.com/ziglang/zig/issues/1396
25982829 Unexpected,
25992830};
26002831
......@@ -2734,7 +2965,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
27342965 // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
27352966 const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
27362967 const newtls: usize = 0;
2737 const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid);
2968 const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
27382969 const err = posix.getErrno(rc);
27392970 switch (err) {
27402971 0 => return thread_ptr,
......@@ -2770,7 +3001,9 @@ pub fn posixFStat(fd: i32) !posix.Stat {
27703001 const err = posix.getErrno(posix.fstat(fd, &stat));
27713002 if (err > 0) {
27723003 return switch (err) {
2773 posix.EBADF => error.BadFd,
3004 // We do not make this an error code because if you get EBADF it's always a bug,
3005 // since the fd could have been reused.
3006 posix.EBADF => unreachable,
27743007 posix.ENOMEM => error.SystemResources,
27753008 else => os.unexpectedErrorPosix(err),
27763009 };
......@@ -2782,6 +3015,8 @@ pub fn posixFStat(fd: i32) !posix.Stat {
27823015pub const CpuCountError = error{
27833016 OutOfMemory,
27843017 PermissionDenied,
3018
3019 /// See https://github.com/ziglang/zig/issues/1396
27853020 Unexpected,
27863021};
27873022
......@@ -2852,6 +3087,7 @@ pub const BsdKQueueError = error{
28523087 /// The system-wide limit on the total number of open files has been reached.
28533088 SystemFdQuotaExceeded,
28543089
3090 /// See https://github.com/ziglang/zig/issues/1396
28553091 Unexpected,
28563092};
28573093
......@@ -2903,3 +3139,44 @@ pub fn bsdKEvent(
29033139 }
29043140 }
29053141}
3142
3143pub fn linuxINotifyInit1(flags: u32) !i32 {
3144 const rc = linux.inotify_init1(flags);
3145 const err = posix.getErrno(rc);
3146 switch (err) {
3147 0 => return @intCast(i32, rc),
3148 posix.EINVAL => unreachable,
3149 posix.EMFILE => return error.ProcessFdQuotaExceeded,
3150 posix.ENFILE => return error.SystemFdQuotaExceeded,
3151 posix.ENOMEM => return error.SystemResources,
3152 else => return unexpectedErrorPosix(err),
3153 }
3154}
3155
3156pub fn linuxINotifyAddWatchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) !i32 {
3157 const rc = linux.inotify_add_watch(inotify_fd, pathname, mask);
3158 const err = posix.getErrno(rc);
3159 switch (err) {
3160 0 => return @intCast(i32, rc),
3161 posix.EACCES => return error.AccessDenied,
3162 posix.EBADF => unreachable,
3163 posix.EFAULT => unreachable,
3164 posix.EINVAL => unreachable,
3165 posix.ENAMETOOLONG => return error.NameTooLong,
3166 posix.ENOENT => return error.FileNotFound,
3167 posix.ENOMEM => return error.SystemResources,
3168 posix.ENOSPC => return error.UserResourceLimitReached,
3169 else => return unexpectedErrorPosix(err),
3170 }
3171}
3172
3173pub fn linuxINotifyRmWatch(inotify_fd: i32, wd: i32) !void {
3174 const rc = linux.inotify_rm_watch(inotify_fd, wd);
3175 const err = posix.getErrno(rc);
3176 switch (err) {
3177 0 => return rc,
3178 posix.EBADF => unreachable,
3179 posix.EINVAL => unreachable,
3180 else => unreachable,
3181 }
3182}
std/os/linux/index.zig+72
......@@ -567,6 +567,37 @@ pub const MNT_DETACH = 2;
567567pub const MNT_EXPIRE = 4;
568568pub const UMOUNT_NOFOLLOW = 8;
569569
570pub const IN_CLOEXEC = O_CLOEXEC;
571pub const IN_NONBLOCK = O_NONBLOCK;
572
573pub const IN_ACCESS = 0x00000001;
574pub const IN_MODIFY = 0x00000002;
575pub const IN_ATTRIB = 0x00000004;
576pub const IN_CLOSE_WRITE = 0x00000008;
577pub const IN_CLOSE_NOWRITE = 0x00000010;
578pub const IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE;
579pub const IN_OPEN = 0x00000020;
580pub const IN_MOVED_FROM = 0x00000040;
581pub const IN_MOVED_TO = 0x00000080;
582pub const IN_MOVE = IN_MOVED_FROM | IN_MOVED_TO;
583pub const IN_CREATE = 0x00000100;
584pub const IN_DELETE = 0x00000200;
585pub const IN_DELETE_SELF = 0x00000400;
586pub const IN_MOVE_SELF = 0x00000800;
587pub const IN_ALL_EVENTS = 0x00000fff;
588
589pub const IN_UNMOUNT = 0x00002000;
590pub const IN_Q_OVERFLOW = 0x00004000;
591pub const IN_IGNORED = 0x00008000;
592
593pub const IN_ONLYDIR = 0x01000000;
594pub const IN_DONT_FOLLOW = 0x02000000;
595pub const IN_EXCL_UNLINK = 0x04000000;
596pub const IN_MASK_ADD = 0x20000000;
597
598pub const IN_ISDIR = 0x40000000;
599pub const IN_ONESHOT = 0x80000000;
600
570601pub const S_IFMT = 0o170000;
571602
572603pub const S_IFDIR = 0o040000;
......@@ -692,6 +723,10 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us
692723 return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));
693724}
694725
726pub fn futex_wake(uaddr: usize, futex_op: u32, val: i32) usize {
727 return syscall3(SYS_futex, uaddr, futex_op, @bitCast(u32, val));
728}
729
695730pub fn getcwd(buf: [*]u8, size: usize) usize {
696731 return syscall2(SYS_getcwd, @ptrToInt(buf), size);
697732}
......@@ -700,6 +735,18 @@ pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize {
700735 return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count);
701736}
702737
738pub fn inotify_init1(flags: u32) usize {
739 return syscall1(SYS_inotify_init1, flags);
740}
741
742pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize {
743 return syscall3(SYS_inotify_add_watch, @intCast(usize, fd), @ptrToInt(pathname), mask);
744}
745
746pub fn inotify_rm_watch(fd: i32, wd: i32) usize {
747 return syscall2(SYS_inotify_rm_watch, @intCast(usize, fd), @intCast(usize, wd));
748}
749
703750pub fn isatty(fd: i32) bool {
704751 var wsz: winsize = undefined;
705752 return syscall3(SYS_ioctl, @intCast(usize, fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
......@@ -742,6 +789,14 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
742789 return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count);
743790}
744791
792pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize {
793 return syscall4(SYS_preadv, @intCast(usize, fd), @ptrToInt(iov), count, offset);
794}
795
796pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize {
797 return syscall4(SYS_pwritev, @intCast(usize, fd), @ptrToInt(iov), count, offset);
798}
799
745800// TODO https://github.com/ziglang/zig/issues/265
746801pub fn rmdir(path: [*]const u8) usize {
747802 return syscall1(SYS_rmdir, @ptrToInt(path));
......@@ -947,6 +1002,10 @@ pub fn getpid() i32 {
9471002 return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid)));
9481003}
9491004
1005pub fn gettid() i32 {
1006 return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid)));
1007}
1008
9501009pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize {
9511010 return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8);
9521011}
......@@ -1060,6 +1119,11 @@ pub const iovec = extern struct {
10601119 iov_len: usize,
10611120};
10621121
1122pub const iovec_const = extern struct {
1123 iov_base: [*]const u8,
1124 iov_len: usize,
1125};
1126
10631127pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
10641128 return syscall3(SYS_getsockname, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len));
10651129}
......@@ -1368,6 +1432,14 @@ pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize {
13681432 return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
13691433}
13701434
1435pub const inotify_event = extern struct {
1436 wd: i32,
1437 mask: u32,
1438 cookie: u32,
1439 len: u32,
1440 //name: [?]u8,
1441};
1442
13711443test "import" {
13721444 if (builtin.os == builtin.Os.linux) {
13731445 _ = @import("test.zig");
std/os/path.zig+138-99
......@@ -11,11 +11,14 @@ const math = std.math;
1111const posix = os.posix;
1212const windows = os.windows;
1313const cstr = std.cstr;
14const windows_util = @import("windows/util.zig");
1415
1516pub const sep_windows = '\\';
1617pub const sep_posix = '/';
1718pub const sep = if (is_windows) sep_windows else sep_posix;
1819
20pub const sep_str = [1]u8{sep};
21
1922pub const delimiter_windows = ';';
2023pub const delimiter_posix = ':';
2124pub const delimiter = if (is_windows) delimiter_windows else delimiter_posix;
......@@ -337,7 +340,7 @@ pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 {
337340pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
338341 if (paths.len == 0) {
339342 assert(is_windows); // resolveWindows called on non windows can't use getCwd
340 return os.getCwd(allocator);
343 return os.getCwdAlloc(allocator);
341344 }
342345
343346 // determine which disk designator we will result with, if any
......@@ -432,7 +435,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
432435 },
433436 WindowsPath.Kind.None => {
434437 assert(is_windows); // resolveWindows called on non windows can't use getCwd
435 const cwd = try os.getCwd(allocator);
438 const cwd = try os.getCwdAlloc(allocator);
436439 defer allocator.free(cwd);
437440 const parsed_cwd = windowsParsePath(cwd);
438441 result = try allocator.alloc(u8, max_size + parsed_cwd.disk_designator.len + 1);
......@@ -448,7 +451,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
448451 } else {
449452 assert(is_windows); // resolveWindows called on non windows can't use getCwd
450453 // TODO call get cwd for the result_disk_designator instead of the global one
451 const cwd = try os.getCwd(allocator);
454 const cwd = try os.getCwdAlloc(allocator);
452455 defer allocator.free(cwd);
453456
454457 result = try allocator.alloc(u8, max_size + cwd.len + 1);
......@@ -506,7 +509,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
506509 result_index += 1;
507510 }
508511
509 return result[0..result_index];
512 return allocator.shrink(u8, result, result_index);
510513}
511514
512515/// This function is like a series of `cd` statements executed one after another.
......@@ -516,7 +519,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
516519pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
517520 if (paths.len == 0) {
518521 assert(!is_windows); // resolvePosix called on windows can't use getCwd
519 return os.getCwd(allocator);
522 return os.getCwdAlloc(allocator);
520523 }
521524
522525 var first_index: usize = 0;
......@@ -538,7 +541,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
538541 result = try allocator.alloc(u8, max_size);
539542 } else {
540543 assert(!is_windows); // resolvePosix called on windows can't use getCwd
541 const cwd = try os.getCwd(allocator);
544 const cwd = try os.getCwdAlloc(allocator);
542545 defer allocator.free(cwd);
543546 result = try allocator.alloc(u8, max_size + cwd.len + 1);
544547 mem.copy(u8, result, cwd);
......@@ -573,11 +576,11 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
573576 result_index += 1;
574577 }
575578
576 return result[0..result_index];
579 return allocator.shrink(u8, result, result_index);
577580}
578581
579582test "os.path.resolve" {
580 const cwd = try os.getCwd(debug.global_allocator);
583 const cwd = try os.getCwdAlloc(debug.global_allocator);
581584 if (is_windows) {
582585 if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) {
583586 cwd[0] = asciiUpper(cwd[0]);
......@@ -591,7 +594,7 @@ test "os.path.resolve" {
591594
592595test "os.path.resolveWindows" {
593596 if (is_windows) {
594 const cwd = try os.getCwd(debug.global_allocator);
597 const cwd = try os.getCwdAlloc(debug.global_allocator);
595598 const parsed_cwd = windowsParsePath(cwd);
596599 {
597600 const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" });
......@@ -1073,112 +1076,148 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons
10731076 assert(mem.eql(u8, result, expected_output));
10741077}
10751078
1076/// Return the canonicalized absolute pathname.
1077/// Expands all symbolic links and resolves references to `.`, `..`, and
1078/// extra `/` characters in ::pathname.
1079/// Caller must deallocate result.
1080pub fn real(allocator: *Allocator, pathname: []const u8) ![]u8 {
1081 switch (builtin.os) {
1082 Os.windows => {
1083 const pathname_buf = try allocator.alloc(u8, pathname.len + 1);
1084 defer allocator.free(pathname_buf);
1085
1086 mem.copy(u8, pathname_buf, pathname);
1087 pathname_buf[pathname.len] = 0;
1088
1089 const h_file = windows.CreateFileA(pathname_buf.ptr, windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null);
1090 if (h_file == windows.INVALID_HANDLE_VALUE) {
1091 const err = windows.GetLastError();
1092 return switch (err) {
1093 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
1094 windows.ERROR.ACCESS_DENIED => error.AccessDenied,
1095 windows.ERROR.FILENAME_EXCED_RANGE => error.NameTooLong,
1096 else => os.unexpectedErrorWindows(err),
1097 };
1098 }
1099 defer os.close(h_file);
1100 var buf = try allocator.alloc(u8, 256);
1101 errdefer allocator.free(buf);
1102 while (true) {
1103 const buf_len = math.cast(windows.DWORD, buf.len) catch return error.NameTooLong;
1104 const result = windows.GetFinalPathNameByHandleA(h_file, buf.ptr, buf_len, windows.VOLUME_NAME_DOS);
1105
1106 if (result == 0) {
1107 const err = windows.GetLastError();
1108 return switch (err) {
1109 windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
1110 windows.ERROR.NOT_ENOUGH_MEMORY => error.OutOfMemory,
1111 windows.ERROR.INVALID_PARAMETER => unreachable,
1112 else => os.unexpectedErrorWindows(err),
1113 };
1114 }
1079pub const RealError = error{
1080 FileNotFound,
1081 AccessDenied,
1082 NameTooLong,
1083 NotSupported,
1084 NotDir,
1085 SymLinkLoop,
1086 InputOutput,
1087 FileTooBig,
1088 IsDir,
1089 ProcessFdQuotaExceeded,
1090 SystemFdQuotaExceeded,
1091 NoDevice,
1092 SystemResources,
1093 NoSpaceLeft,
1094 FileSystem,
1095 BadPathName,
1096
1097 /// On Windows, file paths must be valid Unicode.
1098 InvalidUtf8,
1099
1100 /// TODO remove this possibility
1101 PathAlreadyExists,
1102
1103 /// TODO remove this possibility
1104 Unexpected,
1105};
11151106
1116 if (result > buf.len) {
1117 buf = try allocator.realloc(u8, buf, result);
1118 continue;
1119 }
1107/// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
1108/// Otherwise use `real` or `realC`.
1109pub fn realW(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u16) RealError![]u8 {
1110 const h_file = windows.CreateFileW(
1111 pathname,
1112 windows.GENERIC_READ,
1113 windows.FILE_SHARE_READ,
1114 null,
1115 windows.OPEN_EXISTING,
1116 windows.FILE_ATTRIBUTE_NORMAL,
1117 null,
1118 );
1119 if (h_file == windows.INVALID_HANDLE_VALUE) {
1120 const err = windows.GetLastError();
1121 switch (err) {
1122 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
1123 windows.ERROR.ACCESS_DENIED => return error.AccessDenied,
1124 windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
1125 else => return os.unexpectedErrorWindows(err),
1126 }
1127 }
1128 defer os.close(h_file);
1129 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
1130 const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
1131 const result = windows.GetFinalPathNameByHandleW(h_file, &utf16le_buf, casted_len, windows.VOLUME_NAME_DOS);
1132 assert(result <= utf16le_buf.len);
1133 if (result == 0) {
1134 const err = windows.GetLastError();
1135 switch (err) {
1136 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
1137 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
1138 windows.ERROR.NOT_ENOUGH_MEMORY => return error.SystemResources,
1139 windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
1140 windows.ERROR.INVALID_PARAMETER => unreachable,
1141 else => return os.unexpectedErrorWindows(err),
1142 }
1143 }
1144 const utf16le_slice = utf16le_buf[0..result];
11201145
1121 // windows returns \\?\ prepended to the path
1122 // we strip it because nobody wants \\?\ prepended to their path
1123 const final_len = x: {
1124 if (result > 4 and mem.startsWith(u8, buf, "\\\\?\\")) {
1125 var i: usize = 4;
1126 while (i < result) : (i += 1) {
1127 buf[i - 4] = buf[i];
1128 }
1129 break :x result - 4;
1130 } else {
1131 break :x result;
1132 }
1133 };
1134
1135 return allocator.shrink(u8, buf, final_len);
1136 }
1146 // windows returns \\?\ prepended to the path
1147 // we strip it because nobody wants \\?\ prepended to their path
1148 const prefix = []u16{ '\\', '\\', '?', '\\' };
1149 const start_index = if (mem.startsWith(u16, utf16le_slice, prefix)) prefix.len else 0;
1150
1151 // Trust that Windows gives us valid UTF-16LE.
1152 const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice[start_index..]) catch unreachable;
1153 return out_buffer[0..end_index];
1154}
1155
1156/// See `real`
1157/// Use this when you have a null terminated pointer path.
1158pub fn realC(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u8) RealError![]u8 {
1159 switch (builtin.os) {
1160 Os.windows => {
1161 const pathname_w = try windows_util.cStrToPrefixedFileW(pathname);
1162 return realW(out_buffer, pathname_w);
11371163 },
11381164 Os.macosx, Os.ios => {
1139 // TODO instead of calling the libc function here, port the implementation
1140 // to Zig, and then remove the NameTooLong error possibility.
1141 const pathname_buf = try allocator.alloc(u8, pathname.len + 1);
1142 defer allocator.free(pathname_buf);
1143
1144 const result_buf = try allocator.alloc(u8, posix.PATH_MAX);
1145 errdefer allocator.free(result_buf);
1146
1147 mem.copy(u8, pathname_buf, pathname);
1148 pathname_buf[pathname.len] = 0;
1149
1150 const err = posix.getErrno(posix.realpath(pathname_buf.ptr, result_buf.ptr));
1151 if (err > 0) {
1152 return switch (err) {
1153 posix.EINVAL => unreachable,
1154 posix.EBADF => unreachable,
1155 posix.EFAULT => unreachable,
1156 posix.EACCES => error.AccessDenied,
1157 posix.ENOENT => error.FileNotFound,
1158 posix.ENOTSUP => error.NotSupported,
1159 posix.ENOTDIR => error.NotDir,
1160 posix.ENAMETOOLONG => error.NameTooLong,
1161 posix.ELOOP => error.SymLinkLoop,
1162 posix.EIO => error.InputOutput,
1163 else => os.unexpectedErrorPosix(err),
1164 };
1165 // TODO instead of calling the libc function here, port the implementation to Zig
1166 const err = posix.getErrno(posix.realpath(pathname, out_buffer));
1167 switch (err) {
1168 0 => return mem.toSlice(u8, out_buffer),
1169 posix.EINVAL => unreachable,
1170 posix.EBADF => unreachable,
1171 posix.EFAULT => unreachable,
1172 posix.EACCES => return error.AccessDenied,
1173 posix.ENOENT => return error.FileNotFound,
1174 posix.ENOTSUP => return error.NotSupported,
1175 posix.ENOTDIR => return error.NotDir,
1176 posix.ENAMETOOLONG => return error.NameTooLong,
1177 posix.ELOOP => return error.SymLinkLoop,
1178 posix.EIO => return error.InputOutput,
1179 else => return os.unexpectedErrorPosix(err),
11651180 }
1166 return allocator.shrink(u8, result_buf, cstr.len(result_buf.ptr));
11671181 },
11681182 Os.linux => {
1169 const fd = try os.posixOpen(allocator, pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0);
1183 const fd = try os.posixOpenC(pathname, posix.O_PATH | posix.O_NONBLOCK | posix.O_CLOEXEC, 0);
11701184 defer os.close(fd);
11711185
11721186 var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
1173 const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}", fd) catch unreachable;
1187 const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}\x00", fd) catch unreachable;
11741188
1175 return os.readLink(allocator, proc_path);
1189 return os.readLinkC(out_buffer, proc_path.ptr);
11761190 },
11771191 else => @compileError("TODO implement os.path.real for " ++ @tagName(builtin.os)),
11781192 }
11791193}
11801194
1195/// Return the canonicalized absolute pathname.
1196/// Expands all symbolic links and resolves references to `.`, `..`, and
1197/// extra `/` characters in ::pathname.
1198/// The return value is a slice of out_buffer, and not necessarily from the beginning.
1199pub fn real(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: []const u8) RealError![]u8 {
1200 switch (builtin.os) {
1201 Os.windows => {
1202 const pathname_w = try windows_util.sliceToPrefixedFileW(pathname);
1203 return realW(out_buffer, &pathname_w);
1204 },
1205 Os.macosx, Os.ios, Os.linux => {
1206 const pathname_c = try os.toPosixPath(pathname);
1207 return realC(out_buffer, &pathname_c);
1208 },
1209 else => @compileError("Unsupported OS"),
1210 }
1211}
1212
1213/// `real`, except caller must free the returned memory.
1214pub fn realAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
1215 var buf: [os.MAX_PATH_BYTES]u8 = undefined;
1216 return mem.dupe(allocator, u8, try real(&buf, pathname));
1217}
1218
11811219test "os.path.real" {
11821220 // at least call it so it gets compiled
1183 _ = real(debug.global_allocator, "some_path");
1221 var buf: [os.MAX_PATH_BYTES]u8 = undefined;
1222 std.debug.assertError(real(&buf, "definitely_bogus_does_not_exist1234"), error.FileNotFound);
11841223}
std/os/test.zig+25-8
......@@ -10,30 +10,47 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
1010const AtomicOrder = builtin.AtomicOrder;
1111
1212test "makePath, put some files in it, deleteTree" {
13 try os.makePath(a, "os_test_tmp/b/c");
14 try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense");
15 try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah");
13 try os.makePath(a, "os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "c");
14 try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "c" ++ os.path.sep_str ++ "file.txt", "nonsense");
15 try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "file2.txt", "blah");
1616 try os.deleteTree(a, "os_test_tmp");
1717 if (os.Dir.open(a, "os_test_tmp")) |dir| {
1818 @panic("expected error");
1919 } else |err| {
20 assert(err == error.PathNotFound);
20 assert(err == error.FileNotFound);
2121 }
2222}
2323
2424test "access file" {
2525 try os.makePath(a, "os_test_tmp");
26 if (os.File.access(a, "os_test_tmp/file.txt")) |ok| {
26 if (os.File.access("os_test_tmp" ++ os.path.sep_str ++ "file.txt")) |ok| {
2727 @panic("expected error");
2828 } else |err| {
29 assert(err == error.NotFound);
29 assert(err == error.FileNotFound);
3030 }
3131
32 try io.writeFile(a, "os_test_tmp/file.txt", "");
33 try os.File.access(a, "os_test_tmp/file.txt");
32 try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "file.txt", "");
33 try os.File.access("os_test_tmp" ++ os.path.sep_str ++ "file.txt");
3434 try os.deleteTree(a, "os_test_tmp");
3535}
3636
37fn testThreadIdFn(thread_id: *os.Thread.Id) void {
38 thread_id.* = os.Thread.getCurrentId();
39}
40
41test "std.os.Thread.getCurrentId" {
42 var thread_current_id: os.Thread.Id = undefined;
43 const thread = try os.spawnThread(&thread_current_id, testThreadIdFn);
44 const thread_id = thread.handle();
45 thread.wait();
46 switch (builtin.os) {
47 builtin.Os.windows => assert(os.Thread.getCurrentId() != thread_current_id),
48 else => {
49 assert(thread_current_id == thread_id);
50 },
51 }
52}
53
3754test "spawn threads" {
3855 var shared_ctx: i32 = 1;
3956
std/os/windows/index.zig+15-2
......@@ -67,8 +67,9 @@ pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD));
6767pub const OVERLAPPED = extern struct {
6868 Internal: ULONG_PTR,
6969 InternalHigh: ULONG_PTR,
70 Pointer: PVOID,
71 hEvent: HANDLE,
70 Offset: DWORD,
71 OffsetHigh: DWORD,
72 hEvent: ?HANDLE,
7273};
7374pub const LPOVERLAPPED = *OVERLAPPED;
7475
......@@ -350,3 +351,15 @@ pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));
350351pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));
351352pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));
352353pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057));
354
355pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
356pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000;
357pub const FILE_FLAG_NO_BUFFERING = 0x20000000;
358pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000;
359pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
360pub const FILE_FLAG_OVERLAPPED = 0x40000000;
361pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000;
362pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000;
363pub const FILE_FLAG_SESSION_AWARE = 0x00800000;
364pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000;
365pub const FILE_FLAG_WRITE_THROUGH = 0x80000000;
std/os/windows/kernel32.zig+87-15
......@@ -1,14 +1,24 @@
11use @import("index.zig");
22
3pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL;
4
35pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
46
5pub extern "kernel32" stdcallcc fn CreateDirectoryA(
6 lpPathName: LPCSTR,
7 lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
8) BOOL;
7pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
8pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
99
1010pub extern "kernel32" stdcallcc fn CreateFileA(
11 lpFileName: LPCSTR,
11 lpFileName: [*]const u8, // TODO null terminated pointer type
12 dwDesiredAccess: DWORD,
13 dwShareMode: DWORD,
14 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
15 dwCreationDisposition: DWORD,
16 dwFlagsAndAttributes: DWORD,
17 hTemplateFile: ?HANDLE,
18) HANDLE;
19
20pub extern "kernel32" stdcallcc fn CreateFileW(
21 lpFileName: [*]const u16, // TODO null terminated pointer type
1222 dwDesiredAccess: DWORD,
1323 dwShareMode: DWORD,
1424 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
......@@ -47,7 +57,8 @@ pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, Ex
4757
4858pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
4959
50pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
60pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: [*]const u8) BOOL;
61pub extern "kernel32" stdcallcc fn DeleteFileW(lpFileName: [*]const u16) BOOL;
5162
5263pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
5364
......@@ -61,7 +72,11 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
6172
6273pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
6374
64pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
75pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: DWORD, lpBuffer: ?[*]CHAR) DWORD;
76pub extern "kernel32" stdcallcc fn GetCurrentDirectoryW(nBufferLength: DWORD, lpBuffer: ?[*]WCHAR) DWORD;
77
78pub extern "kernel32" stdcallcc fn GetCurrentThread() HANDLE;
79pub extern "kernel32" stdcallcc fn GetCurrentThreadId() DWORD;
6580
6681pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
6782
......@@ -71,9 +86,11 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
7186
7287pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
7388
74pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
89pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: [*]const CHAR) DWORD;
90pub extern "kernel32" stdcallcc fn GetFileAttributesW(lpFileName: [*]const WCHAR) DWORD;
7591
76pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
92pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: [*]u8, nSize: DWORD) DWORD;
93pub extern "kernel32" stdcallcc fn GetModuleFileNameW(hModule: ?HMODULE, lpFilename: [*]u16, nSize: DWORD) DWORD;
7794
7895pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
7996
......@@ -91,6 +108,15 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
91108 dwFlags: DWORD,
92109) DWORD;
93110
111pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleW(
112 hFile: HANDLE,
113 lpszFilePath: [*]u16,
114 cchFilePath: DWORD,
115 dwFlags: DWORD,
116) DWORD;
117
118pub extern "kernel32" stdcallcc fn GetOverlappedResult(hFile: HANDLE, lpOverlapped: *OVERLAPPED, lpNumberOfBytesTransferred: *DWORD, bWait: BOOL) BOOL;
119
94120pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
95121pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
96122
......@@ -101,7 +127,6 @@ pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: S
101127pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
102128pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
103129pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
104pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
105130pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
106131pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
107132
......@@ -111,9 +136,17 @@ pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBy
111136
112137pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
113138
139pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;
140
114141pub extern "kernel32" stdcallcc fn MoveFileExA(
115 lpExistingFileName: LPCSTR,
116 lpNewFileName: LPCSTR,
142 lpExistingFileName: [*]const u8,
143 lpNewFileName: [*]const u8,
144 dwFlags: DWORD,
145) BOOL;
146
147pub extern "kernel32" stdcallcc fn MoveFileExW(
148 lpExistingFileName: [*]const u16,
149 lpNewFileName: [*]const u16,
117150 dwFlags: DWORD,
118151) BOOL;
119152
......@@ -123,11 +156,22 @@ pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *
123156
124157pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
125158
159pub extern "kernel32" stdcallcc fn ReadDirectoryChangesW(
160 hDirectory: HANDLE,
161 lpBuffer: [*]align(@alignOf(FILE_NOTIFY_INFORMATION)) u8,
162 nBufferLength: DWORD,
163 bWatchSubtree: BOOL,
164 dwNotifyFilter: DWORD,
165 lpBytesReturned: ?*DWORD,
166 lpOverlapped: ?*OVERLAPPED,
167 lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
168) BOOL;
169
126170pub extern "kernel32" stdcallcc fn ReadFile(
127171 in_hFile: HANDLE,
128 out_lpBuffer: *c_void,
172 out_lpBuffer: [*]u8,
129173 in_nNumberOfBytesToRead: DWORD,
130 out_lpNumberOfBytesRead: *DWORD,
174 out_lpNumberOfBytesRead: ?*DWORD,
131175 in_out_lpOverlapped: ?*OVERLAPPED,
132176) BOOL;
133177
......@@ -150,13 +194,41 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis
150194
151195pub extern "kernel32" stdcallcc fn WriteFile(
152196 in_hFile: HANDLE,
153 in_lpBuffer: *const c_void,
197 in_lpBuffer: [*]const u8,
154198 in_nNumberOfBytesToWrite: DWORD,
155199 out_lpNumberOfBytesWritten: ?*DWORD,
156200 in_out_lpOverlapped: ?*OVERLAPPED,
157201) BOOL;
158202
203pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
204
159205//TODO: call unicode versions instead of relying on ANSI code page
160206pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
161207
162208pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
209
210pub const FILE_NOTIFY_INFORMATION = extern struct {
211 NextEntryOffset: DWORD,
212 Action: DWORD,
213 FileNameLength: DWORD,
214 FileName: [1]WCHAR,
215};
216
217pub const FILE_ACTION_ADDED = 0x00000001;
218pub const FILE_ACTION_REMOVED = 0x00000002;
219pub const FILE_ACTION_MODIFIED = 0x00000003;
220pub const FILE_ACTION_RENAMED_OLD_NAME = 0x00000004;
221pub const FILE_ACTION_RENAMED_NEW_NAME = 0x00000005;
222
223pub const LPOVERLAPPED_COMPLETION_ROUTINE = ?extern fn (DWORD, DWORD, *OVERLAPPED) void;
224
225pub const FILE_LIST_DIRECTORY = 1;
226
227pub const FILE_NOTIFY_CHANGE_CREATION = 64;
228pub const FILE_NOTIFY_CHANGE_SIZE = 8;
229pub const FILE_NOTIFY_CHANGE_SECURITY = 256;
230pub const FILE_NOTIFY_CHANGE_LAST_ACCESS = 32;
231pub const FILE_NOTIFY_CHANGE_LAST_WRITE = 16;
232pub const FILE_NOTIFY_CHANGE_DIR_NAME = 2;
233pub const FILE_NOTIFY_CHANGE_FILE_NAME = 1;
234pub const FILE_NOTIFY_CHANGE_ATTRIBUTES = 4;
std/os/windows/util.zig+85-28
......@@ -7,9 +7,17 @@ const mem = std.mem;
77const BufMap = std.BufMap;
88const cstr = std.cstr;
99
10// > The maximum path of 32,767 characters is approximate, because the "\\?\"
11// > prefix may be expanded to a longer string by the system at run time, and
12// > this expansion applies to the total length.
13// from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
14pub const PATH_MAX_WIDE = 32767;
15
1016pub const WaitError = error{
1117 WaitAbandoned,
1218 WaitTimeOut,
19
20 /// See https://github.com/ziglang/zig/issues/1396
1321 Unexpected,
1422};
1523
......@@ -36,20 +44,21 @@ pub fn windowsClose(handle: windows.HANDLE) void {
3644pub const WriteError = error{
3745 SystemResources,
3846 OperationAborted,
39 IoPending,
4047 BrokenPipe,
48
49 /// See https://github.com/ziglang/zig/issues/1396
4150 Unexpected,
4251};
4352
4453pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void {
45 if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), @intCast(u32, bytes.len), null, null) == 0) {
54 if (windows.WriteFile(handle, bytes.ptr, @intCast(u32, bytes.len), null, null) == 0) {
4655 const err = windows.GetLastError();
4756 return switch (err) {
4857 windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,
4958 windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,
5059 windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,
5160 windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,
52 windows.ERROR.IO_PENDING => WriteError.IoPending,
61 windows.ERROR.IO_PENDING => unreachable,
5362 windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,
5463 else => os.unexpectedErrorWindows(err),
5564 };
......@@ -87,37 +96,51 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool {
8796pub const OpenError = error{
8897 SharingViolation,
8998 PathAlreadyExists,
99
100 /// When any of the path components can not be found or the file component can not
101 /// be found. Some operating systems distinguish between path components not found and
102 /// file components not found, but they are collapsed into FileNotFound to gain
103 /// consistency across operating systems.
90104 FileNotFound,
105
91106 AccessDenied,
92107 PipeBusy,
108 NameTooLong,
109
110 /// On Windows, file paths must be valid Unicode.
111 InvalidUtf8,
112
113 /// On Windows, file paths cannot contain these characters:
114 /// '/', '*', '?', '"', '<', '>', '|'
115 BadPathName,
116
117 /// See https://github.com/ziglang/zig/issues/1396
93118 Unexpected,
94 OutOfMemory,
95119};
96120
97/// `file_path` needs to be copied in memory to add a null terminating byte, hence the allocator.
98121pub fn windowsOpen(
99 allocator: *mem.Allocator,
100122 file_path: []const u8,
101123 desired_access: windows.DWORD,
102124 share_mode: windows.DWORD,
103125 creation_disposition: windows.DWORD,
104126 flags_and_attrs: windows.DWORD,
105127) OpenError!windows.HANDLE {
106 const path_with_null = try cstr.addNullByte(allocator, file_path);
107 defer allocator.free(path_with_null);
128 const file_path_w = try sliceToPrefixedFileW(file_path);
108129
109 const result = windows.CreateFileA(path_with_null.ptr, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
130 const result = windows.CreateFileW(&file_path_w, desired_access, share_mode, null, creation_disposition, flags_and_attrs, null);
110131
111132 if (result == windows.INVALID_HANDLE_VALUE) {
112133 const err = windows.GetLastError();
113 return switch (err) {
114 windows.ERROR.SHARING_VIOLATION => OpenError.SharingViolation,
115 windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => OpenError.PathAlreadyExists,
116 windows.ERROR.FILE_NOT_FOUND => OpenError.FileNotFound,
117 windows.ERROR.ACCESS_DENIED => OpenError.AccessDenied,
118 windows.ERROR.PIPE_BUSY => OpenError.PipeBusy,
119 else => os.unexpectedErrorWindows(err),
120 };
134 switch (err) {
135 windows.ERROR.SHARING_VIOLATION => return OpenError.SharingViolation,
136 windows.ERROR.ALREADY_EXISTS => return OpenError.PathAlreadyExists,
137 windows.ERROR.FILE_EXISTS => return OpenError.PathAlreadyExists,
138 windows.ERROR.FILE_NOT_FOUND => return OpenError.FileNotFound,
139 windows.ERROR.PATH_NOT_FOUND => return OpenError.FileNotFound,
140 windows.ERROR.ACCESS_DENIED => return OpenError.AccessDenied,
141 windows.ERROR.PIPE_BUSY => return OpenError.PipeBusy,
142 else => return os.unexpectedErrorWindows(err),
143 }
121144 }
122145
123146 return result;
......@@ -193,9 +216,8 @@ pub fn windowsFindFirstFile(
193216 if (handle == windows.INVALID_HANDLE_VALUE) {
194217 const err = windows.GetLastError();
195218 switch (err) {
196 windows.ERROR.FILE_NOT_FOUND,
197 windows.ERROR.PATH_NOT_FOUND,
198 => return error.PathNotFound,
219 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
220 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
199221 else => return os.unexpectedErrorWindows(err),
200222 }
201223 }
......@@ -221,6 +243,7 @@ pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_compl
221243 const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
222244 const err = windows.GetLastError();
223245 switch (err) {
246 windows.ERROR.INVALID_PARAMETER => unreachable,
224247 else => return os.unexpectedErrorWindows(err),
225248 }
226249 };
......@@ -238,21 +261,55 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_
238261 }
239262}
240263
241pub const WindowsWaitResult = error{
264pub const WindowsWaitResult = enum {
242265 Normal,
243266 Aborted,
267 Cancelled,
244268};
245269
246270pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult {
247271 if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) {
248 if (std.debug.runtime_safety) {
249 const err = windows.GetLastError();
250 if (err != windows.ERROR.ABANDONED_WAIT_0) {
251 std.debug.warn("err: {}\n", err);
252 }
253 assert(err == windows.ERROR.ABANDONED_WAIT_0);
272 const err = windows.GetLastError();
273 switch (err) {
274 windows.ERROR.ABANDONED_WAIT_0 => return WindowsWaitResult.Aborted,
275 windows.ERROR.OPERATION_ABORTED => return WindowsWaitResult.Cancelled,
276 else => {
277 if (std.debug.runtime_safety) {
278 std.debug.panic("unexpected error: {}\n", err);
279 }
280 },
254281 }
255 return WindowsWaitResult.Aborted;
256282 }
257283 return WindowsWaitResult.Normal;
258284}
285
286pub fn cStrToPrefixedFileW(s: [*]const u8) ![PATH_MAX_WIDE + 1]u16 {
287 return sliceToPrefixedFileW(mem.toSliceConst(u8, s));
288}
289
290pub fn sliceToPrefixedFileW(s: []const u8) ![PATH_MAX_WIDE + 1]u16 {
291 // TODO well defined copy elision
292 var result: [PATH_MAX_WIDE + 1]u16 = undefined;
293
294 // > File I/O functions in the Windows API convert "/" to "\" as part of
295 // > converting the name to an NT-style name, except when using the "\\?\"
296 // > prefix as detailed in the following sections.
297 // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
298 // Because we want the larger maximum path length for absolute paths, we
299 // disallow forward slashes in zig std lib file functions on Windows.
300 for (s) |byte|
301 switch (byte) {
302 '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName,
303 else => {},
304 };
305 const start_index = if (mem.startsWith(u8, s, "\\\\") or !os.path.isAbsolute(s)) 0 else blk: {
306 const prefix = []u16{ '\\', '\\', '?', '\\' };
307 mem.copy(u16, result[0..], prefix);
308 break :blk prefix.len;
309 };
310 const end_index = start_index + try std.unicode.utf8ToUtf16Le(result[start_index..], s);
311 assert(end_index <= result.len);
312 if (end_index == result.len) return error.NameTooLong;
313 result[end_index] = 0;
314 return result;
315}
std/os/zen.zig+64-62
......@@ -1,38 +1,55 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3
14//////////////////////////
25//// IPC structures ////
36//////////////////////////
47
58pub const Message = struct {
6 sender: MailboxId,
9sender: MailboxId,
710 receiver: MailboxId,
8 type: usize,
9 payload: usize,
11 code: usize,
12 args: [5]usize,
13 payload: ?[]const u8,
1014
1115 pub fn from(mailbox_id: *const MailboxId) Message {
12 return Message{
13 .sender = MailboxId.Undefined,
14 .receiver = *mailbox_id,
15 .type = 0,
16 .payload = 0,
16 return Message {
17 .sender = MailboxId.Undefined,
18 .receiver = mailbox_id.*,
19 .code = undefined,
20 .args = undefined,
21 .payload = null,
1722 };
1823 }
1924
20 pub fn to(mailbox_id: *const MailboxId, msg_type: usize) Message {
21 return Message{
22 .sender = MailboxId.This,
23 .receiver = *mailbox_id,
24 .type = msg_type,
25 .payload = 0,
25 pub fn to(mailbox_id: *const MailboxId, msg_code: usize, args: ...) Message {
26 var message = Message {
27 .sender = MailboxId.This,
28 .receiver = mailbox_id.*,
29 .code = msg_code,
30 .args = undefined,
31 .payload = null,
2632 };
33
34 assert (args.len <= message.args.len);
35 comptime var i = 0;
36 inline while (i < args.len) : (i += 1) {
37 message.args[i] = args[i];
38 }
39
40 return message;
2741 }
2842
29 pub fn withData(mailbox_id: *const MailboxId, msg_type: usize, payload: usize) Message {
30 return Message{
31 .sender = MailboxId.This,
32 .receiver = *mailbox_id,
33 .type = msg_type,
34 .payload = payload,
35 };
43 pub fn as(self: *const Message, sender: *const MailboxId) Message {
44 var message = self.*;
45 message.sender = sender.*;
46 return message;
47 }
48
49 pub fn withPayload(self: *const Message, payload: []const u8) Message {
50 var message = self.*;
51 message.payload = payload;
52 return message;
3653 }
3754};
3855
......@@ -63,21 +80,26 @@ pub const STDOUT_FILENO = 1;
6380pub const STDERR_FILENO = 2;
6481
6582// FIXME: let's borrow Linux's error numbers for now.
66pub const getErrno = @import("linux/index.zig").getErrno;
6783use @import("linux/errno.zig");
84// Get the errno from a syscall return value, or 0 for no error.
85pub fn getErrno(r: usize) usize {
86 const signed_r = @bitCast(isize, r);
87 return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0;
88}
6889
6990// TODO: implement this correctly.
70pub fn read(fd: i32, buf: *u8, count: usize) usize {
91pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
7192 switch (fd) {
7293 STDIN_FILENO => {
7394 var i: usize = 0;
7495 while (i < count) : (i += 1) {
7596 send(Message.to(Server.Keyboard, 0));
7697
98 // FIXME: we should be certain that we are receiving from Keyboard.
7799 var message = Message.from(MailboxId.This);
78 receive(*message);
100 receive(&message);
79101
80 buf[i] = u8(message.payload);
102 buf[i] = @intCast(u8, message.args[0]);
81103 }
82104 },
83105 else => unreachable,
......@@ -86,13 +108,11 @@ pub fn read(fd: i32, buf: *u8, count: usize) usize {
86108}
87109
88110// TODO: implement this correctly.
89pub fn write(fd: i32, buf: *const u8, count: usize) usize {
111pub fn write(fd: i32, buf: [*]const u8, count: usize) usize {
90112 switch (fd) {
91113 STDOUT_FILENO, STDERR_FILENO => {
92 var i: usize = 0;
93 while (i < count) : (i += 1) {
94 send(Message.withData(Server.Terminal, 1, buf[i]));
95 }
114 send(Message.to(Server.Terminal, 1)
115 .withPayload(buf[0..count]));
96116 },
97117 else => unreachable,
98118 }
......@@ -104,17 +124,14 @@ pub fn write(fd: i32, buf: *const u8, count: usize) usize {
104124///////////////////////////
105125
106126pub const Syscall = enum(usize) {
107 exit = 0,
108 createPort = 1,
109 send = 2,
110 receive = 3,
111 subscribeIRQ = 4,
112 inb = 5,
113 map = 6,
114 createThread = 7,
115 createProcess = 8,
116 wait = 9,
117 portReady = 10,
127 exit = 0,
128 send = 1,
129 receive = 2,
130 subscribeIRQ = 3,
131 inb = 4,
132 outb = 5,
133 map = 6,
134 createThread = 7,
118135};
119136
120137////////////////////
......@@ -126,13 +143,6 @@ pub fn exit(status: i32) noreturn {
126143 unreachable;
127144}
128145
129pub fn createPort(mailbox_id: *const MailboxId) void {
130 _ = switch (*mailbox_id) {
131 MailboxId.Port => |id| syscall1(Syscall.createPort, id),
132 else => unreachable,
133 };
134}
135
136146pub fn send(message: *const Message) void {
137147 _ = syscall1(Syscall.send, @ptrToInt(message));
138148}
......@@ -146,29 +156,21 @@ pub fn subscribeIRQ(irq: u8, mailbox_id: *const MailboxId) void {
146156}
147157
148158pub fn inb(port: u16) u8 {
149 return u8(syscall1(Syscall.inb, port));
159 return @intCast(u8, syscall1(Syscall.inb, port));
160}
161
162pub fn outb(port: u16, value: u8) void {
163 _ = syscall2(Syscall.outb, port, value);
150164}
151165
152166pub fn map(v_addr: usize, p_addr: usize, size: usize, writable: bool) bool {
153 return syscall4(Syscall.map, v_addr, p_addr, size, usize(writable)) != 0;
167 return syscall4(Syscall.map, v_addr, p_addr, size, @boolToInt(writable)) != 0;
154168}
155169
156170pub fn createThread(function: fn () void) u16 {
157171 return u16(syscall1(Syscall.createThread, @ptrToInt(function)));
158172}
159173
160pub fn createProcess(elf_addr: usize) u16 {
161 return u16(syscall1(Syscall.createProcess, elf_addr));
162}
163
164pub fn wait(tid: u16) void {
165 _ = syscall1(Syscall.wait, tid);
166}
167
168pub fn portReady(port: u16) bool {
169 return syscall1(Syscall.portReady, port) != 0;
170}
171
172174/////////////////////////
173175//// Syscall stubs ////
174176/////////////////////////
std/rb.zig created+543
......@@ -0,0 +1,543 @@
1const std = @import("index.zig");
2const assert = std.debug.assert;
3const mem = std.mem; // For mem.Compare
4
5const Color = enum(u1) {
6 Black,
7 Red,
8};
9const Red = Color.Red;
10const Black = Color.Black;
11
12const ReplaceError = error {
13 NotEqual,
14};
15
16/// Insert this into your struct that you want to add to a red-black tree.
17/// Do not use a pointer. Turn the *rb.Node results of the functions in rb
18/// (after resolving optionals) to your structure using @fieldParentPtr(). Example:
19///
20/// const Number = struct {
21/// node: rb.Node,
22/// value: i32,
23/// };
24/// fn number(node: *Node) Number {
25/// return @fieldParentPtr(Number, "node", node);
26/// }
27pub const Node = struct {
28 left: ?*Node,
29 right: ?*Node,
30 parent_and_color: usize, /// parent | color
31
32 pub fn next(constnode: *Node) ?*Node {
33 var node = constnode;
34
35 if (node.right) |right| {
36 var n = right;
37 while (n.left) |left|
38 n = left;
39 return n;
40 }
41
42 while (true) {
43 var parent = node.get_parent();
44 if (parent) |p| {
45 if (node != p.right)
46 return p;
47 node = p;
48 } else
49 return null;
50 }
51 }
52
53 pub fn prev(constnode: *Node) ?*Node {
54 var node = constnode;
55
56 if (node.left) |left| {
57 var n = left;
58 while (n.right) |right|
59 n = right;
60 return n;
61 }
62
63 while (true) {
64 var parent = node.get_parent();
65 if (parent) |p| {
66 if (node != p.left)
67 return p;
68 node = p;
69 } else
70 return null;
71 }
72 }
73
74 pub fn is_root(node: *Node) bool {
75 return node.get_parent() == null;
76 }
77
78 fn is_red(node: *Node) bool {
79 return node.get_color() == Red;
80 }
81
82 fn is_black(node: *Node) bool {
83 return node.get_color() == Black;
84 }
85
86 fn set_parent(node: *Node, parent: ?*Node) void {
87 node.parent_and_color = @ptrToInt(parent) | (node.parent_and_color & 1);
88 }
89
90 fn get_parent(node: *Node) ?*Node {
91 const mask: usize = 1;
92 comptime {
93 assert(@alignOf(*Node) >= 2);
94 }
95 return @intToPtr(*Node, node.parent_and_color & ~mask);
96 }
97
98 fn set_color(node: *Node, color: Color) void {
99 const mask: usize = 1;
100 node.parent_and_color = (node.parent_and_color & ~mask) | @enumToInt(color);
101 }
102
103 fn get_color(node: *Node) Color {
104 return @intToEnum(Color, @intCast(u1, node.parent_and_color & 1));
105 }
106
107 fn set_child(node: *Node, child: ?*Node, is_left: bool) void {
108 if (is_left) {
109 node.left = child;
110 } else {
111 node.right = child;
112 }
113 }
114
115 fn get_first(nodeconst: *Node) *Node {
116 var node = nodeconst;
117 while (node.left) |left| {
118 node = left;
119 }
120 return node;
121 }
122
123 fn get_last(node: *Node) *Node {
124 while (node.right) |right| {
125 node = right;
126 }
127 return node;
128 }
129};
130
131pub const Tree = struct {
132 root: ?*Node,
133 compareFn: fn(*Node, *Node) mem.Compare,
134
135 /// If you have a need for a version that caches this, please file a bug.
136 pub fn first(tree: *Tree) ?*Node {
137 var node: *Node = tree.root orelse return null;
138
139 while (node.left) |left| {
140 node = left;
141 }
142
143 return node;
144 }
145
146 pub fn last(tree: *Tree) ?*Node {
147 var node: *Node = tree.root orelse return null;
148
149 while (node.right) |right| {
150 node = right;
151 }
152
153 return node;
154 }
155
156 /// Duplicate keys are not allowed. The item with the same key already in the
157 /// tree will be returned, and the item will not be inserted.
158 pub fn insert(tree: *Tree, node_const: *Node) ?*Node {
159 var node = node_const;
160 var maybe_key: ?*Node = undefined;
161 var maybe_parent: ?*Node = undefined;
162 var is_left: bool = undefined;
163
164 maybe_key = do_lookup(node, tree, &maybe_parent, &is_left);
165 if (maybe_key) |key| {
166 return key;
167 }
168
169 node.left = null;
170 node.right = null;
171 node.set_color(Red);
172 node.set_parent(maybe_parent);
173
174 if (maybe_parent) |parent| {
175 parent.set_child(node, is_left);
176 } else {
177 tree.root = node;
178 }
179
180 while (node.get_parent()) |*parent| {
181 if (parent.*.is_black())
182 break;
183 // the root is always black
184 var grandpa = parent.*.get_parent() orelse unreachable;
185
186 if (parent.* == grandpa.left) {
187 var maybe_uncle = grandpa.right;
188
189 if (maybe_uncle) |uncle| {
190 if (uncle.is_black())
191 break;
192
193 parent.*.set_color(Black);
194 uncle.set_color(Black);
195 grandpa.set_color(Red);
196 node = grandpa;
197 } else {
198 if (node == parent.*.right) {
199 rotate_left(parent.*, tree);
200 node = parent.*;
201 parent.* = node.get_parent().?; // Just rotated
202 }
203 parent.*.set_color(Black);
204 grandpa.set_color(Red);
205 rotate_right(grandpa, tree);
206 }
207 } else {
208 var maybe_uncle = grandpa.left;
209
210 if (maybe_uncle) |uncle| {
211 if (uncle.is_black())
212 break;
213
214 parent.*.set_color(Black);
215 uncle.set_color(Black);
216 grandpa.set_color(Red);
217 node = grandpa;
218 } else {
219 if (node == parent.*.left) {
220 rotate_right(parent.*, tree);
221 node = parent.*;
222 parent.* = node.get_parent().?; // Just rotated
223 }
224 parent.*.set_color(Black);
225 grandpa.set_color(Red);
226 rotate_left(grandpa, tree);
227 }
228 }
229 }
230 // This was an insert, there is at least one node.
231 tree.root.?.set_color(Black);
232 return null;
233 }
234
235 pub fn lookup(tree: *Tree, key: *Node) ?*Node {
236 var parent: *Node = undefined;
237 var is_left: bool = undefined;
238
239 return do_lookup(key, tree, &parent, &is_left);
240 }
241
242 pub fn remove(tree: *Tree, nodeconst: *Node) void {
243 var node = nodeconst;
244 // as this has the same value as node, it is unsafe to access node after newnode
245 var newnode: ?*Node = nodeconst;
246 var maybe_parent: ?*Node = node.get_parent();
247 var color: Color = undefined;
248 var next: *Node = undefined;
249
250 // This clause is to avoid optionals
251 if (node.left == null and node.right == null) {
252 if (maybe_parent) |parent| {
253 parent.set_child(null, parent.left == node);
254 } else
255 tree.root = null;
256 color = node.get_color();
257 newnode = null;
258 } else {
259 if (node.left == null) {
260 next = node.right.?; // Not both null as per above
261 } else if (node.right == null) {
262 next = node.left.?; // Not both null as per above
263 } else
264 next = node.right.?.get_first(); // Just checked for null above
265
266 if (maybe_parent) |parent| {
267 parent.set_child(next, parent.left == node);
268 } else
269 tree.root = next;
270
271 if (node.left != null and node.right != null) {
272 const left = node.left.?;
273 const right = node.right.?;
274
275 color = next.get_color();
276 next.set_color(node.get_color());
277
278 next.left = left;
279 left.set_parent(next);
280
281 if (next != right) {
282 var parent = next.get_parent().?; // Was traversed via child node (right/left)
283 next.set_parent(node.get_parent());
284
285 newnode = next.right;
286 parent.left = node;
287
288 next.right = right;
289 right.set_parent(next);
290 } else {
291 next.set_parent(maybe_parent);
292 maybe_parent = next;
293 newnode = next.right;
294 }
295 } else {
296 color = node.get_color();
297 newnode = next;
298 }
299 }
300
301 if (newnode) |n|
302 n.set_parent(maybe_parent);
303
304 if (color == Red)
305 return;
306 if (newnode) |n| {
307 n.set_color(Black);
308 return;
309 }
310
311 while (node == tree.root) {
312 // If not root, there must be parent
313 var parent = maybe_parent.?;
314 if (node == parent.left) {
315 var sibling = parent.right.?; // Same number of black nodes.
316
317 if (sibling.is_red()) {
318 sibling.set_color(Black);
319 parent.set_color(Red);
320 rotate_left(parent, tree);
321 sibling = parent.right.?; // Just rotated
322 }
323 if ((if (sibling.left) |n| n.is_black() else true) and
324 (if (sibling.right) |n| n.is_black() else true)) {
325 sibling.set_color(Red);
326 node = parent;
327 maybe_parent = parent.get_parent();
328 continue;
329 }
330 if (if (sibling.right) |n| n.is_black() else true) {
331 sibling.left.?.set_color(Black); // Same number of black nodes.
332 sibling.set_color(Red);
333 rotate_right(sibling, tree);
334 sibling = parent.right.?; // Just rotated
335 }
336 sibling.set_color(parent.get_color());
337 parent.set_color(Black);
338 sibling.right.?.set_color(Black); // Same number of black nodes.
339 rotate_left(parent, tree);
340 newnode = tree.root;
341 break;
342 } else {
343 var sibling = parent.left.?; // Same number of black nodes.
344
345 if (sibling.is_red()) {
346 sibling.set_color(Black);
347 parent.set_color(Red);
348 rotate_right(parent, tree);
349 sibling = parent.left.?; // Just rotated
350 }
351 if ((if (sibling.left) |n| n.is_black() else true) and
352 (if (sibling.right) |n| n.is_black() else true)) {
353 sibling.set_color(Red);
354 node = parent;
355 maybe_parent = parent.get_parent();
356 continue;
357 }
358 if (if (sibling.left) |n| n.is_black() else true) {
359 sibling.right.?.set_color(Black); // Same number of black nodes
360 sibling.set_color(Red);
361 rotate_left(sibling, tree);
362 sibling = parent.left.?; // Just rotated
363 }
364 sibling.set_color(parent.get_color());
365 parent.set_color(Black);
366 sibling.left.?.set_color(Black); // Same number of black nodes
367 rotate_right(parent, tree);
368 newnode = tree.root;
369 break;
370 }
371
372 if (node.is_red())
373 break;
374 }
375
376 if (newnode) |n|
377 n.set_color(Black);
378 }
379
380 /// This is a shortcut to avoid removing and re-inserting an item with the same key.
381 pub fn replace(tree: *Tree, old: *Node, newconst: *Node) !void {
382 var new = newconst;
383
384 // I assume this can get optimized out if the caller already knows.
385 if (tree.compareFn(old, new) != mem.Compare.Equal) return ReplaceError.NotEqual;
386
387 if (old.get_parent()) |parent| {
388 parent.set_child(new, parent.left == old);
389 } else
390 tree.root = new;
391
392 if (old.left) |left|
393 left.set_parent(new);
394 if (old.right) |right|
395 right.set_parent(new);
396
397 new.* = old.*;
398 }
399
400 pub fn init(tree: *Tree, f: fn(*Node, *Node) mem.Compare) void {
401 tree.root = null;
402 tree.compareFn = f;
403 }
404};
405
406fn rotate_left(node: *Node, tree: *Tree) void {
407 var p: *Node = node;
408 var q: *Node = node.right orelse unreachable;
409 var parent: *Node = undefined;
410
411 if (!p.is_root()) {
412 parent = p.get_parent().?;
413 if (parent.left == p) {
414 parent.left = q;
415 } else {
416 parent.right = q;
417 }
418 q.set_parent(parent);
419 } else {
420 tree.root = q;
421 q.set_parent(null);
422 }
423 p.set_parent(q);
424
425 p.right = q.left;
426 if (p.right) |right| {
427 right.set_parent(p);
428 }
429 q.left = p;
430}
431
432fn rotate_right(node: *Node, tree: *Tree) void {
433 var p: *Node = node;
434 var q: *Node = node.left orelse unreachable;
435 var parent: *Node = undefined;
436
437 if (!p.is_root()) {
438 parent = p.get_parent().?;
439 if (parent.left == p) {
440 parent.left = q;
441 } else {
442 parent.right = q;
443 }
444 q.set_parent(parent);
445 } else {
446 tree.root = q;
447 q.set_parent(null);
448 }
449 p.set_parent(q);
450
451 p.left = q.right;
452 if (p.left) |left| {
453 left.set_parent(p);
454 }
455 q.right = p;
456}
457
458fn do_lookup(key: *Node, tree: *Tree, pparent: *?*Node, is_left: *bool) ?*Node {
459 var maybe_node: ?*Node = tree.root;
460
461 pparent.* = null;
462 is_left.* = false;
463
464 while (maybe_node) |node| {
465 var res: mem.Compare = tree.compareFn(node, key);
466 if (res == mem.Compare.Equal) {
467 return node;
468 }
469 pparent.* = node;
470 if (res == mem.Compare.GreaterThan) {
471 is_left.* = true;
472 maybe_node = node.left;
473 } else if (res == mem.Compare.LessThan) {
474 is_left.* = false;
475 maybe_node = node.right;
476 } else {
477 unreachable;
478 }
479 }
480 return null;
481}
482
483const testNumber = struct {
484 node: Node,
485 value: usize,
486};
487
488fn testGetNumber(node: *Node) *testNumber {
489 return @fieldParentPtr(testNumber, "node", node);
490}
491
492fn testCompare(l: *Node, r: *Node) mem.Compare {
493 var left = testGetNumber(l);
494 var right = testGetNumber(r);
495
496 if (left.value < right.value) {
497 return mem.Compare.LessThan;
498 } else if (left.value == right.value) {
499 return mem.Compare.Equal;
500 } else if (left.value > right.value) {
501 return mem.Compare.GreaterThan;
502 }
503 unreachable;
504}
505
506test "rb" {
507 var tree: Tree = undefined;
508 var ns: [10]testNumber = undefined;
509 ns[0].value = 42;
510 ns[1].value = 41;
511 ns[2].value = 40;
512 ns[3].value = 39;
513 ns[4].value = 38;
514 ns[5].value = 39;
515 ns[6].value = 3453;
516 ns[7].value = 32345;
517 ns[8].value = 392345;
518 ns[9].value = 4;
519
520 var dup: testNumber = undefined;
521 dup.value = 32345;
522
523 tree.init(testCompare);
524 _ = tree.insert(&ns[1].node);
525 _ = tree.insert(&ns[2].node);
526 _ = tree.insert(&ns[3].node);
527 _ = tree.insert(&ns[4].node);
528 _ = tree.insert(&ns[5].node);
529 _ = tree.insert(&ns[6].node);
530 _ = tree.insert(&ns[7].node);
531 _ = tree.insert(&ns[8].node);
532 _ = tree.insert(&ns[9].node);
533 tree.remove(&ns[3].node);
534 assert(tree.insert(&dup.node) == &ns[7].node);
535 try tree.replace(&ns[7].node, &dup.node);
536
537 var num: *testNumber = undefined;
538 num = testGetNumber(tree.first().?);
539 while (num.node.next() != null) {
540 assert(testGetNumber(num.node.next().?).value > num.value);
541 num = testGetNumber(num.node.next().?);
542 }
543}
std/segmented_list.zig+13-5
......@@ -2,7 +2,7 @@ const std = @import("index.zig");
22const assert = std.debug.assert;
33const Allocator = std.mem.Allocator;
44
5// Imagine that `fn at(self: &Self, index: usize) &T` is a customer asking for a box
5// Imagine that `fn at(self: *Self, index: usize) &T` is a customer asking for a box
66// from a warehouse, based on a flat array, boxes ordered from 0 to N - 1.
77// But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes.
88// So when the customer requests a box index, we have to translate it to shelf index
......@@ -93,6 +93,14 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
9393
9494 pub const prealloc_count = prealloc_item_count;
9595
96 fn AtType(comptime SelfType: type) type {
97 if (@typeInfo(SelfType).Pointer.is_const) {
98 return *const T;
99 } else {
100 return *T;
101 }
102 }
103
96104 /// Deinitialize with `deinit`
97105 pub fn init(allocator: *Allocator) Self {
98106 return Self{
......@@ -109,7 +117,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
109117 self.* = undefined;
110118 }
111119
112 pub fn at(self: *Self, i: usize) *T {
120 pub fn at(self: var, i: usize) AtType(@typeOf(self)) {
113121 assert(i < self.len);
114122 return self.uncheckedAt(i);
115123 }
......@@ -133,7 +141,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
133141 if (self.len == 0) return null;
134142
135143 const index = self.len - 1;
136 const result = self.uncheckedAt(index).*;
144 const result = uncheckedAt(self, index).*;
137145 self.len = index;
138146 return result;
139147 }
......@@ -141,7 +149,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
141149 pub fn addOne(self: *Self) !*T {
142150 const new_length = self.len + 1;
143151 try self.growCapacity(new_length);
144 const result = self.uncheckedAt(self.len);
152 const result = uncheckedAt(self, self.len);
145153 self.len = new_length;
146154 return result;
147155 }
......@@ -193,7 +201,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
193201 self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, new_cap_shelf_count);
194202 }
195203
196 pub fn uncheckedAt(self: *Self, index: usize) *T {
204 pub fn uncheckedAt(self: var, index: usize) AtType(@typeOf(self)) {
197205 if (index < prealloc_item_count) {
198206 return &self.prealloc_segment[index];
199207 }
std/special/bootstrap.zig-6
......@@ -13,17 +13,11 @@ comptime {
1313 @export("main", main, strong_linkage);
1414 } else if (builtin.os == builtin.Os.windows) {
1515 @export("WinMainCRTStartup", WinMainCRTStartup, strong_linkage);
16 } else if (builtin.os == builtin.Os.zen) {
17 @export("_start", zen_start, strong_linkage);
1816 } else {
1917 @export("_start", _start, strong_linkage);
2018 }
2119}
2220
23extern fn zen_start() noreturn {
24 std.os.posix.exit(@inlineCall(callMain));
25}
26
2721nakedcc fn _start() noreturn {
2822 switch (builtin.arch) {
2923 builtin.Arch.x86_64 => {
std/special/build_runner.zig+2-2
......@@ -72,10 +72,10 @@ pub fn main() !void {
7272 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
7373 const option_name = option_contents[0..name_end];
7474 const option_value = option_contents[name_end + 1 ..];
75 if (builder.addUserInputOption(option_name, option_value))
75 if (try builder.addUserInputOption(option_name, option_value))
7676 return usageAndErr(&builder, false, try stderr_stream);
7777 } else {
78 if (builder.addUserInputFlag(option_contents))
78 if (try builder.addUserInputFlag(option_contents))
7979 return usageAndErr(&builder, false, try stderr_stream);
8080 }
8181 } else if (mem.startsWith(u8, arg, "-")) {
std/unicode.zig+90-32
......@@ -188,6 +188,7 @@ pub const Utf8View = struct {
188188 return Utf8View{ .bytes = s };
189189 }
190190
191 /// TODO: https://github.com/ziglang/zig/issues/425
191192 pub fn initComptime(comptime s: []const u8) Utf8View {
192193 if (comptime init(s)) |r| {
193194 return r;
......@@ -199,7 +200,7 @@ pub const Utf8View = struct {
199200 }
200201 }
201202
202 pub fn iterator(s: *const Utf8View) Utf8Iterator {
203 pub fn iterator(s: Utf8View) Utf8Iterator {
203204 return Utf8Iterator{
204205 .bytes = s.bytes,
205206 .i = 0,
......@@ -217,7 +218,6 @@ const Utf8Iterator = struct {
217218 }
218219
219220 const cp_len = utf8ByteSequenceLength(it.bytes[it.i]) catch unreachable;
220
221221 it.i += cp_len;
222222 return it.bytes[it.i - cp_len .. it.i];
223223 }
......@@ -235,6 +235,38 @@ const Utf8Iterator = struct {
235235 }
236236};
237237
238pub const Utf16LeIterator = struct {
239 bytes: []const u8,
240 i: usize,
241
242 pub fn init(s: []const u16) Utf16LeIterator {
243 return Utf16LeIterator{
244 .bytes = @sliceToBytes(s),
245 .i = 0,
246 };
247 }
248
249 pub fn nextCodepoint(it: *Utf16LeIterator) !?u32 {
250 assert(it.i <= it.bytes.len);
251 if (it.i == it.bytes.len) return null;
252 const c0: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]);
253 if (c0 & ~u32(0x03ff) == 0xd800) {
254 // surrogate pair
255 it.i += 2;
256 if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf;
257 const c1: u32 = mem.readIntLE(u16, it.bytes[it.i .. it.i + 2]);
258 if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
259 it.i += 2;
260 return 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
261 } else if (c0 & ~u32(0x03ff) == 0xdc00) {
262 return error.UnexpectedSecondSurrogateHalf;
263 } else {
264 it.i += 2;
265 return c0;
266 }
267 }
268};
269
238270test "utf8 encode" {
239271 comptime testUtf8Encode() catch unreachable;
240272 try testUtf8Encode();
......@@ -445,42 +477,34 @@ fn testDecode(bytes: []const u8) !u32 {
445477 return utf8Decode(bytes);
446478}
447479
448// TODO: make this API on top of a non-allocating Utf16LeView
449pub fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
480/// Caller must free returned memory.
481pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
450482 var result = std.ArrayList(u8).init(allocator);
451483 // optimistically guess that it will all be ascii.
452484 try result.ensureCapacity(utf16le.len);
453
454 const utf16le_as_bytes = @sliceToBytes(utf16le);
455 var i: usize = 0;
456485 var out_index: usize = 0;
457 while (i < utf16le_as_bytes.len) : (i += 2) {
458 // decode
459 const c0: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
460 var codepoint: u32 = undefined;
461 if (c0 & ~u32(0x03ff) == 0xd800) {
462 // surrogate pair
463 i += 2;
464 if (i >= utf16le_as_bytes.len) return error.DanglingSurrogateHalf;
465 const c1: u32 = mem.readIntLE(u16, utf16le_as_bytes[i..i + 2]);
466 if (c1 & ~u32(0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
467 codepoint = 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
468 } else if (c0 & ~u32(0x03ff) == 0xdc00) {
469 return error.UnexpectedSecondSurrogateHalf;
470 } else {
471 codepoint = c0;
472 }
473
474 // encode
486 var it = Utf16LeIterator.init(utf16le);
487 while (try it.nextCodepoint()) |codepoint| {
475488 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;
476489 try result.resize(result.len + utf8_len);
477 _ = utf8Encode(codepoint, result.items[out_index..]) catch unreachable;
490 assert((utf8Encode(codepoint, result.items[out_index..]) catch unreachable) == utf8_len);
478491 out_index += utf8_len;
479492 }
480493
481494 return result.toOwnedSlice();
482495}
483496
497/// Asserts that the output buffer is big enough.
498/// Returns end byte index into utf8.
499pub fn utf16leToUtf8(utf8: []u8, utf16le: []const u16) !usize {
500 var end_index: usize = 0;
501 var it = Utf16LeIterator.init(utf16le);
502 while (try it.nextCodepoint()) |codepoint| {
503 end_index += try utf8Encode(codepoint, utf8[end_index..]);
504 }
505 return end_index;
506}
507
484508test "utf16leToUtf8" {
485509 var utf16le: [2]u16 = undefined;
486510 const utf16le_as_bytes = @sliceToBytes(utf16le[0..]);
......@@ -488,14 +512,14 @@ test "utf16leToUtf8" {
488512 {
489513 mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little);
490514 mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little);
491 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
515 const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le);
492516 assert(mem.eql(u8, utf8, "Aa"));
493517 }
494518
495519 {
496520 mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little);
497521 mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little);
498 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
522 const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le);
499523 assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf"));
500524 }
501525
......@@ -503,7 +527,7 @@ test "utf16leToUtf8" {
503527 // the values just outside the surrogate half range
504528 mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little);
505529 mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little);
506 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
530 const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le);
507531 assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80"));
508532 }
509533
......@@ -511,7 +535,7 @@ test "utf16leToUtf8" {
511535 // smallest surrogate pair
512536 mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little);
513537 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);
514 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
538 const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le);
515539 assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80"));
516540 }
517541
......@@ -519,14 +543,48 @@ test "utf16leToUtf8" {
519543 // largest surrogate pair
520544 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
521545 mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little);
522 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
546 const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le);
523547 assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf"));
524548 }
525549
526550 {
527551 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
528552 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);
529 const utf8 = try utf16leToUtf8(std.debug.global_allocator, utf16le);
553 const utf8 = try utf16leToUtf8Alloc(std.debug.global_allocator, utf16le);
530554 assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));
531555 }
532556}
557
558/// TODO support codepoints bigger than 16 bits
559/// TODO type for null terminated pointer
560pub fn utf8ToUtf16LeWithNull(allocator: *mem.Allocator, utf8: []const u8) ![]u16 {
561 var result = std.ArrayList(u16).init(allocator);
562 // optimistically guess that it will not require surrogate pairs
563 try result.ensureCapacity(utf8.len + 1);
564
565 const view = try Utf8View.init(utf8);
566 var it = view.iterator();
567 while (it.nextCodepoint()) |codepoint| {
568 try result.append(@intCast(u16, codepoint)); // TODO surrogate pairs
569 }
570
571 try result.append(0);
572 return result.toOwnedSlice();
573}
574
575/// Returns index of next character. If exact fit, returned index equals output slice length.
576/// If ran out of room, returned index equals output slice length + 1.
577/// TODO support codepoints bigger than 16 bits
578pub fn utf8ToUtf16Le(utf16le: []u16, utf8: []const u8) !usize {
579 const utf16le_as_bytes = @sliceToBytes(utf16le[0..]);
580 var end_index: usize = 0;
581
582 var it = (try Utf8View.init(utf8)).iterator();
583 while (it.nextCodepoint()) |codepoint| {
584 if (end_index == utf16le_as_bytes.len) return (end_index / 2) + 1;
585 // TODO surrogate pairs
586 mem.writeInt(utf16le_as_bytes[end_index..], @intCast(u16, codepoint), builtin.Endian.Little);
587 end_index += 2;
588 }
589 return end_index / 2;
590}
std/zig/ast.zig+112-106
......@@ -32,6 +32,12 @@ pub const Tree = struct {
3232 return self.source[token.start..token.end];
3333 }
3434
35 pub fn getNodeSource(self: *const Tree, node: *const Node) []const u8 {
36 const first_token = self.tokens.at(node.firstToken());
37 const last_token = self.tokens.at(node.lastToken());
38 return self.source[first_token.start..last_token.end];
39 }
40
3541 pub const Location = struct {
3642 line: usize,
3743 column: usize,
......@@ -338,7 +344,7 @@ pub const Node = struct {
338344 unreachable;
339345 }
340346
341 pub fn firstToken(base: *Node) TokenIndex {
347 pub fn firstToken(base: *const Node) TokenIndex {
342348 comptime var i = 0;
343349 inline while (i < @memberCount(Id)) : (i += 1) {
344350 if (base.id == @field(Id, @memberName(Id, i))) {
......@@ -349,7 +355,7 @@ pub const Node = struct {
349355 unreachable;
350356 }
351357
352 pub fn lastToken(base: *Node) TokenIndex {
358 pub fn lastToken(base: *const Node) TokenIndex {
353359 comptime var i = 0;
354360 inline while (i < @memberCount(Id)) : (i += 1) {
355361 if (base.id == @field(Id, @memberName(Id, i))) {
......@@ -473,11 +479,11 @@ pub const Node = struct {
473479 return null;
474480 }
475481
476 pub fn firstToken(self: *Root) TokenIndex {
482 pub fn firstToken(self: *const Root) TokenIndex {
477483 return if (self.decls.len == 0) self.eof_token else (self.decls.at(0).*).firstToken();
478484 }
479485
480 pub fn lastToken(self: *Root) TokenIndex {
486 pub fn lastToken(self: *const Root) TokenIndex {
481487 return if (self.decls.len == 0) self.eof_token else (self.decls.at(self.decls.len - 1).*).lastToken();
482488 }
483489 };
......@@ -518,7 +524,7 @@ pub const Node = struct {
518524 return null;
519525 }
520526
521 pub fn firstToken(self: *VarDecl) TokenIndex {
527 pub fn firstToken(self: *const VarDecl) TokenIndex {
522528 if (self.visib_token) |visib_token| return visib_token;
523529 if (self.comptime_token) |comptime_token| return comptime_token;
524530 if (self.extern_export_token) |extern_export_token| return extern_export_token;
......@@ -526,7 +532,7 @@ pub const Node = struct {
526532 return self.mut_token;
527533 }
528534
529 pub fn lastToken(self: *VarDecl) TokenIndex {
535 pub fn lastToken(self: *const VarDecl) TokenIndex {
530536 return self.semicolon_token;
531537 }
532538 };
......@@ -548,12 +554,12 @@ pub const Node = struct {
548554 return null;
549555 }
550556
551 pub fn firstToken(self: *Use) TokenIndex {
557 pub fn firstToken(self: *const Use) TokenIndex {
552558 if (self.visib_token) |visib_token| return visib_token;
553559 return self.use_token;
554560 }
555561
556 pub fn lastToken(self: *Use) TokenIndex {
562 pub fn lastToken(self: *const Use) TokenIndex {
557563 return self.semicolon_token;
558564 }
559565 };
......@@ -575,11 +581,11 @@ pub const Node = struct {
575581 return null;
576582 }
577583
578 pub fn firstToken(self: *ErrorSetDecl) TokenIndex {
584 pub fn firstToken(self: *const ErrorSetDecl) TokenIndex {
579585 return self.error_token;
580586 }
581587
582 pub fn lastToken(self: *ErrorSetDecl) TokenIndex {
588 pub fn lastToken(self: *const ErrorSetDecl) TokenIndex {
583589 return self.rbrace_token;
584590 }
585591 };
......@@ -618,14 +624,14 @@ pub const Node = struct {
618624 return null;
619625 }
620626
621 pub fn firstToken(self: *ContainerDecl) TokenIndex {
627 pub fn firstToken(self: *const ContainerDecl) TokenIndex {
622628 if (self.layout_token) |layout_token| {
623629 return layout_token;
624630 }
625631 return self.kind_token;
626632 }
627633
628 pub fn lastToken(self: *ContainerDecl) TokenIndex {
634 pub fn lastToken(self: *const ContainerDecl) TokenIndex {
629635 return self.rbrace_token;
630636 }
631637 };
......@@ -646,12 +652,12 @@ pub const Node = struct {
646652 return null;
647653 }
648654
649 pub fn firstToken(self: *StructField) TokenIndex {
655 pub fn firstToken(self: *const StructField) TokenIndex {
650656 if (self.visib_token) |visib_token| return visib_token;
651657 return self.name_token;
652658 }
653659
654 pub fn lastToken(self: *StructField) TokenIndex {
660 pub fn lastToken(self: *const StructField) TokenIndex {
655661 return self.type_expr.lastToken();
656662 }
657663 };
......@@ -679,11 +685,11 @@ pub const Node = struct {
679685 return null;
680686 }
681687
682 pub fn firstToken(self: *UnionTag) TokenIndex {
688 pub fn firstToken(self: *const UnionTag) TokenIndex {
683689 return self.name_token;
684690 }
685691
686 pub fn lastToken(self: *UnionTag) TokenIndex {
692 pub fn lastToken(self: *const UnionTag) TokenIndex {
687693 if (self.value_expr) |value_expr| {
688694 return value_expr.lastToken();
689695 }
......@@ -712,11 +718,11 @@ pub const Node = struct {
712718 return null;
713719 }
714720
715 pub fn firstToken(self: *EnumTag) TokenIndex {
721 pub fn firstToken(self: *const EnumTag) TokenIndex {
716722 return self.name_token;
717723 }
718724
719 pub fn lastToken(self: *EnumTag) TokenIndex {
725 pub fn lastToken(self: *const EnumTag) TokenIndex {
720726 if (self.value) |value| {
721727 return value.lastToken();
722728 }
......@@ -741,11 +747,11 @@ pub const Node = struct {
741747 return null;
742748 }
743749
744 pub fn firstToken(self: *ErrorTag) TokenIndex {
750 pub fn firstToken(self: *const ErrorTag) TokenIndex {
745751 return self.name_token;
746752 }
747753
748 pub fn lastToken(self: *ErrorTag) TokenIndex {
754 pub fn lastToken(self: *const ErrorTag) TokenIndex {
749755 return self.name_token;
750756 }
751757 };
......@@ -758,11 +764,11 @@ pub const Node = struct {
758764 return null;
759765 }
760766
761 pub fn firstToken(self: *Identifier) TokenIndex {
767 pub fn firstToken(self: *const Identifier) TokenIndex {
762768 return self.token;
763769 }
764770
765 pub fn lastToken(self: *Identifier) TokenIndex {
771 pub fn lastToken(self: *const Identifier) TokenIndex {
766772 return self.token;
767773 }
768774 };
......@@ -784,11 +790,11 @@ pub const Node = struct {
784790 return null;
785791 }
786792
787 pub fn firstToken(self: *AsyncAttribute) TokenIndex {
793 pub fn firstToken(self: *const AsyncAttribute) TokenIndex {
788794 return self.async_token;
789795 }
790796
791 pub fn lastToken(self: *AsyncAttribute) TokenIndex {
797 pub fn lastToken(self: *const AsyncAttribute) TokenIndex {
792798 if (self.rangle_bracket) |rangle_bracket| {
793799 return rangle_bracket;
794800 }
......@@ -856,7 +862,7 @@ pub const Node = struct {
856862 return null;
857863 }
858864
859 pub fn firstToken(self: *FnProto) TokenIndex {
865 pub fn firstToken(self: *const FnProto) TokenIndex {
860866 if (self.visib_token) |visib_token| return visib_token;
861867 if (self.async_attr) |async_attr| return async_attr.firstToken();
862868 if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
......@@ -865,7 +871,7 @@ pub const Node = struct {
865871 return self.fn_token;
866872 }
867873
868 pub fn lastToken(self: *FnProto) TokenIndex {
874 pub fn lastToken(self: *const FnProto) TokenIndex {
869875 if (self.body_node) |body_node| return body_node.lastToken();
870876 switch (self.return_type) {
871877 // TODO allow this and next prong to share bodies since the types are the same
......@@ -896,11 +902,11 @@ pub const Node = struct {
896902 return null;
897903 }
898904
899 pub fn firstToken(self: *PromiseType) TokenIndex {
905 pub fn firstToken(self: *const PromiseType) TokenIndex {
900906 return self.promise_token;
901907 }
902908
903 pub fn lastToken(self: *PromiseType) TokenIndex {
909 pub fn lastToken(self: *const PromiseType) TokenIndex {
904910 if (self.result) |result| return result.return_type.lastToken();
905911 return self.promise_token;
906912 }
......@@ -923,14 +929,14 @@ pub const Node = struct {
923929 return null;
924930 }
925931
926 pub fn firstToken(self: *ParamDecl) TokenIndex {
932 pub fn firstToken(self: *const ParamDecl) TokenIndex {
927933 if (self.comptime_token) |comptime_token| return comptime_token;
928934 if (self.noalias_token) |noalias_token| return noalias_token;
929935 if (self.name_token) |name_token| return name_token;
930936 return self.type_node.firstToken();
931937 }
932938
933 pub fn lastToken(self: *ParamDecl) TokenIndex {
939 pub fn lastToken(self: *const ParamDecl) TokenIndex {
934940 if (self.var_args_token) |var_args_token| return var_args_token;
935941 return self.type_node.lastToken();
936942 }
......@@ -954,7 +960,7 @@ pub const Node = struct {
954960 return null;
955961 }
956962
957 pub fn firstToken(self: *Block) TokenIndex {
963 pub fn firstToken(self: *const Block) TokenIndex {
958964 if (self.label) |label| {
959965 return label;
960966 }
......@@ -962,7 +968,7 @@ pub const Node = struct {
962968 return self.lbrace;
963969 }
964970
965 pub fn lastToken(self: *Block) TokenIndex {
971 pub fn lastToken(self: *const Block) TokenIndex {
966972 return self.rbrace;
967973 }
968974 };
......@@ -981,11 +987,11 @@ pub const Node = struct {
981987 return null;
982988 }
983989
984 pub fn firstToken(self: *Defer) TokenIndex {
990 pub fn firstToken(self: *const Defer) TokenIndex {
985991 return self.defer_token;
986992 }
987993
988 pub fn lastToken(self: *Defer) TokenIndex {
994 pub fn lastToken(self: *const Defer) TokenIndex {
989995 return self.expr.lastToken();
990996 }
991997 };
......@@ -1005,11 +1011,11 @@ pub const Node = struct {
10051011 return null;
10061012 }
10071013
1008 pub fn firstToken(self: *Comptime) TokenIndex {
1014 pub fn firstToken(self: *const Comptime) TokenIndex {
10091015 return self.comptime_token;
10101016 }
10111017
1012 pub fn lastToken(self: *Comptime) TokenIndex {
1018 pub fn lastToken(self: *const Comptime) TokenIndex {
10131019 return self.expr.lastToken();
10141020 }
10151021 };
......@@ -1029,11 +1035,11 @@ pub const Node = struct {
10291035 return null;
10301036 }
10311037
1032 pub fn firstToken(self: *Payload) TokenIndex {
1038 pub fn firstToken(self: *const Payload) TokenIndex {
10331039 return self.lpipe;
10341040 }
10351041
1036 pub fn lastToken(self: *Payload) TokenIndex {
1042 pub fn lastToken(self: *const Payload) TokenIndex {
10371043 return self.rpipe;
10381044 }
10391045 };
......@@ -1054,11 +1060,11 @@ pub const Node = struct {
10541060 return null;
10551061 }
10561062
1057 pub fn firstToken(self: *PointerPayload) TokenIndex {
1063 pub fn firstToken(self: *const PointerPayload) TokenIndex {
10581064 return self.lpipe;
10591065 }
10601066
1061 pub fn lastToken(self: *PointerPayload) TokenIndex {
1067 pub fn lastToken(self: *const PointerPayload) TokenIndex {
10621068 return self.rpipe;
10631069 }
10641070 };
......@@ -1085,11 +1091,11 @@ pub const Node = struct {
10851091 return null;
10861092 }
10871093
1088 pub fn firstToken(self: *PointerIndexPayload) TokenIndex {
1094 pub fn firstToken(self: *const PointerIndexPayload) TokenIndex {
10891095 return self.lpipe;
10901096 }
10911097
1092 pub fn lastToken(self: *PointerIndexPayload) TokenIndex {
1098 pub fn lastToken(self: *const PointerIndexPayload) TokenIndex {
10931099 return self.rpipe;
10941100 }
10951101 };
......@@ -1114,11 +1120,11 @@ pub const Node = struct {
11141120 return null;
11151121 }
11161122
1117 pub fn firstToken(self: *Else) TokenIndex {
1123 pub fn firstToken(self: *const Else) TokenIndex {
11181124 return self.else_token;
11191125 }
11201126
1121 pub fn lastToken(self: *Else) TokenIndex {
1127 pub fn lastToken(self: *const Else) TokenIndex {
11221128 return self.body.lastToken();
11231129 }
11241130 };
......@@ -1146,11 +1152,11 @@ pub const Node = struct {
11461152 return null;
11471153 }
11481154
1149 pub fn firstToken(self: *Switch) TokenIndex {
1155 pub fn firstToken(self: *const Switch) TokenIndex {
11501156 return self.switch_token;
11511157 }
11521158
1153 pub fn lastToken(self: *Switch) TokenIndex {
1159 pub fn lastToken(self: *const Switch) TokenIndex {
11541160 return self.rbrace;
11551161 }
11561162 };
......@@ -1181,11 +1187,11 @@ pub const Node = struct {
11811187 return null;
11821188 }
11831189
1184 pub fn firstToken(self: *SwitchCase) TokenIndex {
1190 pub fn firstToken(self: *const SwitchCase) TokenIndex {
11851191 return (self.items.at(0).*).firstToken();
11861192 }
11871193
1188 pub fn lastToken(self: *SwitchCase) TokenIndex {
1194 pub fn lastToken(self: *const SwitchCase) TokenIndex {
11891195 return self.expr.lastToken();
11901196 }
11911197 };
......@@ -1198,11 +1204,11 @@ pub const Node = struct {
11981204 return null;
11991205 }
12001206
1201 pub fn firstToken(self: *SwitchElse) TokenIndex {
1207 pub fn firstToken(self: *const SwitchElse) TokenIndex {
12021208 return self.token;
12031209 }
12041210
1205 pub fn lastToken(self: *SwitchElse) TokenIndex {
1211 pub fn lastToken(self: *const SwitchElse) TokenIndex {
12061212 return self.token;
12071213 }
12081214 };
......@@ -1245,7 +1251,7 @@ pub const Node = struct {
12451251 return null;
12461252 }
12471253
1248 pub fn firstToken(self: *While) TokenIndex {
1254 pub fn firstToken(self: *const While) TokenIndex {
12491255 if (self.label) |label| {
12501256 return label;
12511257 }
......@@ -1257,7 +1263,7 @@ pub const Node = struct {
12571263 return self.while_token;
12581264 }
12591265
1260 pub fn lastToken(self: *While) TokenIndex {
1266 pub fn lastToken(self: *const While) TokenIndex {
12611267 if (self.@"else") |@"else"| {
12621268 return @"else".body.lastToken();
12631269 }
......@@ -1298,7 +1304,7 @@ pub const Node = struct {
12981304 return null;
12991305 }
13001306
1301 pub fn firstToken(self: *For) TokenIndex {
1307 pub fn firstToken(self: *const For) TokenIndex {
13021308 if (self.label) |label| {
13031309 return label;
13041310 }
......@@ -1310,7 +1316,7 @@ pub const Node = struct {
13101316 return self.for_token;
13111317 }
13121318
1313 pub fn lastToken(self: *For) TokenIndex {
1319 pub fn lastToken(self: *const For) TokenIndex {
13141320 if (self.@"else") |@"else"| {
13151321 return @"else".body.lastToken();
13161322 }
......@@ -1349,11 +1355,11 @@ pub const Node = struct {
13491355 return null;
13501356 }
13511357
1352 pub fn firstToken(self: *If) TokenIndex {
1358 pub fn firstToken(self: *const If) TokenIndex {
13531359 return self.if_token;
13541360 }
13551361
1356 pub fn lastToken(self: *If) TokenIndex {
1362 pub fn lastToken(self: *const If) TokenIndex {
13571363 if (self.@"else") |@"else"| {
13581364 return @"else".body.lastToken();
13591365 }
......@@ -1480,11 +1486,11 @@ pub const Node = struct {
14801486 return null;
14811487 }
14821488
1483 pub fn firstToken(self: *InfixOp) TokenIndex {
1489 pub fn firstToken(self: *const InfixOp) TokenIndex {
14841490 return self.lhs.firstToken();
14851491 }
14861492
1487 pub fn lastToken(self: *InfixOp) TokenIndex {
1493 pub fn lastToken(self: *const InfixOp) TokenIndex {
14881494 return self.rhs.lastToken();
14891495 }
14901496 };
......@@ -1570,11 +1576,11 @@ pub const Node = struct {
15701576 return null;
15711577 }
15721578
1573 pub fn firstToken(self: *PrefixOp) TokenIndex {
1579 pub fn firstToken(self: *const PrefixOp) TokenIndex {
15741580 return self.op_token;
15751581 }
15761582
1577 pub fn lastToken(self: *PrefixOp) TokenIndex {
1583 pub fn lastToken(self: *const PrefixOp) TokenIndex {
15781584 return self.rhs.lastToken();
15791585 }
15801586 };
......@@ -1594,11 +1600,11 @@ pub const Node = struct {
15941600 return null;
15951601 }
15961602
1597 pub fn firstToken(self: *FieldInitializer) TokenIndex {
1603 pub fn firstToken(self: *const FieldInitializer) TokenIndex {
15981604 return self.period_token;
15991605 }
16001606
1601 pub fn lastToken(self: *FieldInitializer) TokenIndex {
1607 pub fn lastToken(self: *const FieldInitializer) TokenIndex {
16021608 return self.expr.lastToken();
16031609 }
16041610 };
......@@ -1673,7 +1679,7 @@ pub const Node = struct {
16731679 return null;
16741680 }
16751681
1676 pub fn firstToken(self: *SuffixOp) TokenIndex {
1682 pub fn firstToken(self: *const SuffixOp) TokenIndex {
16771683 switch (self.op) {
16781684 @TagType(Op).Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(),
16791685 else => {},
......@@ -1681,7 +1687,7 @@ pub const Node = struct {
16811687 return self.lhs.firstToken();
16821688 }
16831689
1684 pub fn lastToken(self: *SuffixOp) TokenIndex {
1690 pub fn lastToken(self: *const SuffixOp) TokenIndex {
16851691 return self.rtoken;
16861692 }
16871693 };
......@@ -1701,11 +1707,11 @@ pub const Node = struct {
17011707 return null;
17021708 }
17031709
1704 pub fn firstToken(self: *GroupedExpression) TokenIndex {
1710 pub fn firstToken(self: *const GroupedExpression) TokenIndex {
17051711 return self.lparen;
17061712 }
17071713
1708 pub fn lastToken(self: *GroupedExpression) TokenIndex {
1714 pub fn lastToken(self: *const GroupedExpression) TokenIndex {
17091715 return self.rparen;
17101716 }
17111717 };
......@@ -1749,11 +1755,11 @@ pub const Node = struct {
17491755 return null;
17501756 }
17511757
1752 pub fn firstToken(self: *ControlFlowExpression) TokenIndex {
1758 pub fn firstToken(self: *const ControlFlowExpression) TokenIndex {
17531759 return self.ltoken;
17541760 }
17551761
1756 pub fn lastToken(self: *ControlFlowExpression) TokenIndex {
1762 pub fn lastToken(self: *const ControlFlowExpression) TokenIndex {
17571763 if (self.rhs) |rhs| {
17581764 return rhs.lastToken();
17591765 }
......@@ -1792,11 +1798,11 @@ pub const Node = struct {
17921798 return null;
17931799 }
17941800
1795 pub fn firstToken(self: *Suspend) TokenIndex {
1801 pub fn firstToken(self: *const Suspend) TokenIndex {
17961802 return self.suspend_token;
17971803 }
17981804
1799 pub fn lastToken(self: *Suspend) TokenIndex {
1805 pub fn lastToken(self: *const Suspend) TokenIndex {
18001806 if (self.body) |body| {
18011807 return body.lastToken();
18021808 }
......@@ -1813,11 +1819,11 @@ pub const Node = struct {
18131819 return null;
18141820 }
18151821
1816 pub fn firstToken(self: *IntegerLiteral) TokenIndex {
1822 pub fn firstToken(self: *const IntegerLiteral) TokenIndex {
18171823 return self.token;
18181824 }
18191825
1820 pub fn lastToken(self: *IntegerLiteral) TokenIndex {
1826 pub fn lastToken(self: *const IntegerLiteral) TokenIndex {
18211827 return self.token;
18221828 }
18231829 };
......@@ -1830,11 +1836,11 @@ pub const Node = struct {
18301836 return null;
18311837 }
18321838
1833 pub fn firstToken(self: *FloatLiteral) TokenIndex {
1839 pub fn firstToken(self: *const FloatLiteral) TokenIndex {
18341840 return self.token;
18351841 }
18361842
1837 pub fn lastToken(self: *FloatLiteral) TokenIndex {
1843 pub fn lastToken(self: *const FloatLiteral) TokenIndex {
18381844 return self.token;
18391845 }
18401846 };
......@@ -1856,11 +1862,11 @@ pub const Node = struct {
18561862 return null;
18571863 }
18581864
1859 pub fn firstToken(self: *BuiltinCall) TokenIndex {
1865 pub fn firstToken(self: *const BuiltinCall) TokenIndex {
18601866 return self.builtin_token;
18611867 }
18621868
1863 pub fn lastToken(self: *BuiltinCall) TokenIndex {
1869 pub fn lastToken(self: *const BuiltinCall) TokenIndex {
18641870 return self.rparen_token;
18651871 }
18661872 };
......@@ -1873,11 +1879,11 @@ pub const Node = struct {
18731879 return null;
18741880 }
18751881
1876 pub fn firstToken(self: *StringLiteral) TokenIndex {
1882 pub fn firstToken(self: *const StringLiteral) TokenIndex {
18771883 return self.token;
18781884 }
18791885
1880 pub fn lastToken(self: *StringLiteral) TokenIndex {
1886 pub fn lastToken(self: *const StringLiteral) TokenIndex {
18811887 return self.token;
18821888 }
18831889 };
......@@ -1892,11 +1898,11 @@ pub const Node = struct {
18921898 return null;
18931899 }
18941900
1895 pub fn firstToken(self: *MultilineStringLiteral) TokenIndex {
1901 pub fn firstToken(self: *const MultilineStringLiteral) TokenIndex {
18961902 return self.lines.at(0).*;
18971903 }
18981904
1899 pub fn lastToken(self: *MultilineStringLiteral) TokenIndex {
1905 pub fn lastToken(self: *const MultilineStringLiteral) TokenIndex {
19001906 return self.lines.at(self.lines.len - 1).*;
19011907 }
19021908 };
......@@ -1909,11 +1915,11 @@ pub const Node = struct {
19091915 return null;
19101916 }
19111917
1912 pub fn firstToken(self: *CharLiteral) TokenIndex {
1918 pub fn firstToken(self: *const CharLiteral) TokenIndex {
19131919 return self.token;
19141920 }
19151921
1916 pub fn lastToken(self: *CharLiteral) TokenIndex {
1922 pub fn lastToken(self: *const CharLiteral) TokenIndex {
19171923 return self.token;
19181924 }
19191925 };
......@@ -1926,11 +1932,11 @@ pub const Node = struct {
19261932 return null;
19271933 }
19281934
1929 pub fn firstToken(self: *BoolLiteral) TokenIndex {
1935 pub fn firstToken(self: *const BoolLiteral) TokenIndex {
19301936 return self.token;
19311937 }
19321938
1933 pub fn lastToken(self: *BoolLiteral) TokenIndex {
1939 pub fn lastToken(self: *const BoolLiteral) TokenIndex {
19341940 return self.token;
19351941 }
19361942 };
......@@ -1943,11 +1949,11 @@ pub const Node = struct {
19431949 return null;
19441950 }
19451951
1946 pub fn firstToken(self: *NullLiteral) TokenIndex {
1952 pub fn firstToken(self: *const NullLiteral) TokenIndex {
19471953 return self.token;
19481954 }
19491955
1950 pub fn lastToken(self: *NullLiteral) TokenIndex {
1956 pub fn lastToken(self: *const NullLiteral) TokenIndex {
19511957 return self.token;
19521958 }
19531959 };
......@@ -1960,11 +1966,11 @@ pub const Node = struct {
19601966 return null;
19611967 }
19621968
1963 pub fn firstToken(self: *UndefinedLiteral) TokenIndex {
1969 pub fn firstToken(self: *const UndefinedLiteral) TokenIndex {
19641970 return self.token;
19651971 }
19661972
1967 pub fn lastToken(self: *UndefinedLiteral) TokenIndex {
1973 pub fn lastToken(self: *const UndefinedLiteral) TokenIndex {
19681974 return self.token;
19691975 }
19701976 };
......@@ -1977,11 +1983,11 @@ pub const Node = struct {
19771983 return null;
19781984 }
19791985
1980 pub fn firstToken(self: *ThisLiteral) TokenIndex {
1986 pub fn firstToken(self: *const ThisLiteral) TokenIndex {
19811987 return self.token;
19821988 }
19831989
1984 pub fn lastToken(self: *ThisLiteral) TokenIndex {
1990 pub fn lastToken(self: *const ThisLiteral) TokenIndex {
19851991 return self.token;
19861992 }
19871993 };
......@@ -2022,11 +2028,11 @@ pub const Node = struct {
20222028 return null;
20232029 }
20242030
2025 pub fn firstToken(self: *AsmOutput) TokenIndex {
2031 pub fn firstToken(self: *const AsmOutput) TokenIndex {
20262032 return self.lbracket;
20272033 }
20282034
2029 pub fn lastToken(self: *AsmOutput) TokenIndex {
2035 pub fn lastToken(self: *const AsmOutput) TokenIndex {
20302036 return self.rparen;
20312037 }
20322038 };
......@@ -2054,11 +2060,11 @@ pub const Node = struct {
20542060 return null;
20552061 }
20562062
2057 pub fn firstToken(self: *AsmInput) TokenIndex {
2063 pub fn firstToken(self: *const AsmInput) TokenIndex {
20582064 return self.lbracket;
20592065 }
20602066
2061 pub fn lastToken(self: *AsmInput) TokenIndex {
2067 pub fn lastToken(self: *const AsmInput) TokenIndex {
20622068 return self.rparen;
20632069 }
20642070 };
......@@ -2089,11 +2095,11 @@ pub const Node = struct {
20892095 return null;
20902096 }
20912097
2092 pub fn firstToken(self: *Asm) TokenIndex {
2098 pub fn firstToken(self: *const Asm) TokenIndex {
20932099 return self.asm_token;
20942100 }
20952101
2096 pub fn lastToken(self: *Asm) TokenIndex {
2102 pub fn lastToken(self: *const Asm) TokenIndex {
20972103 return self.rparen;
20982104 }
20992105 };
......@@ -2106,11 +2112,11 @@ pub const Node = struct {
21062112 return null;
21072113 }
21082114
2109 pub fn firstToken(self: *Unreachable) TokenIndex {
2115 pub fn firstToken(self: *const Unreachable) TokenIndex {
21102116 return self.token;
21112117 }
21122118
2113 pub fn lastToken(self: *Unreachable) TokenIndex {
2119 pub fn lastToken(self: *const Unreachable) TokenIndex {
21142120 return self.token;
21152121 }
21162122 };
......@@ -2123,11 +2129,11 @@ pub const Node = struct {
21232129 return null;
21242130 }
21252131
2126 pub fn firstToken(self: *ErrorType) TokenIndex {
2132 pub fn firstToken(self: *const ErrorType) TokenIndex {
21272133 return self.token;
21282134 }
21292135
2130 pub fn lastToken(self: *ErrorType) TokenIndex {
2136 pub fn lastToken(self: *const ErrorType) TokenIndex {
21312137 return self.token;
21322138 }
21332139 };
......@@ -2140,11 +2146,11 @@ pub const Node = struct {
21402146 return null;
21412147 }
21422148
2143 pub fn firstToken(self: *VarType) TokenIndex {
2149 pub fn firstToken(self: *const VarType) TokenIndex {
21442150 return self.token;
21452151 }
21462152
2147 pub fn lastToken(self: *VarType) TokenIndex {
2153 pub fn lastToken(self: *const VarType) TokenIndex {
21482154 return self.token;
21492155 }
21502156 };
......@@ -2159,11 +2165,11 @@ pub const Node = struct {
21592165 return null;
21602166 }
21612167
2162 pub fn firstToken(self: *DocComment) TokenIndex {
2168 pub fn firstToken(self: *const DocComment) TokenIndex {
21632169 return self.lines.at(0).*;
21642170 }
21652171
2166 pub fn lastToken(self: *DocComment) TokenIndex {
2172 pub fn lastToken(self: *const DocComment) TokenIndex {
21672173 return self.lines.at(self.lines.len - 1).*;
21682174 }
21692175 };
......@@ -2184,11 +2190,11 @@ pub const Node = struct {
21842190 return null;
21852191 }
21862192
2187 pub fn firstToken(self: *TestDecl) TokenIndex {
2193 pub fn firstToken(self: *const TestDecl) TokenIndex {
21882194 return self.test_token;
21892195 }
21902196
2191 pub fn lastToken(self: *TestDecl) TokenIndex {
2197 pub fn lastToken(self: *const TestDecl) TokenIndex {
21922198 return self.body_node.lastToken();
21932199 }
21942200 };
test/behavior.zig+1
......@@ -10,6 +10,7 @@ comptime {
1010 _ = @import("cases/bool.zig");
1111 _ = @import("cases/bugs/1111.zig");
1212 _ = @import("cases/bugs/1230.zig");
13 _ = @import("cases/bugs/1277.zig");
1314 _ = @import("cases/bugs/394.zig");
1415 _ = @import("cases/bugs/655.zig");
1516 _ = @import("cases/bugs/656.zig");
test/cases/bugs/1277.zig created+15
......@@ -0,0 +1,15 @@
1const std = @import("std");
2
3const S = struct {
4 f: ?fn () i32,
5};
6
7const s = S{ .f = f };
8
9fn f() i32 {
10 return 1234;
11}
12
13test "don't emit an LLVM global for a const function when it's in an optional in a struct" {
14 std.debug.assertOrPanic(s.f.?() == 1234);
15}
test/cases/cast.zig+11
......@@ -485,3 +485,14 @@ fn MakeType(comptime T: type) type {
485485 }
486486 };
487487}
488
489test "implicit cast from *[N]T to ?[*]T" {
490 var x: ?[*]u16 = null;
491 var y: [4]u16 = [4]u16 {0, 1, 2, 3};
492
493 x = &y;
494 assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
495 x.?[0] = 8;
496 y[3] = 6;
497 assert(std.mem.eql(u16, x.?[0..4], y[0..4]));
498}
\ No newline at end of file
test/cases/merge_error_sets.zig+2-2
......@@ -1,5 +1,5 @@
11const A = error{
2 PathNotFound,
2 FileNotFound,
33 NotDir,
44};
55const B = error{OutOfMemory};
......@@ -15,7 +15,7 @@ test "merge error sets" {
1515 @panic("unexpected");
1616 } else |err| switch (err) {
1717 error.OutOfMemory => @panic("unexpected"),
18 error.PathNotFound => @panic("unexpected"),
18 error.FileNotFound => @panic("unexpected"),
1919 error.NotDir => {},
2020 }
2121}
test/translate_c.zig+45
......@@ -1,6 +1,51 @@
11const tests = @import("tests.zig");
22
33pub fn addCases(cases: *tests.TranslateCContext) void {
4 cases.add("for loop with var init but empty body",
5 \\void foo(void) {
6 \\ for (int x = 0; x < 10; x++);
7 \\}
8 ,
9 \\pub fn foo() void {
10 \\ {
11 \\ var x: c_int = 0;
12 \\ while (x < 10) : (x += 1) {}
13 \\ }
14 \\}
15 );
16
17 cases.add("do while with empty body",
18 \\void foo(void) {
19 \\ do ; while (1);
20 \\}
21 , // TODO this should be if (1 != 0) break
22 \\pub fn foo() void {
23 \\ while (true) {
24 \\ if (!1) break;
25 \\ }
26 \\}
27 );
28
29 cases.add("for with empty body",
30 \\void foo(void) {
31 \\ for (;;);
32 \\}
33 ,
34 \\pub fn foo() void {
35 \\ while (true) {}
36 \\}
37 );
38
39 cases.add("while with empty body",
40 \\void foo(void) {
41 \\ while (1);
42 \\}
43 ,
44 \\pub fn foo() void {
45 \\ while (1 != 0) {}
46 \\}
47 );
48
449 cases.add("double define struct",
550 \\typedef struct Bar Bar;
651 \\typedef struct Foo Foo;