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...@@ -463,11 +463,14 @@ set(ZIG_STD_FILES
463 "empty.zig"463 "empty.zig"
464 "event.zig"464 "event.zig"
465 "event/channel.zig"465 "event/channel.zig"
466 "event/fs.zig"
466 "event/future.zig"467 "event/future.zig"
467 "event/group.zig"468 "event/group.zig"
468 "event/lock.zig"469 "event/lock.zig"
469 "event/locked.zig"470 "event/locked.zig"
470 "event/loop.zig"471 "event/loop.zig"
472 "event/rwlock.zig"
473 "event/rwlocked.zig"
471 "event/tcp.zig"474 "event/tcp.zig"
472 "fmt/errol/enum3.zig"475 "fmt/errol/enum3.zig"
473 "fmt/errol/index.zig"476 "fmt/errol/index.zig"
...@@ -556,6 +559,7 @@ set(ZIG_STD_FILES...@@ -556,6 +559,7 @@ set(ZIG_STD_FILES
556 "math/tanh.zig"559 "math/tanh.zig"
557 "math/trunc.zig"560 "math/trunc.zig"
558 "mem.zig"561 "mem.zig"
562 "mutex.zig"
559 "net.zig"563 "net.zig"
560 "os/child_process.zig"564 "os/child_process.zig"
561 "os/darwin.zig"565 "os/darwin.zig"
build.zig+1-1
...@@ -19,7 +19,7 @@ pub fn build(b: *Builder) !void {...@@ -19,7 +19,7 @@ pub fn build(b: *Builder) !void {
19 var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{19 var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{
20 docgen_exe.getOutputPath(),20 docgen_exe.getOutputPath(),
21 rel_zig_exe,21 rel_zig_exe,
22 "doc/langref.html.in",22 "doc" ++ os.path.sep_str ++ "langref.html.in",
23 os.path.join(b.allocator, b.cache_root, "langref.html") catch unreachable,23 os.path.join(b.allocator, b.cache_root, "langref.html") catch unreachable,
24 });24 });
25 docgen_cmd.step.dependOn(&docgen_exe.step);25 docgen_cmd.step.dependOn(&docgen_exe.step);
cmake/Findllvm.cmake+1-1
...@@ -8,7 +8,7 @@...@@ -8,7 +8,7 @@
8# LLVM_LIBDIRS8# LLVM_LIBDIRS
99
10find_program(LLVM_CONFIG_EXE10find_program(LLVM_CONFIG_EXE
11 NAMES llvm-config-7.0 llvm-config11 NAMES llvm-config llvm-config-7.0
12 PATHS12 PATHS
13 "/mingw64/bin"13 "/mingw64/bin"
14 "/c/msys64/mingw64/bin"14 "/c/msys64/mingw64/bin"
doc/docgen.zig+5-5
...@@ -34,10 +34,10 @@ pub fn main() !void {...@@ -34,10 +34,10 @@ pub fn main() !void {
34 const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg"));34 const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg"));
35 defer allocator.free(out_file_name);35 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);
38 defer in_file.close();38 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);
41 defer out_file.close();41 defer out_file.close();
4242
43 var file_in_stream = io.FileInStream.init(&in_file);43 var file_in_stream = io.FileInStream.init(&in_file);
...@@ -370,9 +370,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {...@@ -370,9 +370,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
370 .n = header_stack_size,370 .n = header_stack_size,
371 },371 },
372 });372 });
373 if (try urls.put(urlized, tag_token)) |other_tag_token| {373 if (try urls.put(urlized, tag_token)) |entry| {
374 parseError(tokenizer, tag_token, "duplicate header url: #{}", urlized) catch {};374 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 {};
376 return error.ParseError;376 return error.ParseError;
377 }377 }
378 if (last_action == Action.Open) {378 if (last_action == Action.Open) {
...@@ -738,7 +738,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -738,7 +738,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
738 try out.print("<pre><code class=\"zig\">{}</code></pre>", escaped_source);738 try out.print("<pre><code class=\"zig\">{}</code></pre>", escaped_source);
739 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);739 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
740 const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);740 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
743 switch (code.id) {743 switch (code.id) {
744 Code.Id.Exe => |expected_outcome| {744 Code.Id.Exe => |expected_outcome| {
doc/langref.html.in+151-75
...@@ -247,66 +247,6 @@ pub fn main() void {...@@ -247,66 +247,6 @@ pub fn main() void {
247 Description247 Description
248 </th>248 </th>
249 </tr>249 </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>
310 <tr>250 <tr>
311 <td><code>i8</code></td>251 <td><code>i8</code></td>
312 <td><code>int8_t</code></td>252 <td><code>int8_t</code></td>
...@@ -476,6 +416,11 @@ pub fn main() void {...@@ -476,6 +416,11 @@ pub fn main() void {
476 </tr>416 </tr>
477 </table>417 </table>
478 </div>418 </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>
479 {#see_also|Integers|Floats|void|Errors#}424 {#see_also|Integers|Floats|void|Errors#}
480 {#header_close#}425 {#header_close#}
481 {#header_open|Primitive Values#}426 {#header_open|Primitive Values#}
...@@ -744,19 +689,19 @@ const yet_another_hex_float = 0x103.70P-5;...@@ -744,19 +689,19 @@ const yet_another_hex_float = 0x103.70P-5;
744 {#code_end#}689 {#code_end#}
745 {#header_close#}690 {#header_close#}
746 {#header_open|Floating Point Operations#}691 {#header_open|Floating Point Operations#}
747 <p>By default floating point operations use <code>Optimized</code> mode,692 <p>By default floating point operations use <code>Strict</code> mode,
748 but you can switch to <code>Strict</code> mode on a per-block basis:</p>693 but you can switch to <code>Optimized</code> mode on a per-block basis:</p>
749 {#code_begin|obj|foo#}694 {#code_begin|obj|foo#}
750 {#code_release_fast#}695 {#code_release_fast#}
751const builtin = @import("builtin");696const builtin = @import("builtin");
752const big = f64(1 << 40);697const big = f64(1 << 40);
753698
754export fn foo_strict(x: f64) f64 {699export fn foo_strict(x: f64) f64 {
755 @setFloatMode(this, builtin.FloatMode.Strict);
756 return x + big - big;700 return x + big - big;
757}701}
758702
759export fn foo_optimized(x: f64) f64 {703export fn foo_optimized(x: f64) f64 {
704 @setFloatMode(this, builtin.FloatMode.Optimized);
760 return x + big - big;705 return x + big - big;
761}706}
762 {#code_end#}707 {#code_end#}
...@@ -809,6 +754,8 @@ a += b</code></pre></td>...@@ -809,6 +754,8 @@ a += b</code></pre></td>
809 <td>Addition.754 <td>Addition.
810 <ul>755 <ul>
811 <li>Can cause {#link|overflow|Default Operations#} for integers.</li>756 <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>
812 </ul>759 </ul>
813 </td>760 </td>
814 <td>761 <td>
...@@ -826,6 +773,8 @@ a +%= b</code></pre></td>...@@ -826,6 +773,8 @@ a +%= b</code></pre></td>
826 <td>Wrapping Addition.773 <td>Wrapping Addition.
827 <ul>774 <ul>
828 <li>Guaranteed to have twos-complement wrapping behavior.</li>775 <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>
829 </ul>778 </ul>
830 </td>779 </td>
831 <td>780 <td>
...@@ -844,6 +793,8 @@ a -= b</code></pre></td>...@@ -844,6 +793,8 @@ a -= b</code></pre></td>
844 <td>Subtraction.793 <td>Subtraction.
845 <ul>794 <ul>
846 <li>Can cause {#link|overflow|Default Operations#} for integers.</li>795 <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>
847 </ul>798 </ul>
848 </td>799 </td>
849 <td>800 <td>
...@@ -861,6 +812,8 @@ a -%= b</code></pre></td>...@@ -861,6 +812,8 @@ a -%= b</code></pre></td>
861 <td>Wrapping Subtraction.812 <td>Wrapping Subtraction.
862 <ul>813 <ul>
863 <li>Guaranteed to have twos-complement wrapping behavior.</li>814 <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>
864 </ul>817 </ul>
865 </td>818 </td>
866 <td>819 <td>
...@@ -914,6 +867,8 @@ a *= b</code></pre></td>...@@ -914,6 +867,8 @@ a *= b</code></pre></td>
914 <td>Multiplication.867 <td>Multiplication.
915 <ul>868 <ul>
916 <li>Can cause {#link|overflow|Default Operations#} for integers.</li>869 <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>
917 </ul>872 </ul>
918 </td>873 </td>
919 <td>874 <td>
...@@ -931,6 +886,8 @@ a *%= b</code></pre></td>...@@ -931,6 +886,8 @@ a *%= b</code></pre></td>
931 <td>Wrapping Multiplication.886 <td>Wrapping Multiplication.
932 <ul>887 <ul>
933 <li>Guaranteed to have twos-complement wrapping behavior.</li>888 <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>
934 </ul>891 </ul>
935 </td>892 </td>
936 <td>893 <td>
...@@ -956,6 +913,7 @@ a /= b</code></pre></td>...@@ -956,6 +913,7 @@ a /= b</code></pre></td>
956 {#link|@divFloor#}, or913 {#link|@divFloor#}, or
957 {#link|@divExact#} instead of <code>/</code>.914 {#link|@divExact#} instead of <code>/</code>.
958 </li>915 </li>
916 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
959 </ul>917 </ul>
960 </td>918 </td>
961 <td>919 <td>
...@@ -979,6 +937,7 @@ a %= b</code></pre></td>...@@ -979,6 +937,7 @@ a %= b</code></pre></td>
979 {#link|@rem#} or937 {#link|@rem#} or
980 {#link|@mod#} instead of <code>%</code>.938 {#link|@mod#} instead of <code>%</code>.
981 </li>939 </li>
940 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
982 </ul>941 </ul>
983 </td>942 </td>
984 <td>943 <td>
...@@ -995,6 +954,7 @@ a &lt;&lt;= b</code></pre></td>...@@ -995,6 +954,7 @@ a &lt;&lt;= b</code></pre></td>
995 </td>954 </td>
996 <td>Bit Shift Left.955 <td>Bit Shift Left.
997 <ul>956 <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>
998 <li>See also {#link|@shlExact#}.</li>958 <li>See also {#link|@shlExact#}.</li>
999 <li>See also {#link|@shlWithOverflow#}.</li>959 <li>See also {#link|@shlWithOverflow#}.</li>
1000 </ul>960 </ul>
...@@ -1013,6 +973,7 @@ a &gt;&gt;= b</code></pre></td>...@@ -1013,6 +973,7 @@ a &gt;&gt;= b</code></pre></td>
1013 </td>973 </td>
1014 <td>Bit Shift Right.974 <td>Bit Shift Right.
1015 <ul>975 <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>
1016 <li>See also {#link|@shrExact#}.</li>977 <li>See also {#link|@shrExact#}.</li>
1017 </ul>978 </ul>
1018 </td>979 </td>
...@@ -1029,6 +990,9 @@ a &amp;= b</code></pre></td>...@@ -1029,6 +990,9 @@ a &amp;= b</code></pre></td>
1029 </ul>990 </ul>
1030 </td>991 </td>
1031 <td>Bitwise AND.992 <td>Bitwise AND.
993 <ul>
994 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
995 </ul>
1032 </td>996 </td>
1033 <td>997 <td>
1034 <pre><code class="zig">0b011 &amp; 0b101 == 0b001</code></pre>998 <pre><code class="zig">0b011 &amp; 0b101 == 0b001</code></pre>
...@@ -1043,6 +1007,9 @@ a |= b</code></pre></td>...@@ -1043,6 +1007,9 @@ a |= b</code></pre></td>
1043 </ul>1007 </ul>
1044 </td>1008 </td>
1045 <td>Bitwise OR.1009 <td>Bitwise OR.
1010 <ul>
1011 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
1012 </ul>
1046 </td>1013 </td>
1047 <td>1014 <td>
1048 <pre><code class="zig">0b010 | 0b100 == 0b110</code></pre>1015 <pre><code class="zig">0b010 | 0b100 == 0b110</code></pre>
...@@ -1057,6 +1024,9 @@ a ^= b</code></pre></td>...@@ -1057,6 +1024,9 @@ a ^= b</code></pre></td>
1057 </ul>1024 </ul>
1058 </td>1025 </td>
1059 <td>Bitwise XOR.1026 <td>Bitwise XOR.
1027 <ul>
1028 <li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
1029 </ul>
1060 </td>1030 </td>
1061 <td>1031 <td>
1062 <pre><code class="zig">0b011 ^ 0b101 == 0b110</code></pre>1032 <pre><code class="zig">0b011 ^ 0b101 == 0b110</code></pre>
...@@ -1186,6 +1156,7 @@ unwrapped == 1234</code></pre>...@@ -1186,6 +1156,7 @@ unwrapped == 1234</code></pre>
1186 </td>1156 </td>
1187 <td>1157 <td>
1188 Returns <code>true</code> if a and b are equal, otherwise returns <code>false</code>.1158 Returns <code>true</code> if a and b are equal, otherwise returns <code>false</code>.
1159 Invokes {#link|Peer Type Resolution#} for the operands.
1189 </td>1160 </td>
1190 <td>1161 <td>
1191 <pre><code class="zig">(1 == 1) == true</code></pre>1162 <pre><code class="zig">(1 == 1) == true</code></pre>
...@@ -1218,6 +1189,7 @@ value == null</code></pre>...@@ -1218,6 +1189,7 @@ value == null</code></pre>
1218 </td>1189 </td>
1219 <td>1190 <td>
1220 Returns <code>false</code> if a and b are equal, otherwise returns <code>true</code>.1191 Returns <code>false</code> if a and b are equal, otherwise returns <code>true</code>.
1192 Invokes {#link|Peer Type Resolution#} for the operands.
1221 </td>1193 </td>
1222 <td>1194 <td>
1223 <pre><code class="zig">(1 != 1) == false</code></pre>1195 <pre><code class="zig">(1 != 1) == false</code></pre>
...@@ -1233,6 +1205,7 @@ value == null</code></pre>...@@ -1233,6 +1205,7 @@ value == null</code></pre>
1233 </td>1205 </td>
1234 <td>1206 <td>
1235 Returns <code>true</code> if a is greater than b, otherwise returns <code>false</code>.1207 Returns <code>true</code> if a is greater than b, otherwise returns <code>false</code>.
1208 Invokes {#link|Peer Type Resolution#} for the operands.
1236 </td>1209 </td>
1237 <td>1210 <td>
1238 <pre><code class="zig">(2 &gt; 1) == true</code></pre>1211 <pre><code class="zig">(2 &gt; 1) == true</code></pre>
...@@ -1248,6 +1221,7 @@ value == null</code></pre>...@@ -1248,6 +1221,7 @@ value == null</code></pre>
1248 </td>1221 </td>
1249 <td>1222 <td>
1250 Returns <code>true</code> if a is greater than or equal to b, otherwise returns <code>false</code>.1223 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.
1251 </td>1225 </td>
1252 <td>1226 <td>
1253 <pre><code class="zig">(2 &gt;= 1) == true</code></pre>1227 <pre><code class="zig">(2 &gt;= 1) == true</code></pre>
...@@ -1263,6 +1237,7 @@ value == null</code></pre>...@@ -1263,6 +1237,7 @@ value == null</code></pre>
1263 </td>1237 </td>
1264 <td>1238 <td>
1265 Returns <code>true</code> if a is less than b, otherwise returns <code>false</code>.1239 Returns <code>true</code> if a is less than b, otherwise returns <code>false</code>.
1240 Invokes {#link|Peer Type Resolution#} for the operands.
1266 </td>1241 </td>
1267 <td>1242 <td>
1268 <pre><code class="zig">(1 &lt; 2) == true</code></pre>1243 <pre><code class="zig">(1 &lt; 2) == true</code></pre>
...@@ -1278,6 +1253,7 @@ value == null</code></pre>...@@ -1278,6 +1253,7 @@ value == null</code></pre>
1278 </td>1253 </td>
1279 <td>1254 <td>
1280 Returns <code>true</code> if a is less than or equal to b, otherwise returns <code>false</code>.1255 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.
1281 </td>1257 </td>
1282 <td>1258 <td>
1283 <pre><code class="zig">(1 &lt;= 2) == true</code></pre>1259 <pre><code class="zig">(1 &lt;= 2) == true</code></pre>
...@@ -3807,6 +3783,7 @@ test "float widening" {...@@ -3807,6 +3783,7 @@ test "float widening" {
3807 <p>TODO: [N]T to ?[]const T</p>3783 <p>TODO: [N]T to ?[]const T</p>
3808 <p>TODO: *[N]T to []T</p>3784 <p>TODO: *[N]T to []T</p>
3809 <p>TODO: *[N]T to [*]T</p>3785 <p>TODO: *[N]T to [*]T</p>
3786 <p>TODO: *[N]T to ?[*]T</p>
3810 <p>TODO: *T to *[1]T</p>3787 <p>TODO: *T to *[1]T</p>
3811 <p>TODO: [N]T to E![]const T</p>3788 <p>TODO: [N]T to E![]const T</p>
3812 {#header_close#}3789 {#header_close#}
...@@ -3877,7 +3854,106 @@ test "float widening" {...@@ -3877,7 +3854,106 @@ test "float widening" {
3877 {#header_close#}3854 {#header_close#}
38783855
3879 {#header_open|Peer Type Resolution#}3856 {#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#}
3881 {#header_close#}3957 {#header_close#}
3882 {#header_close#}3958 {#header_close#}
38833959
...@@ -4705,10 +4781,7 @@ async fn testSuspendBlock() void {...@@ -4705,10 +4781,7 @@ async fn testSuspendBlock() void {
4705 <p>4781 <p>
4706 {#link|Await#} counts as a suspend point.4782 {#link|Await#} counts as a suspend point.
4707 </p>4783 </p>
4708 {#header_open|Breaking from Suspend Blocks#}4784 {#header_open|Resuming from Suspend Blocks#}
4709 <p>
4710 Suspend blocks support labeled break, just like {#link|while#} and {#link|for#}.
4711 </p>
4712 <p>4785 <p>
4713 Upon entering a <code>suspend</code> block, the coroutine is already considered4786 Upon entering a <code>suspend</code> block, the coroutine is already considered
4714 suspended, and can be resumed. For example, if you started another kernel thread,4787 suspended, and can be resumed. For example, if you started another kernel thread,
...@@ -4741,6 +4814,9 @@ async fn testResumeFromSuspend(my_result: *i32) void {...@@ -4741,6 +4814,9 @@ async fn testResumeFromSuspend(my_result: *i32) void {
4741 my_result.* += 1;4814 my_result.* += 1;
4742}4815}
4743 {#code_end#}4816 {#code_end#}
4817 <p>
4818 This is guaranteed to be a tail call, and therefore will not cause a new stack frame.
4819 </p>
4744 {#header_close#}4820 {#header_close#}
4745 {#header_close#}4821 {#header_close#}
4746 {#header_open|Await#}4822 {#header_open|Await#}
...@@ -5527,7 +5603,7 @@ fn add(a: i32, b: i32) i32 { return a + b; }...@@ -5527,7 +5603,7 @@ fn add(a: i32, b: i32) i32 { return a + b; }
5527 <p>Returns the field type of a struct or union.</p>5603 <p>Returns the field type of a struct or union.</p>
5528 {#header_close#}5604 {#header_close#}
5529 {#header_open|@memcpy#}5605 {#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>
5531 <p>5607 <p>
5532 This function copies bytes from one region of memory to another. <code>dest</code> and5608 This function copies bytes from one region of memory to another. <code>dest</code> and
5533 <code>source</code> are both pointers and must not overlap.5609 <code>source</code> are both pointers and must not overlap.
...@@ -5545,7 +5621,7 @@ fn add(a: i32, b: i32) i32 { return a + b; }...@@ -5545,7 +5621,7 @@ fn add(a: i32, b: i32) i32 { return a + b; }
5545mem.copy(u8, dest[0...byte_count], source[0...byte_count]);</code></pre>5621mem.copy(u8, dest[0...byte_count], source[0...byte_count]);</code></pre>
5546 {#header_close#}5622 {#header_close#}
5547 {#header_open|@memset#}5623 {#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>
5549 <p>5625 <p>
5550 This function sets a region of memory to <code>c</code>. <code>dest</code> is a pointer.5626 This function sets a region of memory to <code>c</code>. <code>dest</code> is a pointer.
5551 </p>5627 </p>
...@@ -5817,7 +5893,7 @@ pub const FloatMode = enum {...@@ -5817,7 +5893,7 @@ pub const FloatMode = enum {
5817 {#code_end#}5893 {#code_end#}
5818 <ul>5894 <ul>
5819 <li>5895 <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:
5821 <ul>5897 <ul>
5822 <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>5898 <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>
5823 <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>5899 <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 {...@@ -5829,7 +5905,7 @@ pub const FloatMode = enum {
5829 This is equivalent to <code>-ffast-math</code> in GCC.5905 This is equivalent to <code>-ffast-math</code> in GCC.
5830 </li>5906 </li>
5831 <li>5907 <li>
5832 <code>Strict</code> - Floating point operations follow strict IEEE compliance.5908 <code>Strict</code> (default) - Floating point operations follow strict IEEE compliance.
5833 </li>5909 </li>
5834 </ul>5910 </ul>
5835 {#see_also|Floating Point Operations#}5911 {#see_also|Floating Point Operations#}
...@@ -6035,7 +6111,7 @@ pub const TypeInfo = union(TypeId) {...@@ -6035,7 +6111,7 @@ pub const TypeInfo = union(TypeId) {
6035 size: Size,6111 size: Size,
6036 is_const: bool,6112 is_const: bool,
6037 is_volatile: bool,6113 is_volatile: bool,
6038 alignment: u32,6114 alignment: u29,
6039 child: type,6115 child: type,
60406116
6041 pub const Size = enum {6117 pub const Size = enum {
...@@ -7543,8 +7619,8 @@ hljs.registerLanguage("zig", function(t) {...@@ -7543,8 +7619,8 @@ hljs.registerLanguage("zig", function(t) {
7543 },7619 },
7544 a = t.IR + "\\s*\\(",7620 a = t.IR + "\\s*\\(",
7545 c = {7621 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",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",
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",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",
7548 literal: "true false null undefined"7624 literal: "true false null undefined"
7549 },7625 },
7550 n = [e, t.CLCM, t.CBCM, s, r];7626 n = [e, t.CLCM, t.CBCM, s, r];
example/cat/main.zig+1-1
...@@ -20,7 +20,7 @@ pub fn main() !void {...@@ -20,7 +20,7 @@ pub fn main() !void {
20 } else if (arg[0] == '-') {20 } else if (arg[0] == '-') {
21 return usage(exe);21 return usage(exe);
22 } else {22 } else {
23 var file = os.File.openRead(allocator, arg) catch |err| {23 var file = os.File.openRead(arg) catch |err| {
24 warn("Unable to open file: {}\n", @errorName(err));24 warn("Unable to open file: {}\n", @errorName(err));
25 return err;25 return err;
26 };26 };
example/shared_library/mathtest.zig+9
...@@ -1,3 +1,12 @@...@@ -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
1export fn add(a: i32, b: i32) i32 {10export fn add(a: i32, b: i32) i32 {
2 return a + b;11 return a + b;
3}12}
src-self-hosted/codegen.zig+2-2
...@@ -19,8 +19,8 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)...@@ -19,8 +19,8 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
19 var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);19 var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
20 errdefer output_path.deinit();20 errdefer output_path.deinit();
2121
22 const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();22 const llvm_handle = try comp.zig_compiler.getAnyLlvmContext();
23 defer llvm_handle.release(comp.event_loop_local);23 defer llvm_handle.release(comp.zig_compiler);
2424
25 const context = llvm_handle.node.data;25 const context = llvm_handle.node.data;
2626
src-self-hosted/compilation.zig+347-183
...@@ -30,9 +30,12 @@ const Package = @import("package.zig").Package;...@@ -30,9 +30,12 @@ const Package = @import("package.zig").Package;
30const link = @import("link.zig").link;30const link = @import("link.zig").link;
31const LibCInstallation = @import("libc_installation.zig").LibCInstallation;31const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
32const CInt = @import("c_int.zig").CInt;32const CInt = @import("c_int.zig").CInt;
33const fs = event.fs;
34
35const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
3336
34/// Data that is local to the event loop.37/// Data that is local to the event loop.
35pub const EventLoopLocal = struct {38pub const ZigCompiler = struct {
36 loop: *event.Loop,39 loop: *event.Loop,
37 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),40 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
38 lld_lock: event.Lock,41 lld_lock: event.Lock,
...@@ -44,7 +47,7 @@ pub const EventLoopLocal = struct {...@@ -44,7 +47,7 @@ pub const EventLoopLocal = struct {
4447
45 var lazy_init_targets = std.lazyInit(void);48 var lazy_init_targets = std.lazyInit(void);
4649
47 fn init(loop: *event.Loop) !EventLoopLocal {50 fn init(loop: *event.Loop) !ZigCompiler {
48 lazy_init_targets.get() orelse {51 lazy_init_targets.get() orelse {
49 Target.initializeAll();52 Target.initializeAll();
50 lazy_init_targets.resolve();53 lazy_init_targets.resolve();
...@@ -54,7 +57,7 @@ pub const EventLoopLocal = struct {...@@ -54,7 +57,7 @@ pub const EventLoopLocal = struct {
54 try std.os.getRandomBytes(seed_bytes[0..]);57 try std.os.getRandomBytes(seed_bytes[0..]);
55 const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);58 const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
5659
57 return EventLoopLocal{60 return ZigCompiler{
58 .loop = loop,61 .loop = loop,
59 .lld_lock = event.Lock.init(loop),62 .lld_lock = event.Lock.init(loop),
60 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),63 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
...@@ -64,7 +67,7 @@ pub const EventLoopLocal = struct {...@@ -64,7 +67,7 @@ pub const EventLoopLocal = struct {
64 }67 }
6568
66 /// Must be called only after EventLoop.run completes.69 /// Must be called only after EventLoop.run completes.
67 fn deinit(self: *EventLoopLocal) void {70 fn deinit(self: *ZigCompiler) void {
68 self.lld_lock.deinit();71 self.lld_lock.deinit();
69 while (self.llvm_handle_pool.pop()) |node| {72 while (self.llvm_handle_pool.pop()) |node| {
70 c.LLVMContextDispose(node.data);73 c.LLVMContextDispose(node.data);
...@@ -74,7 +77,7 @@ pub const EventLoopLocal = struct {...@@ -74,7 +77,7 @@ pub const EventLoopLocal = struct {
7477
75 /// Gets an exclusive handle on any LlvmContext.78 /// Gets an exclusive handle on any LlvmContext.
76 /// Caller must release the handle when done.79 /// Caller must release the handle when done.
77 pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle {80 pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle {
78 if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };81 if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node };
7982
80 const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;83 const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory;
...@@ -89,24 +92,36 @@ pub const EventLoopLocal = struct {...@@ -89,24 +92,36 @@ pub const EventLoopLocal = struct {
89 return LlvmHandle{ .node = node };92 return LlvmHandle{ .node = node };
90 }93 }
9194
92 pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation {95 pub async fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
93 if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr;96 if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr;
94 try await (async self.native_libc.data.findNative(self.loop) catch unreachable);97 try await (async self.native_libc.data.findNative(self.loop) catch unreachable);
95 self.native_libc.resolve();98 self.native_libc.resolve();
96 return &self.native_libc.data;99 return &self.native_libc.data;
97 }100 }
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 }
98};113};
99114
100pub const LlvmHandle = struct {115pub const LlvmHandle = struct {
101 node: *std.atomic.Stack(llvm.ContextRef).Node,116 node: *std.atomic.Stack(llvm.ContextRef).Node,
102117
103 pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void {118 pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void {
104 event_loop_local.llvm_handle_pool.push(self.node);119 zig_compiler.llvm_handle_pool.push(self.node);
105 }120 }
106};121};
107122
108pub const Compilation = struct {123pub const Compilation = struct {
109 event_loop_local: *EventLoopLocal,124 zig_compiler: *ZigCompiler,
110 loop: *event.Loop,125 loop: *event.Loop,
111 name: Buffer,126 name: Buffer,
112 llvm_triple: Buffer,127 llvm_triple: Buffer,
...@@ -134,7 +149,6 @@ pub const Compilation = struct {...@@ -134,7 +149,6 @@ pub const Compilation = struct {
134 linker_rdynamic: bool,149 linker_rdynamic: bool,
135150
136 clang_argv: []const []const u8,151 clang_argv: []const []const u8,
137 llvm_argv: []const []const u8,
138 lib_dirs: []const []const u8,152 lib_dirs: []const []const u8,
139 rpath_list: []const []const u8,153 rpath_list: []const []const u8,
140 assembly_files: []const []const u8,154 assembly_files: []const []const u8,
...@@ -214,6 +228,8 @@ pub const Compilation = struct {...@@ -214,6 +228,8 @@ pub const Compilation = struct {
214 deinit_group: event.Group(void),228 deinit_group: event.Group(void),
215229
216 destroy_handle: promise,230 destroy_handle: promise,
231 main_loop_handle: promise,
232 main_loop_future: event.Future(void),
217233
218 have_err_ret_tracing: bool,234 have_err_ret_tracing: bool,
219235
...@@ -227,6 +243,8 @@ pub const Compilation = struct {...@@ -227,6 +243,8 @@ pub const Compilation = struct {
227243
228 c_int_types: [CInt.list.len]*Type.Int,244 c_int_types: [CInt.list.len]*Type.Int,
229245
246 fs_watch: *fs.Watch(*Scope.Root),
247
230 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);248 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
231 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);249 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
232 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);250 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 {...@@ -239,8 +257,6 @@ pub const Compilation = struct {
239 pub const BuildError = error{257 pub const BuildError = error{
240 OutOfMemory,258 OutOfMemory,
241 EndOfStream,259 EndOfStream,
242 BadFd,
243 Io,
244 IsDir,260 IsDir,
245 Unexpected,261 Unexpected,
246 SystemResources,262 SystemResources,
...@@ -255,7 +271,6 @@ pub const Compilation = struct {...@@ -255,7 +271,6 @@ pub const Compilation = struct {
255 NameTooLong,271 NameTooLong,
256 SystemFdQuotaExceeded,272 SystemFdQuotaExceeded,
257 NoDevice,273 NoDevice,
258 PathNotFound,
259 NoSpaceLeft,274 NoSpaceLeft,
260 NotDir,275 NotDir,
261 FileSystem,276 FileSystem,
...@@ -282,6 +297,9 @@ pub const Compilation = struct {...@@ -282,6 +297,9 @@ pub const Compilation = struct {
282 LibCMissingDynamicLinker,297 LibCMissingDynamicLinker,
283 InvalidDarwinVersionString,298 InvalidDarwinVersionString,
284 UnsupportedLinkArchitecture,299 UnsupportedLinkArchitecture,
300 UserResourceLimitReached,
301 InvalidUtf8,
302 BadPathName,
285 };303 };
286304
287 pub const Event = union(enum) {305 pub const Event = union(enum) {
...@@ -318,7 +336,7 @@ pub const Compilation = struct {...@@ -318,7 +336,7 @@ pub const Compilation = struct {
318 };336 };
319337
320 pub fn create(338 pub fn create(
321 event_loop_local: *EventLoopLocal,339 zig_compiler: *ZigCompiler,
322 name: []const u8,340 name: []const u8,
323 root_src_path: ?[]const u8,341 root_src_path: ?[]const u8,
324 target: Target,342 target: Target,
...@@ -327,11 +345,45 @@ pub const Compilation = struct {...@@ -327,11 +345,45 @@ pub const Compilation = struct {
327 is_static: bool,345 is_static: bool,
328 zig_lib_dir: []const u8,346 zig_lib_dir: []const u8,
329 ) !*Compilation {347 ) !*Compilation {
330 const loop = event_loop_local.loop;348 var optional_comp: ?*Compilation = null;
331 const comp = try event_loop_local.loop.allocator.create(Compilation{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{
332 .loop = loop,384 .loop = loop,
333 .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),385 .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator),
334 .event_loop_local = event_loop_local,386 .zig_compiler = zig_compiler,
335 .events = undefined,387 .events = undefined,
336 .root_src_path = root_src_path,388 .root_src_path = root_src_path,
337 .target = target,389 .target = target,
...@@ -341,6 +393,9 @@ pub const Compilation = struct {...@@ -341,6 +393,9 @@ pub const Compilation = struct {
341 .zig_lib_dir = zig_lib_dir,393 .zig_lib_dir = zig_lib_dir,
342 .zig_std_dir = undefined,394 .zig_std_dir = undefined,
343 .tmp_dir = event.Future(BuildError![]u8).init(loop),395 .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
345 .name = undefined,400 .name = undefined,
346 .llvm_triple = undefined,401 .llvm_triple = undefined,
...@@ -365,7 +420,6 @@ pub const Compilation = struct {...@@ -365,7 +420,6 @@ pub const Compilation = struct {
365 .is_static = is_static,420 .is_static = is_static,
366 .linker_rdynamic = false,421 .linker_rdynamic = false,
367 .clang_argv = [][]const u8{},422 .clang_argv = [][]const u8{},
368 .llvm_argv = [][]const u8{},
369 .lib_dirs = [][]const u8{},423 .lib_dirs = [][]const u8{},
370 .rpath_list = [][]const u8{},424 .rpath_list = [][]const u8{},
371 .assembly_files = [][]const u8{},425 .assembly_files = [][]const u8{},
...@@ -412,25 +466,26 @@ pub const Compilation = struct {...@@ -412,25 +466,26 @@ pub const Compilation = struct {
412 .std_package = undefined,466 .std_package = undefined,
413467
414 .override_libc = null,468 .override_libc = null,
415 .destroy_handle = undefined,
416 .have_err_ret_tracing = false,469 .have_err_ret_tracing = false,
417 .primitive_type_table = undefined,470 .primitive_type_table = undefined,
418 });471
419 errdefer {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 {
420 comp.int_type_table.private_data.deinit();478 comp.int_type_table.private_data.deinit();
421 comp.array_type_table.private_data.deinit();479 comp.array_type_table.private_data.deinit();
422 comp.ptr_type_table.private_data.deinit();480 comp.ptr_type_table.private_data.deinit();
423 comp.fn_type_table.private_data.deinit();481 comp.fn_type_table.private_data.deinit();
424 comp.arena_allocator.deinit();482 comp.arena_allocator.deinit();
425 comp.loop.allocator.destroy(comp);
426 }483 }
427484
428 comp.name = try Buffer.init(comp.arena(), name);485 comp.name = try Buffer.init(comp.arena(), name);
429 comp.llvm_triple = try target.getTriple(comp.arena());486 comp.llvm_triple = try target.getTriple(comp.arena());
430 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);487 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
431 comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
432 comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");488 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
435 const opt_level = switch (build_mode) {490 const opt_level = switch (build_mode) {
436 builtin.Mode.Debug => llvm.CodeGenLevelNone,491 builtin.Mode.Debug => llvm.CodeGenLevelNone,
...@@ -444,8 +499,8 @@ pub const Compilation = struct {...@@ -444,8 +499,8 @@ pub const Compilation = struct {
444 // As a workaround we do not use target native features on Windows.499 // As a workaround we do not use target native features on Windows.
445 var target_specific_cpu_args: ?[*]u8 = null;500 var target_specific_cpu_args: ?[*]u8 = null;
446 var target_specific_cpu_features: ?[*]u8 = null;501 var target_specific_cpu_features: ?[*]u8 = null;
447 errdefer llvm.DisposeMessage(target_specific_cpu_args);502 defer llvm.DisposeMessage(target_specific_cpu_args);
448 errdefer llvm.DisposeMessage(target_specific_cpu_features);503 defer llvm.DisposeMessage(target_specific_cpu_features);
449 if (target == Target.Native and !target.isWindows()) {504 if (target == Target.Native and !target.isWindows()) {
450 target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory;505 target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory;
451 target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory;506 target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory;
...@@ -460,16 +515,16 @@ pub const Compilation = struct {...@@ -460,16 +515,16 @@ pub const Compilation = struct {
460 reloc_mode,515 reloc_mode,
461 llvm.CodeModelDefault,516 llvm.CodeModelDefault,
462 ) orelse return error.OutOfMemory;517 ) orelse return error.OutOfMemory;
463 errdefer llvm.DisposeTargetMachine(comp.target_machine);518 defer llvm.DisposeTargetMachine(comp.target_machine);
464519
465 comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory;520 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
468 comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory;523 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
471 comp.events = try event.Channel(Event).create(comp.loop, 0);526 comp.events = try event.Channel(Event).create(comp.loop, 0);
472 errdefer comp.events.destroy();527 defer comp.events.destroy();
473528
474 if (root_src_path) |root_src| {529 if (root_src_path) |root_src| {
475 const dirname = std.os.path.dirname(root_src) orelse ".";530 const dirname = std.os.path.dirname(root_src) orelse ".";
...@@ -482,11 +537,27 @@ pub const Compilation = struct {...@@ -482,11 +537,27 @@ pub const Compilation = struct {
482 comp.root_package = try Package.create(comp.arena(), ".", "");537 comp.root_package = try Package.create(comp.arena(), ".", "");
483 }538 }
484539
540 comp.fs_watch = try fs.Watch(*Scope.Root).create(loop, 16);
541 defer comp.fs_watch.destroy();
542
485 try comp.initTypes();543 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 |_| {};
490 }561 }
491562
492 /// it does ref the result because it could be an arbitrary integer size563 /// it does ref the result because it could be an arbitrary integer size
...@@ -672,55 +743,28 @@ pub const Compilation = struct {...@@ -672,55 +743,28 @@ pub const Compilation = struct {
672 assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);743 assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
673 }744 }
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
698 pub fn destroy(self: *Compilation) void {746 pub fn destroy(self: *Compilation) void {
747 cancel self.main_loop_handle;
699 resume self.destroy_handle;748 resume self.destroy_handle;
700 }749 }
701750
702 pub fn build(self: *Compilation) !void {751 fn start(self: *Compilation) void {
703 if (self.llvm_argv.len != 0) {752 self.main_loop_future.resolve();
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();
714 }753 }
715754
716 async fn buildAsync(self: *Compilation) void {755 async fn mainLoop(self: *Compilation) void {
717 while (true) {756 // wait until start() is called
718 // TODO directly awaiting async should guarantee memory allocation elision757 _ = await (async self.main_loop_future.get() catch unreachable);
719 const build_result = await (async self.compileAndLink() 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;
721 // this makes a handy error return trace and stack trace in debug mode765 // this makes a handy error return trace and stack trace in debug mode
722 if (std.debug.runtime_safety) {766 if (std.debug.runtime_safety) {
723 build_result catch unreachable;767 link_result catch unreachable;
724 }768 }
725769
726 const compile_errors = blk: {770 const compile_errors = blk: {
...@@ -729,7 +773,7 @@ pub const Compilation = struct {...@@ -729,7 +773,7 @@ pub const Compilation = struct {
729 break :blk held.value.toOwnedSlice();773 break :blk held.value.toOwnedSlice();
730 };774 };
731775
732 if (build_result) |_| {776 if (link_result) |_| {
733 if (compile_errors.len == 0) {777 if (compile_errors.len == 0) {
734 await (async self.events.put(Event.Ok) catch unreachable);778 await (async self.events.put(Event.Ok) catch unreachable);
735 } else {779 } else {
...@@ -742,105 +786,195 @@ pub const Compilation = struct {...@@ -742,105 +786,195 @@ pub const Compilation = struct {
742 await (async self.events.put(Event{ .Error = err }) catch unreachable);786 await (async self.events.put(Event{ .Error = err }) catch unreachable);
743 }787 }
744788
745 // for now we stop after 1789 // First, get an item from the watch channel, waiting on the channel.
746 return;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);
747 }816 }
748 }817 }
749818
750 async fn compileAndLink(self: *Compilation) !void {819 async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void {
751 if (self.root_src_path) |root_src_path| {820 const tree_scope = blk: {
752 // TODO async/await os.path.real821 const source_code = (await (async fs.readFile(
753 const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {822 self.loop,
754 try printError("unable to get real path '{}': {}", root_src_path, err);823 root_scope.realpath,
755 return err;824 max_src_size,
825 ) catch unreachable)) catch |err| {
826 try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err));
827 return;
756 };828 };
757 const root_scope = blk: {829 errdefer self.gpa().free(source_code);
758 errdefer self.gpa().free(root_src_real_path);
759830
760 // TODO async/await readFileAlloc()831 const tree = try self.gpa().createOne(ast.Tree);
761 const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| {832 tree.* = try std.zig.parse(self.gpa(), source_code);
762 try printError("unable to open '{}': {}", root_src_real_path, err);833 errdefer {
763 return err;834 tree.deinit();
764 };835 self.gpa().destroy(tree);
765 errdefer self.gpa().free(source_code);836 }
766837
767 const tree = try self.gpa().createOne(ast.Tree);838 break :blk try Scope.AstTree.create(self, tree, root_scope);
768 tree.* = try std.zig.parse(self.gpa(), source_code);839 };
769 errdefer {840 defer tree_scope.base.deref(self);
770 tree.deinit();
771 self.gpa().destroy(tree);
772 }
773841
774 break :blk try Scope.Root.create(self, tree, root_src_real_path);842 var error_it = tree_scope.tree.errors.iterator(0);
775 };843 while (error_it.next()) |parse_error| {
776 defer root_scope.base.deref(self);844 const msg = try Msg.createFromParseErrorAndScope(self, tree_scope, parse_error);
777 const tree = root_scope.tree;845 errdefer msg.destroy();
778846
779 var error_it = tree.errors.iterator(0);847 try await (async self.addCompileErrorAsync(msg) catch unreachable);
780 while (error_it.next()) |parse_error| {848 }
781 const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error);849 if (tree_scope.tree.errors.len != 0) {
782 errdefer msg.destroy();850 return;
851 }
783852
784 try await (async self.addCompileErrorAsync(msg) catch unreachable);853 const locked_table = await (async root_scope.decls.table.acquireWrite() catch unreachable);
785 }854 defer locked_table.release();
786 if (tree.errors.len != 0) {
787 return;
788 }
789855
790 const decls = try Scope.Decls.create(self, &root_scope.base);856 var decl_group = event.Group(BuildError!void).init(self.loop);
791 defer decls.base.deref(self);857 defer decl_group.deinit();
792858
793 var decl_group = event.Group(BuildError!void).init(self.loop);859 try await try async self.rebuildChangedDecls(
794 var decl_group_consumed = false;860 &decl_group,
795 errdefer if (!decl_group_consumed) decl_group.cancelAll();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);867 try await (async decl_group.wait() catch unreachable);
798 while (it.next()) |decl_ptr| {868 }
799 const decl = decl_ptr.*;
800 switch (decl.id) {
801 ast.Node.Id.Comptime => {
802 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
803869
804 try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node);870 async fn rebuildChangedDecls(
805 },871 self: *Compilation,
806 ast.Node.Id.VarDecl => @panic("TODO"),872 group: *event.Group(BuildError!void),
807 ast.Node.Id.FnProto => {873 locked_table: *Decl.Table,
808 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);874 decl_scope: *Scope.Decls,
809875 ast_decls: *ast.Node.Root.DeclList,
810 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {876 tree_scope: *Scope.AstTree,
811 try self.addCompileError(root_scope, Span{877 ) !void {
812 .first = fn_proto.fn_token,878 var existing_decls = try locked_table.clone();
813 .last = fn_proto.fn_token + 1,879 defer existing_decls.deinit();
814 }, "missing function name");880
815 continue;881 var ast_it = ast_decls.iterator(0);
816 };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
818 const fn_decl = try self.gpa().create(Decl.Fn{927 const fn_decl = try self.gpa().create(Decl.Fn{
819 .base = Decl{928 .base = Decl{
820 .id = Decl.Id.Fn,929 .id = Decl.Id.Fn,
821 .name = name,930 .name = name,
822 .visib = parseVisibToken(tree, fn_proto.visib_token),931 .visib = parseVisibToken(tree_scope.tree, fn_proto.visib_token),
823 .resolution = event.Future(BuildError!void).init(self.loop),932 .resolution = event.Future(BuildError!void).init(self.loop),
824 .parent_scope = &decls.base,933 .parent_scope = &decl_scope.base,
934 .tree_scope = tree_scope,
825 },935 },
826 .value = Decl.Fn.Val{ .Unresolved = {} },936 .value = Decl.Fn.Val{ .Unresolved = {} },
827 .fn_proto = fn_proto,937 .fn_proto = fn_proto,
828 });938 });
939 tree_scope.base.ref();
829 errdefer self.gpa().destroy(fn_decl);940 errdefer self.gpa().destroy(fn_decl);
830941
831 try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base);942 try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table);
832 },943 }
833 ast.Node.Id.TestDecl => @panic("TODO"),944 },
834 else => unreachable,945 ast.Node.Id.TestDecl => @panic("TODO"),
835 }946 else => unreachable,
836 }947 }
837 decl_group_consumed = true;948 }
838 try await (async decl_group.wait() catch unreachable);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.972 assert((try await try async self.fs_watch.addFile(root_scope.realpath, root_scope)) == null);
841 decls.name_future.resolve();973 try await try async self.rebuildFile(root_scope);
842 }974 }
975 }
843976
977 async fn maybeLink(self: *Compilation) !void {
844 (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {978 (await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
845 error.SemanticAnalysisFailed => {},979 error.SemanticAnalysisFailed => {},
846 else => return err,980 else => return err,
...@@ -861,6 +995,7 @@ pub const Compilation = struct {...@@ -861,6 +995,7 @@ pub const Compilation = struct {
861 /// caller takes ownership of resulting Code995 /// caller takes ownership of resulting Code
862 async fn genAndAnalyzeCode(996 async fn genAndAnalyzeCode(
863 comp: *Compilation,997 comp: *Compilation,
998 tree_scope: *Scope.AstTree,
864 scope: *Scope,999 scope: *Scope,
865 node: *ast.Node,1000 node: *ast.Node,
866 expected_type: ?*Type,1001 expected_type: ?*Type,
...@@ -868,6 +1003,7 @@ pub const Compilation = struct {...@@ -868,6 +1003,7 @@ pub const Compilation = struct {
868 const unanalyzed_code = try await (async ir.gen(1003 const unanalyzed_code = try await (async ir.gen(
869 comp,1004 comp,
870 node,1005 node,
1006 tree_scope,
871 scope,1007 scope,
872 ) catch unreachable);1008 ) catch unreachable);
873 defer unanalyzed_code.destroy(comp.gpa());1009 defer unanalyzed_code.destroy(comp.gpa());
...@@ -894,6 +1030,7 @@ pub const Compilation = struct {...@@ -894,6 +1030,7 @@ pub const Compilation = struct {
8941030
895 async fn addCompTimeBlock(1031 async fn addCompTimeBlock(
896 comp: *Compilation,1032 comp: *Compilation,
1033 tree_scope: *Scope.AstTree,
897 scope: *Scope,1034 scope: *Scope,
898 comptime_node: *ast.Node.Comptime,1035 comptime_node: *ast.Node.Comptime,
899 ) !void {1036 ) !void {
...@@ -902,6 +1039,7 @@ pub const Compilation = struct {...@@ -902,6 +1039,7 @@ pub const Compilation = struct {
9021039
903 const analyzed_code = (await (async genAndAnalyzeCode(1040 const analyzed_code = (await (async genAndAnalyzeCode(
904 comp,1041 comp,
1042 tree_scope,
905 scope,1043 scope,
906 comptime_node.expr,1044 comptime_node.expr,
907 &void_type.base,1045 &void_type.base,
...@@ -914,38 +1052,42 @@ pub const Compilation = struct {...@@ -914,38 +1052,42 @@ pub const Compilation = struct {
914 analyzed_code.destroy(comp.gpa());1052 analyzed_code.destroy(comp.gpa());
915 }1053 }
9161054
917 async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {1055 async fn addTopLevelDecl(
918 const tree = decl.findRootScope().tree;1056 self: *Compilation,
919 const is_export = decl.isExported(tree);1057 decl: *Decl,
9201058 locked_table: *Decl.Table,
921 var add_to_table_resolved = false;1059 ) !void {
922 const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable;1060 const is_export = decl.isExported(decl.tree_scope.tree);
923 errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261
9241061
925 if (is_export) {1062 if (is_export) {
926 try self.prelink_group.call(verifyUniqueSymbol, self, decl);1063 try self.prelink_group.call(verifyUniqueSymbol, self, decl);
927 try self.prelink_group.call(resolveDecl, self, decl);1064 try self.prelink_group.call(resolveDecl, self, decl);
928 }1065 }
9291066
930 add_to_table_resolved = true;1067 const gop = try locked_table.getOrPut(decl.name);
931 try await add_to_table;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 }
932 }1074 }
9331075
934 async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {1076 fn addCompileError(self: *Compilation, tree_scope: *Scope.AstTree, span: Span, comptime fmt: []const u8, args: ...) !void {
935 const held = await (async decls.table.acquire() catch unreachable);1077 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
936 defer held.release();1078 errdefer self.gpa().free(text);
9371079
938 if (try held.value.put(decl.name, decl)) |other_decl| {1080 const msg = try Msg.createFromScope(self, tree_scope, span, text);
939 try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);1081 errdefer msg.destroy();
940 // TODO note: other definition here1082
941 }1083 try self.prelink_group.call(addCompileErrorAsync, self, msg);
942 }1084 }
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 {
945 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);1087 const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
946 errdefer self.gpa().free(text);1088 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);
949 errdefer msg.destroy();1091 errdefer msg.destroy();
9501092
951 try self.prelink_group.call(addCompileErrorAsync, self, msg);1093 try self.prelink_group.call(addCompileErrorAsync, self, msg);
...@@ -969,7 +1111,7 @@ pub const Compilation = struct {...@@ -969,7 +1111,7 @@ pub const Compilation = struct {
9691111
970 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {1112 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
971 try self.addCompileError(1113 try self.addCompileError(
972 decl.findRootScope(),1114 decl.tree_scope,
973 decl.getSpan(),1115 decl.getSpan(),
974 "exported symbol collision: '{}'",1116 "exported symbol collision: '{}'",
975 decl.name,1117 decl.name,
...@@ -1019,7 +1161,7 @@ pub const Compilation = struct {...@@ -1019,7 +1161,7 @@ pub const Compilation = struct {
1019 async fn startFindingNativeLibC(self: *Compilation) void {1161 async fn startFindingNativeLibC(self: *Compilation) void {
1020 await (async self.loop.yield() catch unreachable);1162 await (async self.loop.yield() catch unreachable);
1021 // we don't care if it fails, we're just trying to kick off the future resolution1163 // 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;
1023 }1165 }
10241166
1025 /// General Purpose Allocator. Must free when done.1167 /// General Purpose Allocator. Must free when done.
...@@ -1077,7 +1219,7 @@ pub const Compilation = struct {...@@ -1077,7 +1219,7 @@ pub const Compilation = struct {
1077 var rand_bytes: [9]u8 = undefined;1219 var rand_bytes: [9]u8 = undefined;
10781220
1079 {1221 {
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);
1081 defer held.release();1223 defer held.release();
10821224
1083 held.value.random.bytes(rand_bytes[0..]);1225 held.value.random.bytes(rand_bytes[0..]);
...@@ -1093,18 +1235,24 @@ pub const Compilation = struct {...@@ -1093,18 +1235,24 @@ pub const Compilation = struct {
1093 }1235 }
10941236
1095 /// Returns a value which has been ref()'d once1237 /// Returns a value which has been ref()'d once
1096 async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value {1238 async fn analyzeConstValue(
1097 const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable);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);
1098 defer analyzed_code.destroy(comp.gpa());1246 defer analyzed_code.destroy(comp.gpa());
10991247
1100 return analyzed_code.getCompTimeResult(comp);1248 return analyzed_code.getCompTimeResult(comp);
1101 }1249 }
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 {
1104 const meta_type = &Type.MetaType.get(comp).base;1252 const meta_type = &Type.MetaType.get(comp).base;
1105 defer meta_type.base.deref(comp);1253 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);
1108 errdefer result_val.base.deref(comp);1256 errdefer result_val.base.deref(comp);
11091257
1110 return result_val.cast(Type).?;1258 return result_val.cast(Type).?;
...@@ -1120,13 +1268,6 @@ pub const Compilation = struct {...@@ -1120,13 +1268,6 @@ pub const Compilation = struct {
1120 }1268 }
1121};1269};
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
1130fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {1271fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
1131 if (optional_token_index) |token_index| {1272 if (optional_token_index) |token_index| {
1132 const token = tree.tokens.at(token_index);1273 const token = tree.tokens.at(token_index);
...@@ -1150,12 +1291,14 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {...@@ -1150,12 +1291,14 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
1150}1291}
11511292
1152async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {1293async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1294 const tree_scope = fn_decl.base.tree_scope;
1295
1153 const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);1296 const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);
11541297
1155 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);1298 const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
1156 defer fndef_scope.base.deref(comp);1299 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);
1159 defer fn_type.base.base.deref(comp);1302 defer fn_type.base.base.deref(comp);
11601303
1161 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);1304 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 {...@@ -1168,18 +1311,17 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1168 symbol_name_consumed = true;1311 symbol_name_consumed = true;
11691312
1170 // Define local parameter variables1313 // Define local parameter variables
1171 const root_scope = fn_decl.base.findRootScope();
1172 for (fn_type.key.data.Normal.params) |param, i| {1314 for (fn_type.key.data.Normal.params) |param, i| {
1173 //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);1315 //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
1174 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);1316 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
1175 const name_token = param_decl.name_token orelse {1317 const name_token = param_decl.name_token orelse {
1176 try comp.addCompileError(root_scope, Span{1318 try comp.addCompileError(tree_scope, Span{
1177 .first = param_decl.firstToken(),1319 .first = param_decl.firstToken(),
1178 .last = param_decl.type_node.firstToken(),1320 .last = param_decl.type_node.firstToken(),
1179 }, "missing parameter name");1321 }, "missing parameter name");
1180 return error.SemanticAnalysisFailed;1322 return error.SemanticAnalysisFailed;
1181 };1323 };
1182 const param_name = root_scope.tree.tokenSlice(name_token);1324 const param_name = tree_scope.tree.tokenSlice(name_token);
11831325
1184 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {1326 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
1185 // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));1327 // 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 {...@@ -1201,6 +1343,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1201 }1343 }
12021344
1203 const analyzed_code = try await (async comp.genAndAnalyzeCode(1345 const analyzed_code = try await (async comp.genAndAnalyzeCode(
1346 tree_scope,
1204 fn_val.child_scope,1347 fn_val.child_scope,
1205 body_node,1348 body_node,
1206 fn_type.key.data.Normal.return_type,1349 fn_type.key.data.Normal.return_type,
...@@ -1231,12 +1374,17 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {...@@ -1231,12 +1374,17 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {
1231 return os.getAppDataDir(allocator, "zig");1374 return os.getAppDataDir(allocator, "zig");
1232}1375}
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 {
1235 const return_type_node = switch (fn_proto.return_type) {1383 const return_type_node = switch (fn_proto.return_type) {
1236 ast.Node.FnProto.ReturnType.Explicit => |n| n,1384 ast.Node.FnProto.ReturnType.Explicit => |n| n,
1237 ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,1385 ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
1238 };1386 };
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);
1240 return_type.base.deref(comp);1388 return_type.base.deref(comp);
12411389
1242 var params = ArrayList(Type.Fn.Param).init(comp.gpa());1390 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...@@ -1252,7 +1400,7 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
1252 var it = fn_proto.params.iterator(0);1400 var it = fn_proto.params.iterator(0);
1253 while (it.next()) |param_node_ptr| {1401 while (it.next()) |param_node_ptr| {
1254 const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;1402 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);
1256 errdefer param_type.base.deref(comp);1404 errdefer param_type.base.deref(comp);
1257 try params.append(Type.Fn.Param{1405 try params.append(Type.Fn.Param{
1258 .typ = param_type,1406 .typ = param_type,
...@@ -1289,7 +1437,12 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn...@@ -1289,7 +1437,12 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
1289}1437}
12901438
1291async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {1439async 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);
1293 defer fn_type.base.base.deref(comp);1446 defer fn_type.base.base.deref(comp);
12941447
1295 var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);1448 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 {...@@ -1301,3 +1454,14 @@ async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1301 fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };1454 fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
1302 symbol_name_consumed = true;1455 symbol_name_consumed = true;
1303}1456}
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 {...@@ -17,8 +17,16 @@ pub const Decl = struct {
17 resolution: event.Future(Compilation.BuildError!void),17 resolution: event.Future(Compilation.BuildError!void),
18 parent_scope: *Scope,18 parent_scope: *Scope,
1919
20 // TODO when we destroy the decl, deref the tree scope
21 tree_scope: *Scope.AstTree,
22
20 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);23 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
22 pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {30 pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
23 switch (base.id) {31 switch (base.id) {
24 Id.Fn => {32 Id.Fn => {
...@@ -95,4 +103,3 @@ pub const Decl = struct {...@@ -95,4 +103,3 @@ pub const Decl = struct {
95 base: Decl,103 base: Decl,
96 };104 };
97};105};
98
src-self-hosted/errmsg.zig+87-40
...@@ -33,35 +33,48 @@ pub const Span = struct {...@@ -33,35 +33,48 @@ pub const Span = struct {
33};33};
3434
35pub const Msg = struct {35pub const Msg = struct {
36 span: Span,
37 text: []u8,36 text: []u8,
37 realpath: []u8,
38 data: Data,38 data: Data,
3939
40 const Data = union(enum) {40 const Data = union(enum) {
41 Cli: Cli,
41 PathAndTree: PathAndTree,42 PathAndTree: PathAndTree,
42 ScopeAndComp: ScopeAndComp,43 ScopeAndComp: ScopeAndComp,
43 };44 };
4445
45 const PathAndTree = struct {46 const PathAndTree = struct {
46 realpath: []const u8,47 span: Span,
47 tree: *ast.Tree,48 tree: *ast.Tree,
48 allocator: *mem.Allocator,49 allocator: *mem.Allocator,
49 };50 };
5051
51 const ScopeAndComp = struct {52 const ScopeAndComp = struct {
52 root_scope: *Scope.Root,53 span: Span,
54 tree_scope: *Scope.AstTree,
53 compilation: *Compilation,55 compilation: *Compilation,
54 };56 };
5557
58 const Cli = struct {
59 allocator: *mem.Allocator,
60 };
61
56 pub fn destroy(self: *Msg) void {62 pub fn destroy(self: *Msg) void {
57 switch (self.data) {63 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 },
58 Data.PathAndTree => |path_and_tree| {69 Data.PathAndTree => |path_and_tree| {
59 path_and_tree.allocator.free(self.text);70 path_and_tree.allocator.free(self.text);
71 path_and_tree.allocator.free(self.realpath);
60 path_and_tree.allocator.destroy(self);72 path_and_tree.allocator.destroy(self);
61 },73 },
62 Data.ScopeAndComp => |scope_and_comp| {74 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);
64 scope_and_comp.compilation.gpa().free(self.text);76 scope_and_comp.compilation.gpa().free(self.text);
77 scope_and_comp.compilation.gpa().free(self.realpath);
65 scope_and_comp.compilation.gpa().destroy(self);78 scope_and_comp.compilation.gpa().destroy(self);
66 },79 },
67 }80 }
...@@ -69,6 +82,7 @@ pub const Msg = struct {...@@ -69,6 +82,7 @@ pub const Msg = struct {
6982
70 fn getAllocator(self: *const Msg) *mem.Allocator {83 fn getAllocator(self: *const Msg) *mem.Allocator {
71 switch (self.data) {84 switch (self.data) {
85 Data.Cli => |cli| return cli.allocator,
72 Data.PathAndTree => |path_and_tree| {86 Data.PathAndTree => |path_and_tree| {
73 return path_and_tree.allocator;87 return path_and_tree.allocator;
74 },88 },
...@@ -78,71 +92,93 @@ pub const Msg = struct {...@@ -78,71 +92,93 @@ pub const Msg = struct {
78 }92 }
79 }93 }
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
92 pub fn getTree(self: *const Msg) *ast.Tree {95 pub fn getTree(self: *const Msg) *ast.Tree {
93 switch (self.data) {96 switch (self.data) {
97 Data.Cli => unreachable,
94 Data.PathAndTree => |path_and_tree| {98 Data.PathAndTree => |path_and_tree| {
95 return path_and_tree.tree;99 return path_and_tree.tree;
96 },100 },
97 Data.ScopeAndComp => |scope_and_comp| {101 Data.ScopeAndComp => |scope_and_comp| {
98 return scope_and_comp.root_scope.tree;102 return scope_and_comp.tree_scope.tree;
99 },103 },
100 }104 }
101 }105 }
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
103 /// Takes ownership of text115 /// Takes ownership of text
104 /// References root_scope, and derefs when the msg is freed116 /// References tree_scope, and derefs when the msg is freed
105 pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {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
106 const msg = try comp.gpa().create(Msg{121 const msg = try comp.gpa().create(Msg{
107 .text = text,122 .text = text,
108 .span = span,123 .realpath = realpath,
109 .data = Data{124 .data = Data{
110 .ScopeAndComp = ScopeAndComp{125 .ScopeAndComp = ScopeAndComp{
111 .root_scope = root_scope,126 .tree_scope = tree_scope,
112 .compilation = comp,127 .compilation = comp,
128 .span = span,
113 },129 },
114 },130 },
115 });131 });
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 });
117 return msg;149 return msg;
118 }150 }
119151
120 pub fn createFromParseErrorAndScope(152 pub fn createFromParseErrorAndScope(
121 comp: *Compilation,153 comp: *Compilation,
122 root_scope: *Scope.Root,154 tree_scope: *Scope.AstTree,
123 parse_error: *const ast.Error,155 parse_error: *const ast.Error,
124 ) !*Msg {156 ) !*Msg {
125 const loc_token = parse_error.loc();157 const loc_token = parse_error.loc();
126 var text_buf = try std.Buffer.initSize(comp.gpa(), 0);158 var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
127 defer text_buf.deinit();159 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
129 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;164 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
132 const msg = try comp.gpa().create(Msg{167 const msg = try comp.gpa().create(Msg{
133 .text = undefined,168 .text = undefined,
134 .span = Span{169 .realpath = realpath_copy,
135 .first = loc_token,
136 .last = loc_token,
137 },
138 .data = Data{170 .data = Data{
139 .ScopeAndComp = ScopeAndComp{171 .ScopeAndComp = ScopeAndComp{
140 .root_scope = root_scope,172 .tree_scope = tree_scope,
141 .compilation = comp,173 .compilation = comp,
174 .span = Span{
175 .first = loc_token,
176 .last = loc_token,
177 },
142 },178 },
143 },179 },
144 });180 });
145 root_scope.base.ref();181 tree_scope.base.ref();
146 msg.text = text_buf.toOwnedSlice();182 msg.text = text_buf.toOwnedSlice();
147 return msg;183 return msg;
148 }184 }
...@@ -161,22 +197,25 @@ pub const Msg = struct {...@@ -161,22 +197,25 @@ pub const Msg = struct {
161 var text_buf = try std.Buffer.initSize(allocator, 0);197 var text_buf = try std.Buffer.initSize(allocator, 0);
162 defer text_buf.deinit();198 defer text_buf.deinit();
163199
200 const realpath_copy = try mem.dupe(allocator, u8, realpath);
201 errdefer allocator.free(realpath_copy);
202
164 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;203 var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
165 try parse_error.render(&tree.tokens, out_stream);204 try parse_error.render(&tree.tokens, out_stream);
166205
167 const msg = try allocator.create(Msg{206 const msg = try allocator.create(Msg{
168 .text = undefined,207 .text = undefined,
208 .realpath = realpath_copy,
169 .data = Data{209 .data = Data{
170 .PathAndTree = PathAndTree{210 .PathAndTree = PathAndTree{
171 .allocator = allocator,211 .allocator = allocator,
172 .realpath = realpath,
173 .tree = tree,212 .tree = tree,
213 .span = Span{
214 .first = loc_token,
215 .last = loc_token,
216 },
174 },217 },
175 },218 },
176 .span = Span{
177 .first = loc_token,
178 .last = loc_token,
179 },
180 });219 });
181 msg.text = text_buf.toOwnedSlice();220 msg.text = text_buf.toOwnedSlice();
182 errdefer allocator.destroy(msg);221 errdefer allocator.destroy(msg);
...@@ -185,20 +224,28 @@ pub const Msg = struct {...@@ -185,20 +224,28 @@ pub const Msg = struct {
185 }224 }
186225
187 pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {226 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
188 const allocator = msg.getAllocator();235 const allocator = msg.getAllocator();
189 const realpath = msg.getRealPath();
190 const tree = msg.getTree();236 const tree = msg.getTree();
191237
192 const cwd = try os.getCwd(allocator);238 const cwd = try os.getCwdAlloc(allocator);
193 defer allocator.free(cwd);239 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);
196 defer allocator.free(relpath);242 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);247 const first_token = tree.tokens.at(span.first);
201 const last_token = tree.tokens.at(msg.span.last);248 const last_token = tree.tokens.at(span.last);
202 const start_loc = tree.tokenLocationPtr(0, first_token);249 const start_loc = tree.tokenLocationPtr(0, first_token);
203 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);250 const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
204 if (!color_on) {251 if (!color_on) {
src-self-hosted/introspect.zig+2-2
...@@ -14,7 +14,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![...@@ -14,7 +14,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![
14 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");14 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
15 defer allocator.free(test_index_file);15 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);
18 file.close();18 file.close();
1919
20 return test_zig_dir;20 return test_zig_dir;
...@@ -22,7 +22,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![...@@ -22,7 +22,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![
2222
23/// Caller must free result23/// Caller must free result
24pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {24pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {
25 const self_exe_path = try os.selfExeDirPath(allocator);25 const self_exe_path = try os.selfExeDirPathAlloc(allocator);
26 defer allocator.free(self_exe_path);26 defer allocator.free(self_exe_path);
2727
28 var cur_path: []const u8 = self_exe_path;28 var cur_path: []const u8 = self_exe_path;
src-self-hosted/ir.zig+26-22
...@@ -961,6 +961,7 @@ pub const Code = struct {...@@ -961,6 +961,7 @@ pub const Code = struct {
961 basic_block_list: std.ArrayList(*BasicBlock),961 basic_block_list: std.ArrayList(*BasicBlock),
962 arena: std.heap.ArenaAllocator,962 arena: std.heap.ArenaAllocator,
963 return_type: ?*Type,963 return_type: ?*Type,
964 tree_scope: *Scope.AstTree,
964965
965 /// allocator is comp.gpa()966 /// allocator is comp.gpa()
966 pub fn destroy(self: *Code, allocator: *Allocator) void {967 pub fn destroy(self: *Code, allocator: *Allocator) void {
...@@ -990,14 +991,14 @@ pub const Code = struct {...@@ -990,14 +991,14 @@ pub const Code = struct {
990 return ret_value.val.KnownValue.getRef();991 return ret_value.val.KnownValue.getRef();
991 }992 }
992 try comp.addCompileError(993 try comp.addCompileError(
993 ret_value.scope.findRoot(),994 self.tree_scope,
994 ret_value.span,995 ret_value.span,
995 "unable to evaluate constant expression",996 "unable to evaluate constant expression",
996 );997 );
997 return error.SemanticAnalysisFailed;998 return error.SemanticAnalysisFailed;
998 } else if (inst.hasSideEffects()) {999 } else if (inst.hasSideEffects()) {
999 try comp.addCompileError(1000 try comp.addCompileError(
1000 inst.scope.findRoot(),1001 self.tree_scope,
1001 inst.span,1002 inst.span,
1002 "unable to evaluate constant expression",1003 "unable to evaluate constant expression",
1003 );1004 );
...@@ -1013,25 +1014,24 @@ pub const Builder = struct {...@@ -1013,25 +1014,24 @@ pub const Builder = struct {
1013 code: *Code,1014 code: *Code,
1014 current_basic_block: *BasicBlock,1015 current_basic_block: *BasicBlock,
1015 next_debug_id: usize,1016 next_debug_id: usize,
1016 root_scope: *Scope.Root,
1017 is_comptime: bool,1017 is_comptime: bool,
1018 is_async: bool,1018 is_async: bool,
1019 begin_scope: ?*Scope,1019 begin_scope: ?*Scope,
10201020
1021 pub const Error = Analyze.Error;1021 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 {
1024 const code = try comp.gpa().create(Code{1024 const code = try comp.gpa().create(Code{
1025 .basic_block_list = undefined,1025 .basic_block_list = undefined,
1026 .arena = std.heap.ArenaAllocator.init(comp.gpa()),1026 .arena = std.heap.ArenaAllocator.init(comp.gpa()),
1027 .return_type = null,1027 .return_type = null,
1028 .tree_scope = tree_scope,
1028 });1029 });
1029 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);1030 code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator);
1030 errdefer code.destroy(comp.gpa());1031 errdefer code.destroy(comp.gpa());
10311032
1032 return Builder{1033 return Builder{
1033 .comp = comp,1034 .comp = comp,
1034 .root_scope = root_scope,
1035 .current_basic_block = undefined,1035 .current_basic_block = undefined,
1036 .code = code,1036 .code = code,
1037 .next_debug_id = 0,1037 .next_debug_id = 0,
...@@ -1292,6 +1292,7 @@ pub const Builder = struct {...@@ -1292,6 +1292,7 @@ pub const Builder = struct {
1292 Scope.Id.FnDef => return false,1292 Scope.Id.FnDef => return false,
1293 Scope.Id.Decls => unreachable,1293 Scope.Id.Decls => unreachable,
1294 Scope.Id.Root => unreachable,1294 Scope.Id.Root => unreachable,
1295 Scope.Id.AstTree => unreachable,
1295 Scope.Id.Block,1296 Scope.Id.Block,
1296 Scope.Id.Defer,1297 Scope.Id.Defer,
1297 Scope.Id.DeferExpr,1298 Scope.Id.DeferExpr,
...@@ -1302,7 +1303,7 @@ pub const Builder = struct {...@@ -1302,7 +1303,7 @@ pub const Builder = struct {
1302 }1303 }
13031304
1304 pub fn genIntLit(irb: *Builder, int_lit: *ast.Node.IntegerLiteral, scope: *Scope) !*Inst {1305 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
1307 var base: u8 = undefined;1308 var base: u8 = undefined;
1308 var rest: []const u8 = undefined;1309 var rest: []const u8 = undefined;
...@@ -1341,7 +1342,7 @@ pub const Builder = struct {...@@ -1341,7 +1342,7 @@ pub const Builder = struct {
1341 }1342 }
13421343
1343 pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {1344 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);
1345 const src_span = Span.token(str_lit.token);1346 const src_span = Span.token(str_lit.token);
13461347
1347 var bad_index: usize = undefined;1348 var bad_index: usize = undefined;
...@@ -1349,7 +1350,7 @@ pub const Builder = struct {...@@ -1349,7 +1350,7 @@ pub const Builder = struct {
1349 error.OutOfMemory => return error.OutOfMemory,1350 error.OutOfMemory => return error.OutOfMemory,
1350 error.InvalidCharacter => {1351 error.InvalidCharacter => {
1351 try irb.comp.addCompileError(1352 try irb.comp.addCompileError(
1352 irb.root_scope,1353 irb.code.tree_scope,
1353 src_span,1354 src_span,
1354 "invalid character in string literal: '{c}'",1355 "invalid character in string literal: '{c}'",
1355 str_token[bad_index],1356 str_token[bad_index],
...@@ -1427,7 +1428,7 @@ pub const Builder = struct {...@@ -1427,7 +1428,7 @@ pub const Builder = struct {
14271428
1428 if (statement_node.cast(ast.Node.Defer)) |defer_node| {1429 if (statement_node.cast(ast.Node.Defer)) |defer_node| {
1429 // defer starts a new scope1430 // 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);
1431 const kind = switch (defer_token.id) {1432 const kind = switch (defer_token.id) {
1432 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,1433 Token.Id.Keyword_defer => Scope.Defer.Kind.ScopeExit,
1433 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,1434 Token.Id.Keyword_errdefer => Scope.Defer.Kind.ErrorExit,
...@@ -1513,7 +1514,7 @@ pub const Builder = struct {...@@ -1513,7 +1514,7 @@ pub const Builder = struct {
1513 const src_span = Span.token(control_flow_expr.ltoken);1514 const src_span = Span.token(control_flow_expr.ltoken);
1514 if (scope.findFnDef() == null) {1515 if (scope.findFnDef() == null) {
1515 try irb.comp.addCompileError(1516 try irb.comp.addCompileError(
1516 irb.root_scope,1517 irb.code.tree_scope,
1517 src_span,1518 src_span,
1518 "return expression outside function definition",1519 "return expression outside function definition",
1519 );1520 );
...@@ -1523,7 +1524,7 @@ pub const Builder = struct {...@@ -1523,7 +1524,7 @@ pub const Builder = struct {
1523 if (scope.findDeferExpr()) |scope_defer_expr| {1524 if (scope.findDeferExpr()) |scope_defer_expr| {
1524 if (!scope_defer_expr.reported_err) {1525 if (!scope_defer_expr.reported_err) {
1525 try irb.comp.addCompileError(1526 try irb.comp.addCompileError(
1526 irb.root_scope,1527 irb.code.tree_scope,
1527 src_span,1528 src_span,
1528 "cannot return from defer expression",1529 "cannot return from defer expression",
1529 );1530 );
...@@ -1599,7 +1600,7 @@ pub const Builder = struct {...@@ -1599,7 +1600,7 @@ pub const Builder = struct {
15991600
1600 pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {1601 pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
1601 const src_span = Span.token(identifier.token);1602 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
1604 //if (buf_eql_str(variable_name, "_") && lval == LValPtr) {1605 //if (buf_eql_str(variable_name, "_") && lval == LValPtr) {
1605 // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);1606 // IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, node);
...@@ -1622,7 +1623,7 @@ pub const Builder = struct {...@@ -1622,7 +1623,7 @@ pub const Builder = struct {
1622 }1623 }
1623 } else |err| switch (err) {1624 } else |err| switch (err) {
1624 error.Overflow => {1625 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");
1626 return error.SemanticAnalysisFailed;1627 return error.SemanticAnalysisFailed;
1627 },1628 },
1628 error.OutOfMemory => return error.OutOfMemory,1629 error.OutOfMemory => return error.OutOfMemory,
...@@ -1656,7 +1657,7 @@ pub const Builder = struct {...@@ -1656,7 +1657,7 @@ pub const Builder = struct {
1656 // TODO put a variable of same name with invalid type in global scope1657 // TODO put a variable of same name with invalid type in global scope
1657 // so that future references to this same name will find a variable with an invalid type1658 // 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);
1660 return error.SemanticAnalysisFailed;1661 return error.SemanticAnalysisFailed;
1661 }1662 }
16621663
...@@ -1689,6 +1690,7 @@ pub const Builder = struct {...@@ -1689,6 +1690,7 @@ pub const Builder = struct {
1689 => scope = scope.parent orelse break,1690 => scope = scope.parent orelse break,
16901691
1691 Scope.Id.DeferExpr => unreachable,1692 Scope.Id.DeferExpr => unreachable,
1693 Scope.Id.AstTree => unreachable,
1692 }1694 }
1693 }1695 }
1694 return result;1696 return result;
...@@ -1740,6 +1742,7 @@ pub const Builder = struct {...@@ -1740,6 +1742,7 @@ pub const Builder = struct {
1740 => scope = scope.parent orelse return is_noreturn,1742 => scope = scope.parent orelse return is_noreturn,
17411743
1742 Scope.Id.DeferExpr => unreachable,1744 Scope.Id.DeferExpr => unreachable,
1745 Scope.Id.AstTree => unreachable,
1743 }1746 }
1744 }1747 }
1745 }1748 }
...@@ -1929,8 +1932,9 @@ pub const Builder = struct {...@@ -1929,8 +1932,9 @@ pub const Builder = struct {
1929 Scope.Id.Root => return Ident.NotFound,1932 Scope.Id.Root => return Ident.NotFound,
1930 Scope.Id.Decls => {1933 Scope.Id.Decls => {
1931 const decls = @fieldParentPtr(Scope.Decls, "base", s);1934 const decls = @fieldParentPtr(Scope.Decls, "base", s);
1932 const table = await (async decls.getTableReadOnly() catch unreachable);1935 const locked_table = await (async decls.table.acquireRead() catch unreachable);
1933 if (table.get(name)) |entry| {1936 defer locked_table.release();
1937 if (locked_table.value.get(name)) |entry| {
1934 return Ident{ .Decl = entry.value };1938 return Ident{ .Decl = entry.value };
1935 }1939 }
1936 },1940 },
...@@ -1967,8 +1971,8 @@ const Analyze = struct {...@@ -1967,8 +1971,8 @@ const Analyze = struct {
1967 OutOfMemory,1971 OutOfMemory,
1968 };1972 };
19691973
1970 pub fn init(comp: *Compilation, root_scope: *Scope.Root, explicit_return_type: ?*Type) !Analyze {1974 pub fn init(comp: *Compilation, tree_scope: *Scope.AstTree, explicit_return_type: ?*Type) !Analyze {
1971 var irb = try Builder.init(comp, root_scope, null);1975 var irb = try Builder.init(comp, tree_scope, null);
1972 errdefer irb.abort();1976 errdefer irb.abort();
19731977
1974 return Analyze{1978 return Analyze{
...@@ -2046,7 +2050,7 @@ const Analyze = struct {...@@ -2046,7 +2050,7 @@ const Analyze = struct {
2046 }2050 }
20472051
2048 fn addCompileError(self: *Analyze, span: Span, comptime fmt: []const u8, args: ...) !void {2052 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);
2050 }2054 }
20512055
2052 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {2056 fn resolvePeerTypes(self: *Analyze, expected_type: ?*Type, peers: []const *Inst) Analyze.Error!*Type {
...@@ -2534,9 +2538,10 @@ const Analyze = struct {...@@ -2534,9 +2538,10 @@ const Analyze = struct {
2534pub async fn gen(2538pub async fn gen(
2535 comp: *Compilation,2539 comp: *Compilation,
2536 body_node: *ast.Node,2540 body_node: *ast.Node,
2541 tree_scope: *Scope.AstTree,
2537 scope: *Scope,2542 scope: *Scope,
2538) !*Code {2543) !*Code {
2539 var irb = try Builder.init(comp, scope.findRoot(), scope);2544 var irb = try Builder.init(comp, tree_scope, scope);
2540 errdefer irb.abort();2545 errdefer irb.abort();
25412546
2542 const entry_block = try irb.createBasicBlock(scope, c"Entry");2547 const entry_block = try irb.createBasicBlock(scope, c"Entry");
...@@ -2554,9 +2559,8 @@ pub async fn gen(...@@ -2554,9 +2559,8 @@ pub async fn gen(
25542559
2555pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {2560pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
2556 const old_entry_bb = old_code.basic_block_list.at(0);2561 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);
2560 errdefer ira.abort();2564 errdefer ira.abort();
25612565
2562 const new_entry_bb = try ira.getNewBasicBlock(old_entry_bb, null);2566 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 {...@@ -143,7 +143,7 @@ pub const LibCInstallation = struct {
143 pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {143 pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
144 self.initEmpty();144 self.initEmpty();
145 var group = event.Group(FindError!void).init(loop);145 var group = event.Group(FindError!void).init(loop);
146 errdefer group.cancelAll();146 errdefer group.deinit();
147 var windows_sdk: ?*c.ZigWindowsSDK = null;147 var windows_sdk: ?*c.ZigWindowsSDK = null;
148 errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));148 errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));
149149
...@@ -233,7 +233,7 @@ pub const LibCInstallation = struct {...@@ -233,7 +233,7 @@ pub const LibCInstallation = struct {
233 const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");233 const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");
234 defer loop.allocator.free(stdlib_path);234 defer loop.allocator.free(stdlib_path);
235235
236 if (try fileExists(loop.allocator, stdlib_path)) {236 if (try fileExists(stdlib_path)) {
237 self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path);237 self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path);
238 return;238 return;
239 }239 }
...@@ -257,7 +257,7 @@ pub const LibCInstallation = struct {...@@ -257,7 +257,7 @@ pub const LibCInstallation = struct {
257 const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");257 const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");
258 defer loop.allocator.free(stdlib_path);258 defer loop.allocator.free(stdlib_path);
259259
260 if (try fileExists(loop.allocator, stdlib_path)) {260 if (try fileExists(stdlib_path)) {
261 self.include_dir = result_buf.toOwnedSlice();261 self.include_dir = result_buf.toOwnedSlice();
262 return;262 return;
263 }263 }
...@@ -285,7 +285,7 @@ pub const LibCInstallation = struct {...@@ -285,7 +285,7 @@ pub const LibCInstallation = struct {
285 }285 }
286 const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");286 const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");
287 defer loop.allocator.free(ucrt_lib_path);287 defer loop.allocator.free(ucrt_lib_path);
288 if (try fileExists(loop.allocator, ucrt_lib_path)) {288 if (try fileExists(ucrt_lib_path)) {
289 self.lib_dir = result_buf.toOwnedSlice();289 self.lib_dir = result_buf.toOwnedSlice();
290 return;290 return;
291 }291 }
...@@ -313,7 +313,7 @@ pub const LibCInstallation = struct {...@@ -313,7 +313,7 @@ pub const LibCInstallation = struct {
313 },313 },
314 };314 };
315 var group = event.Group(FindError!void).init(loop);315 var group = event.Group(FindError!void).init(loop);
316 errdefer group.cancelAll();316 errdefer group.deinit();
317 for (dyn_tests) |*dyn_test| {317 for (dyn_tests) |*dyn_test| {
318 try group.call(testNativeDynamicLinker, self, loop, dyn_test);318 try group.call(testNativeDynamicLinker, self, loop, dyn_test);
319 }319 }
...@@ -341,7 +341,6 @@ pub const LibCInstallation = struct {...@@ -341,7 +341,6 @@ pub const LibCInstallation = struct {
341 }341 }
342 }342 }
343343
344
345 async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {344 async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
346 var search_buf: [2]Search = undefined;345 var search_buf: [2]Search = undefined;
347 const searches = fillSearch(&search_buf, sdk);346 const searches = fillSearch(&search_buf, sdk);
...@@ -361,7 +360,7 @@ pub const LibCInstallation = struct {...@@ -361,7 +360,7 @@ pub const LibCInstallation = struct {
361 }360 }
362 const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");361 const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");
363 defer loop.allocator.free(kernel32_path);362 defer loop.allocator.free(kernel32_path);
364 if (try fileExists(loop.allocator, kernel32_path)) {363 if (try fileExists(kernel32_path)) {
365 self.kernel32_lib_dir = result_buf.toOwnedSlice();364 self.kernel32_lib_dir = result_buf.toOwnedSlice();
366 return;365 return;
367 }366 }
...@@ -450,13 +449,11 @@ fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {...@@ -450,13 +449,11 @@ fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
450 return search_buf[0..search_end];449 return search_buf[0..search_end];
451}450}
452451
453452fn fileExists(path: []const u8) !bool {
454fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {453 if (std.os.File.access(path)) |_| {
455 if (std.os.File.access(allocator, path)) |_| {
456 return true;454 return true;
457 } else |err| switch (err) {455 } else |err| switch (err) {
458 error.NotFound, error.PermissionDenied => return false,456 error.FileNotFound, error.PermissionDenied => return false,
459 error.OutOfMemory => return error.OutOfMemory,
460 else => return error.FileSystem,457 else => return error.FileSystem,
461 }458 }
462}459}
src-self-hosted/link.zig+2-2
...@@ -61,7 +61,7 @@ pub async fn link(comp: *Compilation) !void {...@@ -61,7 +61,7 @@ pub async fn link(comp: *Compilation) !void {
61 ctx.libc = ctx.comp.override_libc orelse blk: {61 ctx.libc = ctx.comp.override_libc orelse blk: {
62 switch (comp.target) {62 switch (comp.target) {
63 Target.Native => {63 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;
65 },65 },
66 else => return error.LibCRequiredButNotProvidedOrFound,66 else => return error.LibCRequiredButNotProvidedOrFound,
67 }67 }
...@@ -83,7 +83,7 @@ pub async fn link(comp: *Compilation) !void {...@@ -83,7 +83,7 @@ pub async fn link(comp: *Compilation) !void {
8383
84 {84 {
85 // LLD is not thread-safe, so we grab a global lock.85 // 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);
87 defer held.release();87 defer held.release();
8888
89 // Not evented I/O. LLD does its own multithreading internally.89 // 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");...@@ -14,7 +14,7 @@ const c = @import("c.zig");
14const introspect = @import("introspect.zig");14const introspect = @import("introspect.zig");
15const Args = arg.Args;15const Args = arg.Args;
16const Flag = arg.Flag;16const Flag = arg.Flag;
17const EventLoopLocal = @import("compilation.zig").EventLoopLocal;17const ZigCompiler = @import("compilation.zig").ZigCompiler;
18const Compilation = @import("compilation.zig").Compilation;18const Compilation = @import("compilation.zig").Compilation;
19const Target = @import("target.zig").Target;19const Target = @import("target.zig").Target;
20const errmsg = @import("errmsg.zig");20const errmsg = @import("errmsg.zig");
...@@ -24,6 +24,8 @@ var stderr_file: os.File = undefined;...@@ -24,6 +24,8 @@ var stderr_file: os.File = undefined;
24var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;24var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
25var stdout: *io.OutStream(io.FileOutStream.Error) = undefined;25var stdout: *io.OutStream(io.FileOutStream.Error) = undefined;
2626
27const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
28
27const usage =29const usage =
28 \\usage: zig [command] [options]30 \\usage: zig [command] [options]
29 \\31 \\
...@@ -371,6 +373,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -371,6 +373,16 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
371 os.exit(1);373 os.exit(1);
372 }374 }
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
374 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);386 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
375 defer allocator.free(zig_lib_dir);387 defer allocator.free(zig_lib_dir);
376388
...@@ -380,11 +392,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -380,11 +392,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
380 try loop.initMultiThreaded(allocator);392 try loop.initMultiThreaded(allocator);
381 defer loop.deinit();393 defer loop.deinit();
382394
383 var event_loop_local = try EventLoopLocal.init(&loop);395 var zig_compiler = try ZigCompiler.init(&loop);
384 defer event_loop_local.deinit();396 defer zig_compiler.deinit();
385397
386 var comp = try Compilation.create(398 var comp = try Compilation.create(
387 &event_loop_local,399 &zig_compiler,
388 root_name,400 root_name,
389 root_source_file,401 root_source_file,
390 Target.Native,402 Target.Native,
...@@ -413,16 +425,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -413,16 +425,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
413 comp.linker_script = flags.single("linker-script");425 comp.linker_script = flags.single("linker-script");
414 comp.each_lib_rpath = flags.present("each-lib-rpath");426 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;
426 comp.clang_argv = clang_argv_buf.toSliceConst();428 comp.clang_argv = clang_argv_buf.toSliceConst();
427429
428 comp.strip = flags.present("strip");430 comp.strip = flags.present("strip");
...@@ -465,30 +467,34 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co...@@ -465,30 +467,34 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
465 comp.link_out_file = flags.single("output");467 comp.link_out_file = flags.single("output");
466 comp.link_objects = link_objects;468 comp.link_objects = link_objects;
467469
468 try comp.build();470 comp.start();
469 const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);471 const process_build_events_handle = try async<loop.allocator> processBuildEvents(comp, color);
470 defer cancel process_build_events_handle;472 defer cancel process_build_events_handle;
471 loop.run();473 loop.run();
472}474}
473475
474async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {476async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
475 // TODO directly awaiting async should guarantee memory allocation elision477 var count: usize = 0;
476 const build_event = await (async comp.events.get() catch unreachable);478 while (true) {
477479 // TODO directly awaiting async should guarantee memory allocation elision
478 switch (build_event) {480 const build_event = await (async comp.events.get() catch unreachable);
479 Compilation.Event.Ok => {481 count += 1;
480 return;482
481 },483 switch (build_event) {
482 Compilation.Event.Error => |err| {484 Compilation.Event.Ok => {
483 std.debug.warn("build failed: {}\n", @errorName(err));485 stderr.print("Build {} succeeded\n", count) catch os.exit(1);
484 os.exit(1);486 },
485 },487 Compilation.Event.Error => |err| {
486 Compilation.Event.Fail => |msgs| {488 stderr.print("Build {} failed: {}\n", count, @errorName(err)) catch os.exit(1);
487 for (msgs) |msg| {489 },
488 defer msg.destroy();490 Compilation.Event.Fail => |msgs| {
489 msg.printToFile(&stderr_file, color) catch os.exit(1);491 stderr.print("Build {} compile errors:\n", count) catch os.exit(1);
490 }492 for (msgs) |msg| {
491 },493 defer msg.destroy();
494 msg.printToFile(&stderr_file, color) catch os.exit(1);
495 }
496 },
497 }
492 }498 }
493}499}
494500
...@@ -528,33 +534,12 @@ const args_fmt_spec = []Flag{...@@ -528,33 +534,12 @@ const args_fmt_spec = []Flag{
528};534};
529535
530const Fmt = struct {536const Fmt = struct {
531 seen: std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8),537 seen: event.Locked(SeenMap),
532 queue: std.LinkedList([]const u8),
533 any_error: bool,538 any_error: bool,
539 color: errmsg.Color,
540 loop: *event.Loop,
534541
535 // file_path must outlive Fmt542 const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
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 }
558};543};
559544
560fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {545fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
...@@ -587,17 +572,17 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {...@@ -587,17 +572,17 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
587 try loop.initMultiThreaded(allocator);572 try loop.initMultiThreaded(allocator);
588 defer loop.deinit();573 defer loop.deinit();
589574
590 var event_loop_local = try EventLoopLocal.init(&loop);575 var zig_compiler = try ZigCompiler.init(&loop);
591 defer event_loop_local.deinit();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);
594 defer cancel handle;579 defer cancel handle;
595580
596 loop.run();581 loop.run();
597}582}
598583
599async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {584async fn findLibCAsync(zig_compiler: *ZigCompiler) void {
600 const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| {585 const libc = (await (async zig_compiler.getNativeLibC() catch unreachable)) catch |err| {
601 stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);586 stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
602 os.exit(1);587 os.exit(1);
603 };588 };
...@@ -636,7 +621,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {...@@ -636,7 +621,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
636 var stdin_file = try io.getStdIn();621 var stdin_file = try io.getStdIn();
637 var stdin = io.FileInStream.init(&stdin_file);622 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);
640 defer allocator.free(source_code);625 defer allocator.free(source_code);
641626
642 var tree = std.zig.parse(allocator, source_code) catch |err| {627 var tree = std.zig.parse(allocator, source_code) catch |err| {
...@@ -665,66 +650,143 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {...@@ -665,66 +650,143 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
665 os.exit(1);650 os.exit(1);
666 }651 }
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 }
668 var fmt = Fmt{707 var fmt = Fmt{
669 .seen = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator),708 .seen = event.Locked(Fmt.SeenMap).init(loop, Fmt.SeenMap.init(loop.allocator)),
670 .queue = std.LinkedList([]const u8).init(),
671 .any_error = false,709 .any_error = false,
710 .color = color,
711 .loop = loop,
672 };712 };
673713
714 var group = event.Group(FmtError!void).init(loop);
674 for (flags.positionals.toSliceConst()) |file_path| {715 for (flags.positionals.toSliceConst()) |file_path| {
675 try fmt.addToQueue(file_path);716 try group.call(fmtPath, &fmt, file_path);
676 }717 }
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| {724async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8) FmtError!void {
679 const file_path = node.data;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);728 {
682 defer file.close();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) {732 if (try held.value.put(file_path, {})) |_| return;
685 error.IsDir => {733 }
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);
696734
697 var tree = std.zig.parse(allocator, source_code) catch |err| {735 const source_code = (await try async event.fs.readFile(
698 try stderr.print("error parsing file '{}': {}\n", file_path, err);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);
699 fmt.any_error = true;757 fmt.any_error = true;
700 continue;758 return;
701 };759 },
702 defer tree.deinit();760 };
703761 defer fmt.loop.allocator.free(source_code);
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();
708762
709 try msg.printToFile(&stderr_file, color);763 var tree = std.zig.parse(fmt.loop.allocator, source_code) catch |err| {
710 }764 try stderr.print("error parsing file '{}': {}\n", file_path, err);
711 if (tree.errors.len != 0) {765 fmt.any_error = true;
712 fmt.any_error = true;766 return;
713 continue;767 };
714 }768 defer tree.deinit();
715769
716 const baf = try io.BufferedAtomicFile.create(allocator, file_path);770 var error_it = tree.errors.iterator(0);
717 defer baf.destroy();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);775 try msg.printToFile(&stderr_file, fmt.color);
720 if (anything_changed) {776 }
721 try stderr.print("{}\n", file_path);777 if (tree.errors.len != 0) {
722 try baf.finish();778 fmt.any_error = true;
723 }779 return;
724 }780 }
725781
726 if (fmt.any_error) {782 // TODO make this evented
727 os.exit(1);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();
728 }790 }
729}791}
730792
src-self-hosted/scope.zig+45-21
...@@ -36,6 +36,7 @@ pub const Scope = struct {...@@ -36,6 +36,7 @@ pub const Scope = struct {
36 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),36 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),
37 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),37 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),
38 Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),38 Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),
39 Id.AstTree => @fieldParentPtr(AstTree, "base", base).destroy(comp),
39 }40 }
40 }41 }
41 }42 }
...@@ -62,6 +63,8 @@ pub const Scope = struct {...@@ -62,6 +63,8 @@ pub const Scope = struct {
62 Id.CompTime,63 Id.CompTime,
63 Id.Var,64 Id.Var,
64 => scope = scope.parent.?,65 => scope = scope.parent.?,
66
67 Id.AstTree => unreachable,
65 }68 }
66 }69 }
67 }70 }
...@@ -82,6 +85,8 @@ pub const Scope = struct {...@@ -82,6 +85,8 @@ pub const Scope = struct {
82 Id.Root,85 Id.Root,
83 Id.Var,86 Id.Var,
84 => scope = scope.parent orelse return null,87 => scope = scope.parent orelse return null,
88
89 Id.AstTree => unreachable,
85 }90 }
86 }91 }
87 }92 }
...@@ -97,6 +102,7 @@ pub const Scope = struct {...@@ -97,6 +102,7 @@ pub const Scope = struct {
97102
98 pub const Id = enum {103 pub const Id = enum {
99 Root,104 Root,
105 AstTree,
100 Decls,106 Decls,
101 Block,107 Block,
102 FnDef,108 FnDef,
...@@ -108,13 +114,12 @@ pub const Scope = struct {...@@ -108,13 +114,12 @@ pub const Scope = struct {
108114
109 pub const Root = struct {115 pub const Root = struct {
110 base: Scope,116 base: Scope,
111 tree: *ast.Tree,
112 realpath: []const u8,117 realpath: []const u8,
118 decls: *Decls,
113119
114 /// Creates a Root scope with 1 reference120 /// Creates a Root scope with 1 reference
115 /// Takes ownership of realpath121 /// Takes ownership of realpath
116 /// Takes ownership of tree, will deinit and destroy when done.122 pub fn create(comp: *Compilation, realpath: []u8) !*Root {
117 pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
118 const self = try comp.gpa().createOne(Root);123 const self = try comp.gpa().createOne(Root);
119 self.* = Root{124 self.* = Root{
120 .base = Scope{125 .base = Scope{
...@@ -122,41 +127,65 @@ pub const Scope = struct {...@@ -122,41 +127,65 @@ pub const Scope = struct {
122 .parent = null,127 .parent = null,
123 .ref_count = std.atomic.Int(usize).init(1),128 .ref_count = std.atomic.Int(usize).init(1),
124 },129 },
125 .tree = tree,
126 .realpath = realpath,130 .realpath = realpath,
131 .decls = undefined,
127 };132 };
128133 errdefer comp.gpa().destroy(self);
134 self.decls = try Decls.create(comp, &self.base);
129 return self;135 return self;
130 }136 }
131137
132 pub fn destroy(self: *Root, comp: *Compilation) void {138 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 {
133 comp.gpa().free(self.tree.source);164 comp.gpa().free(self.tree.source);
134 self.tree.deinit();165 self.tree.deinit();
135 comp.gpa().destroy(self.tree);166 comp.gpa().destroy(self.tree);
136 comp.gpa().free(self.realpath);
137 comp.gpa().destroy(self);167 comp.gpa().destroy(self);
138 }168 }
169
170 pub fn root(self: *AstTree) *Root {
171 return self.base.findRoot();
172 }
139 };173 };
140174
141 pub const Decls = struct {175 pub const Decls = struct {
142 base: Scope,176 base: Scope,
143177
144 /// The lock must be respected for writing. However once name_future resolves,178 /// This table remains Write Locked when the names are incomplete or possibly outdated.
145 /// readers can freely access it.179 /// So if a reader manages to grab a lock, it can be sure that the set of names is complete
146 table: event.Locked(Decl.Table),180 /// and correct.
147181 table: event.RwLocked(Decl.Table),
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),
152182
153 /// Creates a Decls scope with 1 reference183 /// Creates a Decls scope with 1 reference
154 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {184 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
155 const self = try comp.gpa().createOne(Decls);185 const self = try comp.gpa().createOne(Decls);
156 self.* = Decls{186 self.* = Decls{
157 .base = undefined,187 .base = undefined,
158 .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),188 .table = event.RwLocked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
159 .name_future = event.Future(void).init(comp.loop),
160 };189 };
161 self.base.init(Id.Decls, parent);190 self.base.init(Id.Decls, parent);
162 return self;191 return self;
...@@ -166,11 +195,6 @@ pub const Scope = struct {...@@ -166,11 +195,6 @@ pub const Scope = struct {
166 self.table.deinit();195 self.table.deinit();
167 comp.gpa().destroy(self);196 comp.gpa().destroy(self);
168 }197 }
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 }
174 };198 };
175199
176 pub const Block = struct {200 pub const Block = struct {
src-self-hosted/test.zig+18-17
...@@ -6,7 +6,7 @@ const Compilation = @import("compilation.zig").Compilation;...@@ -6,7 +6,7 @@ const Compilation = @import("compilation.zig").Compilation;
6const introspect = @import("introspect.zig");6const introspect = @import("introspect.zig");
7const assertOrPanic = std.debug.assertOrPanic;7const assertOrPanic = std.debug.assertOrPanic;
8const errmsg = @import("errmsg.zig");8const errmsg = @import("errmsg.zig");
9const EventLoopLocal = @import("compilation.zig").EventLoopLocal;9const ZigCompiler = @import("compilation.zig").ZigCompiler;
1010
11var ctx: TestContext = undefined;11var ctx: TestContext = undefined;
1212
...@@ -25,7 +25,7 @@ const allocator = std.heap.c_allocator;...@@ -25,7 +25,7 @@ const allocator = std.heap.c_allocator;
2525
26pub const TestContext = struct {26pub const TestContext = struct {
27 loop: std.event.Loop,27 loop: std.event.Loop,
28 event_loop_local: EventLoopLocal,28 zig_compiler: ZigCompiler,
29 zig_lib_dir: []u8,29 zig_lib_dir: []u8,
30 file_index: std.atomic.Int(usize),30 file_index: std.atomic.Int(usize),
31 group: std.event.Group(error!void),31 group: std.event.Group(error!void),
...@@ -37,20 +37,20 @@ pub const TestContext = struct {...@@ -37,20 +37,20 @@ pub const TestContext = struct {
37 self.* = TestContext{37 self.* = TestContext{
38 .any_err = {},38 .any_err = {},
39 .loop = undefined,39 .loop = undefined,
40 .event_loop_local = undefined,40 .zig_compiler = undefined,
41 .zig_lib_dir = undefined,41 .zig_lib_dir = undefined,
42 .group = undefined,42 .group = undefined,
43 .file_index = std.atomic.Int(usize).init(0),43 .file_index = std.atomic.Int(usize).init(0),
44 };44 };
4545
46 try self.loop.initMultiThreaded(allocator);46 try self.loop.initSingleThreaded(allocator);
47 errdefer self.loop.deinit();47 errdefer self.loop.deinit();
4848
49 self.event_loop_local = try EventLoopLocal.init(&self.loop);49 self.zig_compiler = try ZigCompiler.init(&self.loop);
50 errdefer self.event_loop_local.deinit();50 errdefer self.zig_compiler.deinit();
5151
52 self.group = std.event.Group(error!void).init(&self.loop);52 self.group = std.event.Group(error!void).init(&self.loop);
53 errdefer self.group.cancelAll();53 errdefer self.group.deinit();
5454
55 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);55 self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
56 errdefer allocator.free(self.zig_lib_dir);56 errdefer allocator.free(self.zig_lib_dir);
...@@ -62,7 +62,7 @@ pub const TestContext = struct {...@@ -62,7 +62,7 @@ pub const TestContext = struct {
62 fn deinit(self: *TestContext) void {62 fn deinit(self: *TestContext) void {
63 std.os.deleteTree(allocator, tmp_dir_name) catch {};63 std.os.deleteTree(allocator, tmp_dir_name) catch {};
64 allocator.free(self.zig_lib_dir);64 allocator.free(self.zig_lib_dir);
65 self.event_loop_local.deinit();65 self.zig_compiler.deinit();
66 self.loop.deinit();66 self.loop.deinit();
67 }67 }
6868
...@@ -94,10 +94,10 @@ pub const TestContext = struct {...@@ -94,10 +94,10 @@ pub const TestContext = struct {
94 }94 }
9595
96 // TODO async I/O96 // TODO async I/O
97 try std.io.writeFile(allocator, file1_path, source);97 try std.io.writeFile(file1_path, source);
9898
99 var comp = try Compilation.create(99 var comp = try Compilation.create(
100 &self.event_loop_local,100 &self.zig_compiler,
101 "test",101 "test",
102 file1_path,102 file1_path,
103 Target.Native,103 Target.Native,
...@@ -108,7 +108,7 @@ pub const TestContext = struct {...@@ -108,7 +108,7 @@ pub const TestContext = struct {
108 );108 );
109 errdefer comp.destroy();109 errdefer comp.destroy();
110110
111 try comp.build();111 comp.start();
112112
113 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);113 try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
114 }114 }
...@@ -128,10 +128,10 @@ pub const TestContext = struct {...@@ -128,10 +128,10 @@ pub const TestContext = struct {
128 }128 }
129129
130 // TODO async I/O130 // TODO async I/O
131 try std.io.writeFile(allocator, file1_path, source);131 try std.io.writeFile(file1_path, source);
132132
133 var comp = try Compilation.create(133 var comp = try Compilation.create(
134 &self.event_loop_local,134 &self.zig_compiler,
135 "test",135 "test",
136 file1_path,136 file1_path,
137 Target.Native,137 Target.Native,
...@@ -144,7 +144,7 @@ pub const TestContext = struct {...@@ -144,7 +144,7 @@ pub const TestContext = struct {
144144
145 _ = try comp.addLinkLib("c", true);145 _ = try comp.addLinkLib("c", true);
146 comp.link_out_file = output_file;146 comp.link_out_file = output_file;
147 try comp.build();147 comp.start();
148148
149 try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);149 try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
150 }150 }
...@@ -212,9 +212,10 @@ pub const TestContext = struct {...@@ -212,9 +212,10 @@ pub const TestContext = struct {
212 Compilation.Event.Fail => |msgs| {212 Compilation.Event.Fail => |msgs| {
213 assertOrPanic(msgs.len != 0);213 assertOrPanic(msgs.len != 0);
214 for (msgs) |msg| {214 for (msgs) |msg| {
215 if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {215 if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) {
216 const first_token = msg.getTree().tokens.at(msg.span.first);216 const span = msg.getSpan();
217 const last_token = msg.getTree().tokens.at(msg.span.first);217 const first_token = msg.getTree().tokens.at(span.first);
218 const last_token = msg.getTree().tokens.at(span.first);
218 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);219 const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
219 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {220 if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
220 return;221 return;
src-self-hosted/type.zig+2-2
...@@ -184,8 +184,8 @@ pub const Type = struct {...@@ -184,8 +184,8 @@ pub const Type = struct {
184 if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;184 if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
185185
186 {186 {
187 const held = try comp.event_loop_local.getAnyLlvmContext();187 const held = try comp.zig_compiler.getAnyLlvmContext();
188 defer held.release(comp.event_loop_local);188 defer held.release(comp.zig_compiler);
189189
190 const llvm_context = held.node.data;190 const llvm_context = held.node.data;
191191
src/all_types.hpp+2-2
...@@ -1850,7 +1850,7 @@ struct ScopeDecls {...@@ -1850,7 +1850,7 @@ struct ScopeDecls {
1850 HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> decl_table;1850 HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> decl_table;
1851 bool safety_off;1851 bool safety_off;
1852 AstNode *safety_set_node;1852 AstNode *safety_set_node;
1853 bool fast_math_off;1853 bool fast_math_on;
1854 AstNode *fast_math_set_node;1854 AstNode *fast_math_set_node;
1855 ImportTableEntry *import;1855 ImportTableEntry *import;
1856 // If this is a scope from a container, this is the type entry, otherwise null1856 // If this is a scope from a container, this is the type entry, otherwise null
...@@ -1870,7 +1870,7 @@ struct ScopeBlock {...@@ -1870,7 +1870,7 @@ struct ScopeBlock {
18701870
1871 bool safety_off;1871 bool safety_off;
1872 AstNode *safety_set_node;1872 AstNode *safety_set_node;
1873 bool fast_math_off;1873 bool fast_math_on;
1874 AstNode *fast_math_set_node;1874 AstNode *fast_math_set_node;
1875};1875};
18761876
src/analyze.cpp+130-86
...@@ -19,12 +19,12 @@...@@ -19,12 +19,12 @@
1919
20static const size_t default_backward_branch_quota = 1000;20static const size_t default_backward_branch_quota = 1000;
2121
22static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type);22static Error resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type);
23static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type);23static Error resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type);
2424
25static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type);25static Error ATTRIBUTE_MUST_USE resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type);
26static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type);26static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type);
27static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type);27static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type);
28static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry);28static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry);
2929
30ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {30ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
...@@ -370,15 +370,20 @@ uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry) {...@@ -370,15 +370,20 @@ uint64_t type_size_bits(CodeGen *g, TypeTableEntry *type_entry) {
370 return LLVMSizeOfTypeInBits(g->target_data_ref, type_entry->type_ref);370 return LLVMSizeOfTypeInBits(g->target_data_ref, type_entry->type_ref);
371}371}
372372
373bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry) {373Result<bool> type_is_copyable(CodeGen *g, TypeTableEntry *type_entry) {
374 type_ensure_zero_bits_known(g, type_entry);374 Error err;
375 if ((err = type_ensure_zero_bits_known(g, type_entry)))
376 return err;
377
375 if (!type_has_bits(type_entry))378 if (!type_has_bits(type_entry))
376 return true;379 return true;
377380
378 if (!handle_is_ptr(type_entry))381 if (!handle_is_ptr(type_entry))
379 return true;382 return true;
380383
381 ensure_complete_type(g, type_entry);384 if ((err = ensure_complete_type(g, type_entry)))
385 return err;
386
382 return type_entry->is_copyable;387 return type_entry->is_copyable;
383}388}
384389
...@@ -447,7 +452,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type...@@ -447,7 +452,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type
447 }452 }
448 }453 }
449454
450 type_ensure_zero_bits_known(g, child_type);455 assertNoError(type_ensure_zero_bits_known(g, child_type));
451456
452 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer);457 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPointer);
453 entry->is_copyable = true;458 entry->is_copyable = true;
...@@ -554,11 +559,11 @@ TypeTableEntry *get_optional_type(CodeGen *g, TypeTableEntry *child_type) {...@@ -554,11 +559,11 @@ TypeTableEntry *get_optional_type(CodeGen *g, TypeTableEntry *child_type) {
554 TypeTableEntry *entry = child_type->optional_parent;559 TypeTableEntry *entry = child_type->optional_parent;
555 return entry;560 return entry;
556 } else {561 } else {
557 ensure_complete_type(g, child_type);562 assertNoError(ensure_complete_type(g, child_type));
558563
559 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdOptional);564 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdOptional);
560 assert(child_type->type_ref || child_type->zero_bits);565 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
563 buf_resize(&entry->name, 0);568 buf_resize(&entry->name, 0);
564 buf_appendf(&entry->name, "?%s", buf_ptr(&child_type->name));569 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...@@ -650,7 +655,7 @@ TypeTableEntry *get_error_union_type(CodeGen *g, TypeTableEntry *err_set_type, T
650 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion);655 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion);
651 entry->is_copyable = true;656 entry->is_copyable = true;
652 assert(payload_type->di_type);657 assert(payload_type->di_type);
653 ensure_complete_type(g, payload_type);658 assertNoError(ensure_complete_type(g, payload_type));
654659
655 buf_resize(&entry->name, 0);660 buf_resize(&entry->name, 0);
656 buf_appendf(&entry->name, "%s!%s", buf_ptr(&err_set_type->name), buf_ptr(&payload_type->name));661 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...@@ -739,7 +744,7 @@ TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t
739 return entry;744 return entry;
740 }745 }
741746
742 ensure_complete_type(g, child_type);747 assertNoError(ensure_complete_type(g, child_type));
743748
744 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);749 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
745 entry->zero_bits = (array_size == 0) || child_type->zero_bits;750 entry->zero_bits = (array_size == 0) || child_type->zero_bits;
...@@ -1050,13 +1055,13 @@ TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) {...@@ -1050,13 +1055,13 @@ TypeTableEntry *get_ptr_to_stack_trace_type(CodeGen *g) {
1050}1055}
10511056
1052TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {1057TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
1058 Error err;
1053 auto table_entry = g->fn_type_table.maybe_get(fn_type_id);1059 auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
1054 if (table_entry) {1060 if (table_entry) {
1055 return table_entry->value;1061 return table_entry->value;
1056 }1062 }
1057 if (fn_type_id->return_type != nullptr) {1063 if (fn_type_id->return_type != nullptr) {
1058 ensure_complete_type(g, fn_type_id->return_type);1064 if ((err = ensure_complete_type(g, fn_type_id->return_type)))
1059 if (type_is_invalid(fn_type_id->return_type))
1060 return g->builtin_types.entry_invalid;1065 return g->builtin_types.entry_invalid;
1061 assert(fn_type_id->return_type->id != TypeTableEntryIdOpaque);1066 assert(fn_type_id->return_type->id != TypeTableEntryIdOpaque);
1062 } else {1067 } else {
...@@ -1172,8 +1177,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {...@@ -1172,8 +1177,7 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
1172 gen_param_info->src_index = i;1177 gen_param_info->src_index = i;
1173 gen_param_info->gen_index = SIZE_MAX;1178 gen_param_info->gen_index = SIZE_MAX;
11741179
1175 ensure_complete_type(g, type_entry);1180 if ((err = ensure_complete_type(g, type_entry)))
1176 if (type_is_invalid(type_entry))
1177 return g->builtin_types.entry_invalid;1181 return g->builtin_types.entry_invalid;
11781182
1179 if (type_has_bits(type_entry)) {1183 if (type_has_bits(type_entry)) {
...@@ -1493,6 +1497,7 @@ TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry) {...@@ -1493,6 +1497,7 @@ TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry) {
1493static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, FnTableEntry *fn_entry) {1497static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope, FnTableEntry *fn_entry) {
1494 assert(proto_node->type == NodeTypeFnProto);1498 assert(proto_node->type == NodeTypeFnProto);
1495 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;1499 AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
1500 Error err;
14961501
1497 FnTypeId fn_type_id = {0};1502 FnTypeId fn_type_id = {0};
1498 init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);1503 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...@@ -1550,7 +1555,8 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1550 return g->builtin_types.entry_invalid;1555 return g->builtin_types.entry_invalid;
1551 }1556 }
1552 if (!calling_convention_allows_zig_types(fn_type_id.cc)) {1557 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;
1554 if (!type_has_bits(type_entry)) {1560 if (!type_has_bits(type_entry)) {
1555 add_node_error(g, param_node->data.param_decl.type,1561 add_node_error(g, param_node->data.param_decl.type,
1556 buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",1562 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...@@ -1598,7 +1604,8 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1598 case TypeTableEntryIdUnion:1604 case TypeTableEntryIdUnion:
1599 case TypeTableEntryIdFn:1605 case TypeTableEntryIdFn:
1600 case TypeTableEntryIdPromise:1606 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;
1602 if (type_requires_comptime(type_entry)) {1609 if (type_requires_comptime(type_entry)) {
1603 add_node_error(g, param_node->data.param_decl.type,1610 add_node_error(g, param_node->data.param_decl.type,
1604 buf_sprintf("parameter of type '%s' must be declared comptime",1611 buf_sprintf("parameter of type '%s' must be declared comptime",
...@@ -1729,24 +1736,28 @@ bool type_is_invalid(TypeTableEntry *type_entry) {...@@ -1729,24 +1736,28 @@ bool type_is_invalid(TypeTableEntry *type_entry) {
1729}1736}
17301737
17311738
1732static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {1739static Error resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
1733 assert(enum_type->id == TypeTableEntryIdEnum);1740 assert(enum_type->id == TypeTableEntryIdEnum);
17341741
1742 if (enum_type->data.enumeration.is_invalid)
1743 return ErrorSemanticAnalyzeFail;
1744
1735 if (enum_type->data.enumeration.complete)1745 if (enum_type->data.enumeration.complete)
1736 return;1746 return ErrorNone;
17371747
1738 resolve_enum_zero_bits(g, enum_type);1748 Error err;
1739 if (type_is_invalid(enum_type))1749 if ((err = resolve_enum_zero_bits(g, enum_type)))
1740 return;1750 return err;
17411751
1742 AstNode *decl_node = enum_type->data.enumeration.decl_node;1752 AstNode *decl_node = enum_type->data.enumeration.decl_node;
17431753
1744 if (enum_type->data.enumeration.embedded_in_current) {1754 if (enum_type->data.enumeration.embedded_in_current) {
1745 if (!enum_type->data.enumeration.reported_infinite_err) {1755 if (!enum_type->data.enumeration.reported_infinite_err) {
1756 enum_type->data.enumeration.is_invalid = true;
1746 enum_type->data.enumeration.reported_infinite_err = true;1757 enum_type->data.enumeration.reported_infinite_err = true;
1747 add_node_error(g, decl_node, buf_sprintf("enum '%s' contains itself", buf_ptr(&enum_type->name)));1758 add_node_error(g, decl_node, buf_sprintf("enum '%s' contains itself", buf_ptr(&enum_type->name)));
1748 }1759 }
1749 return;1760 return ErrorSemanticAnalyzeFail;
1750 }1761 }
17511762
1752 assert(!enum_type->data.enumeration.zero_bits_loop_flag);1763 assert(!enum_type->data.enumeration.zero_bits_loop_flag);
...@@ -1778,7 +1789,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {...@@ -1778,7 +1789,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
1778 enum_type->data.enumeration.complete = true;1789 enum_type->data.enumeration.complete = true;
17791790
1780 if (enum_type->data.enumeration.is_invalid)1791 if (enum_type->data.enumeration.is_invalid)
1781 return;1792 return ErrorSemanticAnalyzeFail;
17821793
1783 if (enum_type->zero_bits) {1794 if (enum_type->zero_bits) {
1784 enum_type->type_ref = LLVMVoidType();1795 enum_type->type_ref = LLVMVoidType();
...@@ -1797,7 +1808,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {...@@ -1797,7 +1808,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
17971808
1798 ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type);1809 ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, replacement_di_type);
1799 enum_type->di_type = replacement_di_type;1810 enum_type->di_type = replacement_di_type;
1800 return;1811 return ErrorNone;
1801 }1812 }
18021813
1803 TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type;1814 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) {...@@ -1815,6 +1826,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
18151826
1816 ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type);1827 ZigLLVMReplaceTemporary(g->dbuilder, enum_type->di_type, tag_di_type);
1817 enum_type->di_type = tag_di_type;1828 enum_type->di_type = tag_di_type;
1829 return ErrorNone;
1818}1830}
18191831
18201832
...@@ -1897,15 +1909,15 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f...@@ -1897,15 +1909,15 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f
1897 return struct_type;1909 return struct_type;
1898}1910}
18991911
1900static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {1912static Error resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
1901 assert(struct_type->id == TypeTableEntryIdStruct);1913 assert(struct_type->id == TypeTableEntryIdStruct);
19021914
1903 if (struct_type->data.structure.complete)1915 if (struct_type->data.structure.complete)
1904 return;1916 return ErrorNone;
19051917
1906 resolve_struct_zero_bits(g, struct_type);1918 Error err;
1907 if (struct_type->data.structure.is_invalid)1919 if ((err = resolve_struct_zero_bits(g, struct_type)))
1908 return;1920 return err;
19091921
1910 AstNode *decl_node = struct_type->data.structure.decl_node;1922 AstNode *decl_node = struct_type->data.structure.decl_node;
19111923
...@@ -1916,7 +1928,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {...@@ -1916,7 +1928,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
1916 add_node_error(g, decl_node,1928 add_node_error(g, decl_node,
1917 buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name)));1929 buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name)));
1918 }1930 }
1919 return;1931 return ErrorSemanticAnalyzeFail;
1920 }1932 }
19211933
1922 assert(!struct_type->data.structure.zero_bits_loop_flag);1934 assert(!struct_type->data.structure.zero_bits_loop_flag);
...@@ -1943,8 +1955,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {...@@ -1943,8 +1955,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
1943 TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];1955 TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
1944 TypeTableEntry *field_type = type_struct_field->type_entry;1956 TypeTableEntry *field_type = type_struct_field->type_entry;
19451957
1946 ensure_complete_type(g, field_type);1958 if ((err = ensure_complete_type(g, field_type))) {
1947 if (type_is_invalid(field_type)) {
1948 struct_type->data.structure.is_invalid = true;1959 struct_type->data.structure.is_invalid = true;
1949 break;1960 break;
1950 }1961 }
...@@ -2026,7 +2037,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {...@@ -2026,7 +2037,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
2026 struct_type->data.structure.complete = true;2037 struct_type->data.structure.complete = true;
20272038
2028 if (struct_type->data.structure.is_invalid)2039 if (struct_type->data.structure.is_invalid)
2029 return;2040 return ErrorSemanticAnalyzeFail;
20302041
2031 if (struct_type->zero_bits) {2042 if (struct_type->zero_bits) {
2032 struct_type->type_ref = LLVMVoidType();2043 struct_type->type_ref = LLVMVoidType();
...@@ -2045,7 +2056,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {...@@ -2045,7 +2056,7 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
2045 0, nullptr, di_element_types, (int)debug_field_count, 0, nullptr, "");2056 0, nullptr, di_element_types, (int)debug_field_count, 0, nullptr, "");
2046 ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type);2057 ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type);
2047 struct_type->di_type = replacement_di_type;2058 struct_type->di_type = replacement_di_type;
2048 return;2059 return ErrorNone;
2049 }2060 }
2050 assert(struct_type->di_type);2061 assert(struct_type->di_type);
20512062
...@@ -2128,17 +2139,19 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {...@@ -2128,17 +2139,19 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
21282139
2129 ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type);2140 ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type);
2130 struct_type->di_type = replacement_di_type;2141 struct_type->di_type = replacement_di_type;
2142
2143 return ErrorNone;
2131}2144}
21322145
2133static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {2146static Error resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
2134 assert(union_type->id == TypeTableEntryIdUnion);2147 assert(union_type->id == TypeTableEntryIdUnion);
21352148
2136 if (union_type->data.unionation.complete)2149 if (union_type->data.unionation.complete)
2137 return;2150 return ErrorNone;
21382151
2139 resolve_union_zero_bits(g, union_type);2152 Error err;
2140 if (type_is_invalid(union_type))2153 if ((err = resolve_union_zero_bits(g, union_type)))
2141 return;2154 return err;
21422155
2143 AstNode *decl_node = union_type->data.unionation.decl_node;2156 AstNode *decl_node = union_type->data.unionation.decl_node;
21442157
...@@ -2148,7 +2161,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {...@@ -2148,7 +2161,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
2148 union_type->data.unionation.is_invalid = true;2161 union_type->data.unionation.is_invalid = true;
2149 add_node_error(g, decl_node, buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name)));2162 add_node_error(g, decl_node, buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name)));
2150 }2163 }
2151 return;2164 return ErrorSemanticAnalyzeFail;
2152 }2165 }
21532166
2154 assert(!union_type->data.unionation.zero_bits_loop_flag);2167 assert(!union_type->data.unionation.zero_bits_loop_flag);
...@@ -2179,8 +2192,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {...@@ -2179,8 +2192,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
2179 TypeUnionField *union_field = &union_type->data.unionation.fields[i];2192 TypeUnionField *union_field = &union_type->data.unionation.fields[i];
2180 TypeTableEntry *field_type = union_field->type_entry;2193 TypeTableEntry *field_type = union_field->type_entry;
21812194
2182 ensure_complete_type(g, field_type);2195 if ((err = ensure_complete_type(g, field_type))) {
2183 if (type_is_invalid(field_type)) {
2184 union_type->data.unionation.is_invalid = true;2196 union_type->data.unionation.is_invalid = true;
2185 continue;2197 continue;
2186 }2198 }
...@@ -2219,7 +2231,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {...@@ -2219,7 +2231,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
2219 union_type->data.unionation.most_aligned_union_member = most_aligned_union_member;2231 union_type->data.unionation.most_aligned_union_member = most_aligned_union_member;
22202232
2221 if (union_type->data.unionation.is_invalid)2233 if (union_type->data.unionation.is_invalid)
2222 return;2234 return ErrorSemanticAnalyzeFail;
22232235
2224 if (union_type->zero_bits) {2236 if (union_type->zero_bits) {
2225 union_type->type_ref = LLVMVoidType();2237 union_type->type_ref = LLVMVoidType();
...@@ -2238,7 +2250,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {...@@ -2238,7 +2250,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
22382250
2239 ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);2251 ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
2240 union_type->di_type = replacement_di_type;2252 union_type->di_type = replacement_di_type;
2241 return;2253 return ErrorNone;
2242 }2254 }
22432255
2244 uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits;2256 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) {...@@ -2274,7 +2286,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
22742286
2275 ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);2287 ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
2276 union_type->di_type = replacement_di_type;2288 union_type->di_type = replacement_di_type;
2277 return;2289 return ErrorNone;
2278 }2290 }
22792291
2280 LLVMTypeRef union_type_ref;2292 LLVMTypeRef union_type_ref;
...@@ -2293,7 +2305,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {...@@ -2293,7 +2305,7 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
22932305
2294 ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, tag_type->di_type);2306 ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, tag_type->di_type);
2295 union_type->di_type = tag_type->di_type;2307 union_type->di_type = tag_type->di_type;
2296 return;2308 return ErrorNone;
2297 } else {2309 } else {
2298 union_type_ref = most_aligned_union_member->type_ref;2310 union_type_ref = most_aligned_union_member->type_ref;
2299 }2311 }
...@@ -2367,19 +2379,21 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {...@@ -2367,19 +2379,21 @@ static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
23672379
2368 ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);2380 ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
2369 union_type->di_type = replacement_di_type;2381 union_type->di_type = replacement_di_type;
2382
2383 return ErrorNone;
2370}2384}
23712385
2372static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {2386static Error resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
2373 assert(enum_type->id == TypeTableEntryIdEnum);2387 assert(enum_type->id == TypeTableEntryIdEnum);
23742388
2375 if (enum_type->data.enumeration.zero_bits_known)2389 if (enum_type->data.enumeration.zero_bits_known)
2376 return;2390 return ErrorNone;
23772391
2378 if (enum_type->data.enumeration.zero_bits_loop_flag) {2392 if (enum_type->data.enumeration.zero_bits_loop_flag) {
2379 add_node_error(g, enum_type->data.enumeration.decl_node,2393 add_node_error(g, enum_type->data.enumeration.decl_node,
2380 buf_sprintf("'%s' depends on itself", buf_ptr(&enum_type->name)));2394 buf_sprintf("'%s' depends on itself", buf_ptr(&enum_type->name)));
2381 enum_type->data.enumeration.is_invalid = true;2395 enum_type->data.enumeration.is_invalid = true;
2382 return;2396 return ErrorSemanticAnalyzeFail;
2383 }2397 }
23842398
2385 enum_type->data.enumeration.zero_bits_loop_flag = true;2399 enum_type->data.enumeration.zero_bits_loop_flag = true;
...@@ -2398,7 +2412,7 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {...@@ -2398,7 +2412,7 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
2398 enum_type->data.enumeration.is_invalid = true;2412 enum_type->data.enumeration.is_invalid = true;
2399 enum_type->data.enumeration.zero_bits_loop_flag = false;2413 enum_type->data.enumeration.zero_bits_loop_flag = false;
2400 enum_type->data.enumeration.zero_bits_known = true;2414 enum_type->data.enumeration.zero_bits_known = true;
2401 return;2415 return ErrorSemanticAnalyzeFail;
2402 }2416 }
24032417
2404 enum_type->data.enumeration.src_field_count = field_count;2418 enum_type->data.enumeration.src_field_count = field_count;
...@@ -2525,13 +2539,23 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {...@@ -2525,13 +2539,23 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
2525 enum_type->data.enumeration.zero_bits_loop_flag = false;2539 enum_type->data.enumeration.zero_bits_loop_flag = false;
2526 enum_type->zero_bits = !type_has_bits(tag_int_type);2540 enum_type->zero_bits = !type_has_bits(tag_int_type);
2527 enum_type->data.enumeration.zero_bits_known = true;2541 enum_type->data.enumeration.zero_bits_known = true;
2542
2543 if (enum_type->data.enumeration.is_invalid)
2544 return ErrorSemanticAnalyzeFail;
2545
2546 return ErrorNone;
2528}2547}
25292548
2530static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {2549static Error resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
2531 assert(struct_type->id == TypeTableEntryIdStruct);2550 assert(struct_type->id == TypeTableEntryIdStruct);
25322551
2552 Error err;
2553
2554 if (struct_type->data.structure.is_invalid)
2555 return ErrorSemanticAnalyzeFail;
2556
2533 if (struct_type->data.structure.zero_bits_known)2557 if (struct_type->data.structure.zero_bits_known)
2534 return;2558 return ErrorNone;
25352559
2536 if (struct_type->data.structure.zero_bits_loop_flag) {2560 if (struct_type->data.structure.zero_bits_loop_flag) {
2537 // If we get here it's due to recursion. This is a design flaw in the compiler,2561 // 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) {...@@ -2547,7 +2571,7 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
2547 struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0));2571 struct_type->data.structure.abi_alignment = LLVMABIAlignmentOfType(g->target_data_ref, LLVMPointerType(LLVMInt8Type(), 0));
2548 }2572 }
2549 }2573 }
2550 return;2574 return ErrorNone;
2551 }2575 }
25522576
2553 struct_type->data.structure.zero_bits_loop_flag = true;2577 struct_type->data.structure.zero_bits_loop_flag = true;
...@@ -2596,8 +2620,7 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {...@@ -2596,8 +2620,7 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
2596 buf_sprintf("enums, not structs, support field assignment"));2620 buf_sprintf("enums, not structs, support field assignment"));
2597 }2621 }
25982622
2599 type_ensure_zero_bits_known(g, field_type);2623 if ((err = type_ensure_zero_bits_known(g, field_type))) {
2600 if (type_is_invalid(field_type)) {
2601 struct_type->data.structure.is_invalid = true;2624 struct_type->data.structure.is_invalid = true;
2602 continue;2625 continue;
2603 }2626 }
...@@ -2634,16 +2657,27 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {...@@ -2634,16 +2657,27 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
2634 struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index;2657 struct_type->data.structure.gen_field_count = (uint32_t)gen_field_index;
2635 struct_type->zero_bits = (gen_field_index == 0);2658 struct_type->zero_bits = (gen_field_index == 0);
2636 struct_type->data.structure.zero_bits_known = true;2659 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;
2637}2666}
26382667
2639static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {2668static Error resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
2640 assert(union_type->id == TypeTableEntryIdUnion);2669 assert(union_type->id == TypeTableEntryIdUnion);
26412670
2671 Error err;
2672
2673 if (union_type->data.unionation.is_invalid)
2674 return ErrorSemanticAnalyzeFail;
2675
2642 if (union_type->data.unionation.zero_bits_known)2676 if (union_type->data.unionation.zero_bits_known)
2643 return;2677 return ErrorNone;
26442678
2645 if (type_is_invalid(union_type))2679 if (type_is_invalid(union_type))
2646 return;2680 return ErrorSemanticAnalyzeFail;
26472681
2648 if (union_type->data.unionation.zero_bits_loop_flag) {2682 if (union_type->data.unionation.zero_bits_loop_flag) {
2649 // If we get here it's due to recursion. From this we conclude that the struct is2683 // 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) {...@@ -2660,7 +2694,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
2660 LLVMPointerType(LLVMInt8Type(), 0));2694 LLVMPointerType(LLVMInt8Type(), 0));
2661 }2695 }
2662 }2696 }
2663 return;2697 return ErrorNone;
2664 }2698 }
26652699
2666 union_type->data.unionation.zero_bits_loop_flag = true;2700 union_type->data.unionation.zero_bits_loop_flag = true;
...@@ -2679,7 +2713,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {...@@ -2679,7 +2713,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
2679 union_type->data.unionation.is_invalid = true;2713 union_type->data.unionation.is_invalid = true;
2680 union_type->data.unionation.zero_bits_loop_flag = false;2714 union_type->data.unionation.zero_bits_loop_flag = false;
2681 union_type->data.unionation.zero_bits_known = true;2715 union_type->data.unionation.zero_bits_known = true;
2682 return;2716 return ErrorSemanticAnalyzeFail;
2683 }2717 }
2684 union_type->data.unionation.src_field_count = field_count;2718 union_type->data.unionation.src_field_count = field_count;
2685 union_type->data.unionation.fields = allocate<TypeUnionField>(field_count);2719 union_type->data.unionation.fields = allocate<TypeUnionField>(field_count);
...@@ -2711,13 +2745,13 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {...@@ -2711,13 +2745,13 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
2711 tag_int_type = analyze_type_expr(g, scope, enum_type_node);2745 tag_int_type = analyze_type_expr(g, scope, enum_type_node);
2712 if (type_is_invalid(tag_int_type)) {2746 if (type_is_invalid(tag_int_type)) {
2713 union_type->data.unionation.is_invalid = true;2747 union_type->data.unionation.is_invalid = true;
2714 return;2748 return ErrorSemanticAnalyzeFail;
2715 }2749 }
2716 if (tag_int_type->id != TypeTableEntryIdInt) {2750 if (tag_int_type->id != TypeTableEntryIdInt) {
2717 add_node_error(g, enum_type_node,2751 add_node_error(g, enum_type_node,
2718 buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name)));2752 buf_sprintf("expected integer tag type, found '%s'", buf_ptr(&tag_int_type->name)));
2719 union_type->data.unionation.is_invalid = true;2753 union_type->data.unionation.is_invalid = true;
2720 return;2754 return ErrorSemanticAnalyzeFail;
2721 }2755 }
2722 } else {2756 } else {
2723 tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);2757 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) {...@@ -2744,13 +2778,13 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
2744 TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node);2778 TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node);
2745 if (type_is_invalid(enum_type)) {2779 if (type_is_invalid(enum_type)) {
2746 union_type->data.unionation.is_invalid = true;2780 union_type->data.unionation.is_invalid = true;
2747 return;2781 return ErrorSemanticAnalyzeFail;
2748 }2782 }
2749 if (enum_type->id != TypeTableEntryIdEnum) {2783 if (enum_type->id != TypeTableEntryIdEnum) {
2750 union_type->data.unionation.is_invalid = true;2784 union_type->data.unionation.is_invalid = true;
2751 add_node_error(g, enum_type_node,2785 add_node_error(g, enum_type_node,
2752 buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));2786 buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));
2753 return;2787 return ErrorSemanticAnalyzeFail;
2754 }2788 }
2755 tag_type = enum_type;2789 tag_type = enum_type;
2756 abi_alignment_so_far = get_abi_alignment(g, enum_type); // this populates src_field_count2790 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) {...@@ -2789,8 +2823,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
2789 }2823 }
2790 } else {2824 } else {
2791 field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);2825 field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
2792 type_ensure_zero_bits_known(g, field_type);2826 if ((err = type_ensure_zero_bits_known(g, field_type))) {
2793 if (type_is_invalid(field_type)) {
2794 union_type->data.unionation.is_invalid = true;2827 union_type->data.unionation.is_invalid = true;
2795 continue;2828 continue;
2796 }2829 }
...@@ -2883,7 +2916,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {...@@ -2883,7 +2916,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
2883 union_type->data.unionation.abi_alignment = abi_alignment_so_far;2916 union_type->data.unionation.abi_alignment = abi_alignment_so_far;
28842917
2885 if (union_type->data.unionation.is_invalid)2918 if (union_type->data.unionation.is_invalid)
2886 return;2919 return ErrorSemanticAnalyzeFail;
28872920
2888 bool src_have_tag = decl_node->data.container_decl.auto_enum ||2921 bool src_have_tag = decl_node->data.container_decl.auto_enum ||
2889 decl_node->data.container_decl.init_arg_expr != nullptr;2922 decl_node->data.container_decl.init_arg_expr != nullptr;
...@@ -2905,7 +2938,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {...@@ -2905,7 +2938,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
2905 add_node_error(g, source_node,2938 add_node_error(g, source_node,
2906 buf_sprintf("%s union does not support enum tag type", qual_str));2939 buf_sprintf("%s union does not support enum tag type", qual_str));
2907 union_type->data.unionation.is_invalid = true;2940 union_type->data.unionation.is_invalid = true;
2908 return;2941 return ErrorSemanticAnalyzeFail;
2909 }2942 }
29102943
2911 if (create_enum_type) {2944 if (create_enum_type) {
...@@ -2970,6 +3003,11 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {...@@ -2970,6 +3003,11 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
2970 union_type->data.unionation.gen_field_count = gen_field_index;3003 union_type->data.unionation.gen_field_count = gen_field_index;
2971 union_type->zero_bits = (gen_field_index == 0 && (field_count < 2 || !src_have_tag));3004 union_type->zero_bits = (gen_field_index == 0 && (field_count < 2 || !src_have_tag));
2972 union_type->data.unionation.zero_bits_known = true;3005 union_type->data.unionation.zero_bits_known = true;
3006
3007 if (union_type->data.unionation.is_invalid)
3008 return ErrorSemanticAnalyzeFail;
3009
3010 return ErrorNone;
2973}3011}
29743012
2975static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) {3013static 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) {...@@ -3035,7 +3073,7 @@ static bool scope_is_root_decls(Scope *scope) {
30353073
3036static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntry *fn_type) {3074static void wrong_panic_prototype(CodeGen *g, AstNode *proto_node, TypeTableEntry *fn_type) {
3037 add_node_error(g, proto_node,3075 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'",
3039 buf_ptr(&fn_type->name)));3077 buf_ptr(&fn_type->name)));
3040}3078}
30413079
...@@ -3463,13 +3501,13 @@ VariableTableEntry *add_variable(CodeGen *g, AstNode *source_node, Scope *parent...@@ -3463,13 +3501,13 @@ VariableTableEntry *add_variable(CodeGen *g, AstNode *source_node, Scope *parent
3463 variable_entry->shadowable = false;3501 variable_entry->shadowable = false;
3464 variable_entry->mem_slot_index = SIZE_MAX;3502 variable_entry->mem_slot_index = SIZE_MAX;
3465 variable_entry->src_arg_index = SIZE_MAX;3503 variable_entry->src_arg_index = SIZE_MAX;
3466 variable_entry->align_bytes = get_abi_alignment(g, value->type);
34673504
3468 assert(name);3505 assert(name);
3469
3470 buf_init_from_buf(&variable_entry->name, name);3506 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
3473 VariableTableEntry *existing_var = find_variable(g, parent_scope, name);3511 VariableTableEntry *existing_var = find_variable(g, parent_scope, name);
3474 if (existing_var && !existing_var->shadowable) {3512 if (existing_var && !existing_var->shadowable) {
3475 ErrorMsg *msg = add_node_error(g, source_node,3513 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_...@@ -5311,13 +5349,13 @@ ConstExprValue *create_const_arg_tuple(CodeGen *g, size_t arg_index_start, size_
53115349
53125350
5313void init_const_undefined(CodeGen *g, ConstExprValue *const_val) {5351void init_const_undefined(CodeGen *g, ConstExprValue *const_val) {
5352 Error err;
5314 TypeTableEntry *wanted_type = const_val->type;5353 TypeTableEntry *wanted_type = const_val->type;
5315 if (wanted_type->id == TypeTableEntryIdArray) {5354 if (wanted_type->id == TypeTableEntryIdArray) {
5316 const_val->special = ConstValSpecialStatic;5355 const_val->special = ConstValSpecialStatic;
5317 const_val->data.x_array.special = ConstArraySpecialUndef;5356 const_val->data.x_array.special = ConstArraySpecialUndef;
5318 } else if (wanted_type->id == TypeTableEntryIdStruct) {5357 } else if (wanted_type->id == TypeTableEntryIdStruct) {
5319 ensure_complete_type(g, wanted_type);5358 if ((err = ensure_complete_type(g, wanted_type))) {
5320 if (type_is_invalid(wanted_type)) {
5321 return;5359 return;
5322 }5360 }
53235361
...@@ -5350,27 +5388,33 @@ ConstExprValue *create_const_vals(size_t count) {...@@ -5350,27 +5388,33 @@ ConstExprValue *create_const_vals(size_t count) {
5350 return vals;5388 return vals;
5351}5389}
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;
5354 if (type_entry->id == TypeTableEntryIdStruct) {5394 if (type_entry->id == TypeTableEntryIdStruct) {
5355 if (!type_entry->data.structure.complete)5395 if (!type_entry->data.structure.complete)
5356 resolve_struct_type(g, type_entry);5396 return resolve_struct_type(g, type_entry);
5357 } else if (type_entry->id == TypeTableEntryIdEnum) {5397 } else if (type_entry->id == TypeTableEntryIdEnum) {
5358 if (!type_entry->data.enumeration.complete)5398 if (!type_entry->data.enumeration.complete)
5359 resolve_enum_type(g, type_entry);5399 return resolve_enum_type(g, type_entry);
5360 } else if (type_entry->id == TypeTableEntryIdUnion) {5400 } else if (type_entry->id == TypeTableEntryIdUnion) {
5361 if (!type_entry->data.unionation.complete)5401 if (!type_entry->data.unionation.complete)
5362 resolve_union_type(g, type_entry);5402 return resolve_union_type(g, type_entry);
5363 }5403 }
5404 return ErrorNone;
5364}5405}
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;
5367 if (type_entry->id == TypeTableEntryIdStruct) {5410 if (type_entry->id == TypeTableEntryIdStruct) {
5368 resolve_struct_zero_bits(g, type_entry);5411 return resolve_struct_zero_bits(g, type_entry);
5369 } else if (type_entry->id == TypeTableEntryIdEnum) {5412 } else if (type_entry->id == TypeTableEntryIdEnum) {
5370 resolve_enum_zero_bits(g, type_entry);5413 return resolve_enum_zero_bits(g, type_entry);
5371 } else if (type_entry->id == TypeTableEntryIdUnion) {5414 } else if (type_entry->id == TypeTableEntryIdUnion) {
5372 resolve_union_zero_bits(g, type_entry);5415 return resolve_union_zero_bits(g, type_entry);
5373 }5416 }
5417 return ErrorNone;
5374}5418}
53755419
5376bool ir_get_var_is_comptime(VariableTableEntry *var) {5420bool ir_get_var_is_comptime(VariableTableEntry *var) {
...@@ -6213,7 +6257,7 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) {...@@ -6213,7 +6257,7 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) {
6213}6257}
62146258
6215uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {6259uint32_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));
6217 if (type_entry->zero_bits) return 0;6261 if (type_entry->zero_bits) return 0;
62186262
6219 // We need to make this function work without requiring ensure_complete_type6263 // We need to make this function work without requiring ensure_complete_type
src/analyze.hpp+4-3
...@@ -9,6 +9,7 @@...@@ -9,6 +9,7 @@
9#define ZIG_ANALYZE_HPP9#define ZIG_ANALYZE_HPP
1010
11#include "all_types.hpp"11#include "all_types.hpp"
12#include "result.hpp"
1213
13void semantic_analyze(CodeGen *g);14void semantic_analyze(CodeGen *g);
14ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);15ErrorMsg *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...@@ -88,8 +89,8 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
88AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index);89AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index);
89FnTableEntry *scope_get_fn_if_root(Scope *scope);90FnTableEntry *scope_get_fn_if_root(Scope *scope);
90bool type_requires_comptime(TypeTableEntry *type_entry);91bool type_requires_comptime(TypeTableEntry *type_entry);
91void ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry);92Error ATTRIBUTE_MUST_USE ensure_complete_type(CodeGen *g, TypeTableEntry *type_entry);
92void type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry);93Error ATTRIBUTE_MUST_USE type_ensure_zero_bits_known(CodeGen *g, TypeTableEntry *type_entry);
93void complete_enum(CodeGen *g, TypeTableEntry *enum_type);94void complete_enum(CodeGen *g, TypeTableEntry *enum_type);
94bool ir_get_var_is_comptime(VariableTableEntry *var);95bool ir_get_var_is_comptime(VariableTableEntry *var);
95bool const_values_equal(ConstExprValue *a, ConstExprValue *b);96bool const_values_equal(ConstExprValue *a, ConstExprValue *b);
...@@ -178,7 +179,7 @@ TypeTableEntryId type_id_at_index(size_t index);...@@ -178,7 +179,7 @@ TypeTableEntryId type_id_at_index(size_t index);
178size_t type_id_len();179size_t type_id_len();
179size_t type_id_index(TypeTableEntry *entry);180size_t type_id_index(TypeTableEntry *entry);
180TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id);181TypeTableEntry *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);
182LinkLib *create_link_lib(Buf *name);183LinkLib *create_link_lib(Buf *name);
183bool calling_convention_does_first_arg_return(CallingConvention cc);184bool calling_convention_does_first_arg_return(CallingConvention cc);
184LinkLib *add_link_lib(CodeGen *codegen, Buf *lib);185LinkLib *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) {...@@ -829,15 +829,15 @@ static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) {
829 if (scope->id == ScopeIdBlock) {829 if (scope->id == ScopeIdBlock) {
830 ScopeBlock *block_scope = (ScopeBlock *)scope;830 ScopeBlock *block_scope = (ScopeBlock *)scope;
831 if (block_scope->fast_math_set_node)831 if (block_scope->fast_math_set_node)
832 return !block_scope->fast_math_off;832 return block_scope->fast_math_on;
833 } else if (scope->id == ScopeIdDecls) {833 } else if (scope->id == ScopeIdDecls) {
834 ScopeDecls *decls_scope = (ScopeDecls *)scope;834 ScopeDecls *decls_scope = (ScopeDecls *)scope;
835 if (decls_scope->fast_math_set_node)835 if (decls_scope->fast_math_set_node)
836 return !decls_scope->fast_math_off;836 return decls_scope->fast_math_on;
837 }837 }
838 scope = scope->parent;838 scope = scope->parent;
839 }839 }
840 return true;840 return false;
841}841}
842842
843static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) {843static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) {
...@@ -5131,13 +5131,13 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef...@@ -5131,13 +5131,13 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef
5131}5131}
51325132
5133static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, const char *name) {5133static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, const char *name) {
5134 render_const_val_global(g, const_val, name);
5135 switch (const_val->data.x_ptr.special) {5134 switch (const_val->data.x_ptr.special) {
5136 case ConstPtrSpecialInvalid:5135 case ConstPtrSpecialInvalid:
5137 case ConstPtrSpecialDiscard:5136 case ConstPtrSpecialDiscard:
5138 zig_unreachable();5137 zig_unreachable();
5139 case ConstPtrSpecialRef:5138 case ConstPtrSpecialRef:
5140 {5139 {
5140 render_const_val_global(g, const_val, name);
5141 ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;5141 ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
5142 render_const_val(g, pointee, "");5142 render_const_val(g, pointee, "");
5143 render_const_val_global(g, pointee, "");5143 render_const_val_global(g, pointee, "");
...@@ -5148,6 +5148,7 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con...@@ -5148,6 +5148,7 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
5148 }5148 }
5149 case ConstPtrSpecialBaseArray:5149 case ConstPtrSpecialBaseArray:
5150 {5150 {
5151 render_const_val_global(g, const_val, name);
5151 ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;5152 ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
5152 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;5153 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
5153 assert(array_const_val->type->id == TypeTableEntryIdArray);5154 assert(array_const_val->type->id == TypeTableEntryIdArray);
...@@ -5168,6 +5169,7 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con...@@ -5168,6 +5169,7 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
5168 }5169 }
5169 case ConstPtrSpecialBaseStruct:5170 case ConstPtrSpecialBaseStruct:
5170 {5171 {
5172 render_const_val_global(g, const_val, name);
5171 ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val;5173 ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val;
5172 assert(struct_const_val->type->id == TypeTableEntryIdStruct);5174 assert(struct_const_val->type->id == TypeTableEntryIdStruct);
5173 if (struct_const_val->type->zero_bits) {5175 if (struct_const_val->type->zero_bits) {
...@@ -5190,6 +5192,7 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con...@@ -5190,6 +5192,7 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, con
5190 }5192 }
5191 case ConstPtrSpecialHardCodedAddr:5193 case ConstPtrSpecialHardCodedAddr:
5192 {5194 {
5195 render_const_val_global(g, const_val, name);
5193 uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr;5196 uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr;
5194 TypeTableEntry *usize = g->builtin_types.entry_usize;5197 TypeTableEntry *usize = g->builtin_types.entry_usize;
5195 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false),5198 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) {...@@ -5720,12 +5723,16 @@ static void do_code_gen(CodeGen *g) {
57205723
5721 LLVMValueRef global_value;5724 LLVMValueRef global_value;
5722 if (var->linkage == VarLinkageExternal) {5725 if (var->linkage == VarLinkageExternal) {
5723 global_value = LLVMAddGlobal(g->module, var->value->type->type_ref, buf_ptr(&var->name));5726 LLVMValueRef existing_llvm_var = LLVMGetNamedGlobal(g->module, buf_ptr(&var->name));
57245727 if (existing_llvm_var) {
5725 // TODO debug info for the extern variable5728 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);5733 LLVMSetLinkage(global_value, LLVMExternalLinkage);
5728 LLVMSetAlignment(global_value, var->align_bytes);5734 LLVMSetAlignment(global_value, var->align_bytes);
5735 }
5729 } else {5736 } else {
5730 bool exported = (var->linkage == VarLinkageExport);5737 bool exported = (var->linkage == VarLinkageExport);
5731 const char *mangled_name = buf_ptr(get_mangled_name(g, &var->name, exported));5738 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 *...@@ -8711,6 +8711,7 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *
8711}8711}
87128712
8713static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, TypeTableEntry *expected_type, IrInstruction **instructions, size_t instruction_count) {8713static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, TypeTableEntry *expected_type, IrInstruction **instructions, size_t instruction_count) {
8714 Error err;
8714 assert(instruction_count >= 1);8715 assert(instruction_count >= 1);
8715 IrInstruction *prev_inst = instructions[0];8716 IrInstruction *prev_inst = instructions[0];
8716 if (type_is_invalid(prev_inst->value.type)) {8717 if (type_is_invalid(prev_inst->value.type)) {
...@@ -9172,8 +9173,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -9172,8 +9173,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
9172 if (prev_type->id == TypeTableEntryIdEnum && cur_type->id == TypeTableEntryIdUnion &&9173 if (prev_type->id == TypeTableEntryIdEnum && cur_type->id == TypeTableEntryIdUnion &&
9173 (cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))9174 (cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
9174 {9175 {
9175 type_ensure_zero_bits_known(ira->codegen, cur_type);9176 if ((err = type_ensure_zero_bits_known(ira->codegen, cur_type)))
9176 if (type_is_invalid(cur_type))
9177 return ira->codegen->builtin_types.entry_invalid;9177 return ira->codegen->builtin_types.entry_invalid;
9178 if (cur_type->data.unionation.tag_type == prev_type) {9178 if (cur_type->data.unionation.tag_type == prev_type) {
9179 continue;9179 continue;
...@@ -9183,8 +9183,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -9183,8 +9183,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
9183 if (cur_type->id == TypeTableEntryIdEnum && prev_type->id == TypeTableEntryIdUnion &&9183 if (cur_type->id == TypeTableEntryIdEnum && prev_type->id == TypeTableEntryIdUnion &&
9184 (prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))9184 (prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
9185 {9185 {
9186 type_ensure_zero_bits_known(ira->codegen, prev_type);9186 if ((err = type_ensure_zero_bits_known(ira->codegen, prev_type)))
9187 if (type_is_invalid(prev_type))
9188 return ira->codegen->builtin_types.entry_invalid;9187 return ira->codegen->builtin_types.entry_invalid;
9189 if (prev_type->data.unionation.tag_type == cur_type) {9188 if (prev_type->data.unionation.tag_type == cur_type) {
9190 prev_inst = cur_inst;9189 prev_inst = cur_inst;
...@@ -9999,11 +9998,11 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s...@@ -9999,11 +9998,11 @@ static IrInstruction *ir_analyze_array_to_slice(IrAnalyze *ira, IrInstruction *s
9999static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr,9998static IrInstruction *ir_analyze_enum_to_int(IrAnalyze *ira, IrInstruction *source_instr,
10000 IrInstruction *target, TypeTableEntry *wanted_type)9999 IrInstruction *target, TypeTableEntry *wanted_type)
10001{10000{
10001 Error err;
10002 assert(wanted_type->id == TypeTableEntryIdInt);10002 assert(wanted_type->id == TypeTableEntryIdInt);
1000310003
10004 TypeTableEntry *actual_type = target->value.type;10004 TypeTableEntry *actual_type = target->value.type;
10005 ensure_complete_type(ira->codegen, actual_type);10005 if ((err = ensure_complete_type(ira->codegen, actual_type)))
10006 if (type_is_invalid(actual_type))
10007 return ira->codegen->invalid_instruction;10006 return ira->codegen->invalid_instruction;
1000810007
10009 if (wanted_type != actual_type->data.enumeration.tag_int_type) {10008 if (wanted_type != actual_type->data.enumeration.tag_int_type) {
...@@ -10069,6 +10068,7 @@ static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruc...@@ -10069,6 +10068,7 @@ static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruc
10069static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *source_instr,10068static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *source_instr,
10070 IrInstruction *target, TypeTableEntry *wanted_type)10069 IrInstruction *target, TypeTableEntry *wanted_type)
10071{10070{
10071 Error err;
10072 assert(wanted_type->id == TypeTableEntryIdUnion);10072 assert(wanted_type->id == TypeTableEntryIdUnion);
10073 assert(target->value.type->id == TypeTableEntryIdEnum);10073 assert(target->value.type->id == TypeTableEntryIdEnum);
1007410074
...@@ -10078,8 +10078,7 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so...@@ -10078,8 +10078,7 @@ static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *so
10078 return ira->codegen->invalid_instruction;10078 return ira->codegen->invalid_instruction;
10079 TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);10079 TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);
10080 assert(union_field != nullptr);10080 assert(union_field != nullptr);
10081 type_ensure_zero_bits_known(ira->codegen, union_field->type_entry);10081 if ((err = type_ensure_zero_bits_known(ira->codegen, union_field->type_entry)))
10082 if (type_is_invalid(union_field->type_entry))
10083 return ira->codegen->invalid_instruction;10082 return ira->codegen->invalid_instruction;
10084 if (!union_field->type_entry->zero_bits) {10083 if (!union_field->type_entry->zero_bits) {
10085 AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(10084 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...@@ -10169,12 +10168,12 @@ static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction
10169static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *source_instr,10168static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *source_instr,
10170 IrInstruction *target, TypeTableEntry *wanted_type)10169 IrInstruction *target, TypeTableEntry *wanted_type)
10171{10170{
10171 Error err;
10172 assert(wanted_type->id == TypeTableEntryIdEnum);10172 assert(wanted_type->id == TypeTableEntryIdEnum);
1017310173
10174 TypeTableEntry *actual_type = target->value.type;10174 TypeTableEntry *actual_type = target->value.type;
1017510175
10176 ensure_complete_type(ira->codegen, wanted_type);10176 if ((err = ensure_complete_type(ira->codegen, wanted_type)))
10177 if (type_is_invalid(wanted_type))
10178 return ira->codegen->invalid_instruction;10177 return ira->codegen->invalid_instruction;
1017910178
10180 if (actual_type != wanted_type->data.enumeration.tag_int_type) {10179 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...@@ -10517,6 +10516,7 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
10517static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,10516static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr,
10518 TypeTableEntry *wanted_type, IrInstruction *value)10517 TypeTableEntry *wanted_type, IrInstruction *value)
10519{10518{
10519 Error err;
10520 TypeTableEntry *actual_type = value->value.type;10520 TypeTableEntry *actual_type = value->value.type;
10521 AstNode *source_node = source_instr->source_node;10521 AstNode *source_node = source_instr->source_node;
1052210522
...@@ -10697,6 +10697,19 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10697,6 +10697,19 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10697 return ira->codegen->invalid_instruction;10697 return ira->codegen->invalid_instruction;
1069810698
10699 return cast2;10699 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);
10700 }10713 }
10701 }10714 }
1070210715
...@@ -10783,8 +10796,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10783,8 +10796,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10783 if (actual_type->id == TypeTableEntryIdComptimeFloat ||10796 if (actual_type->id == TypeTableEntryIdComptimeFloat ||
10784 actual_type->id == TypeTableEntryIdComptimeInt)10797 actual_type->id == TypeTableEntryIdComptimeInt)
10785 {10798 {
10786 ensure_complete_type(ira->codegen, wanted_type);10799 if ((err = ensure_complete_type(ira->codegen, wanted_type)))
10787 if (type_is_invalid(wanted_type))
10788 return ira->codegen->invalid_instruction;10800 return ira->codegen->invalid_instruction;
10789 if (wanted_type->id == TypeTableEntryIdEnum) {10801 if (wanted_type->id == TypeTableEntryIdEnum) {
10790 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value);10802 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...@@ -10840,8 +10852,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1084010852
10841 // cast from union to the enum type of the union10853 // cast from union to the enum type of the union
10842 if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) {10854 if (actual_type->id == TypeTableEntryIdUnion && wanted_type->id == TypeTableEntryIdEnum) {
10843 type_ensure_zero_bits_known(ira->codegen, actual_type);10855 if ((err = type_ensure_zero_bits_known(ira->codegen, actual_type)))
10844 if (type_is_invalid(actual_type))
10845 return ira->codegen->invalid_instruction;10856 return ira->codegen->invalid_instruction;
1084610857
10847 if (actual_type->data.unionation.tag_type == wanted_type) {10858 if (actual_type->data.unionation.tag_type == wanted_type) {
...@@ -10854,7 +10865,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10854,7 +10865,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10854 (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||10865 (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
10855 wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))10866 wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
10856 {10867 {
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
10858 if (wanted_type->data.unionation.tag_type == actual_type) {10871 if (wanted_type->data.unionation.tag_type == actual_type) {
10859 return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);10872 return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);
10860 }10873 }
...@@ -10866,7 +10879,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10866,7 +10879,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10866 if (union_type->data.unionation.decl_node->data.container_decl.auto_enum ||10879 if (union_type->data.unionation.decl_node->data.container_decl.auto_enum ||
10867 union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)10880 union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)
10868 {10881 {
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
10870 if (union_type->data.unionation.tag_type == actual_type) {10885 if (union_type->data.unionation.tag_type == actual_type) {
10871 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, union_type, value);10886 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, union_type, value);
10872 if (type_is_invalid(cast1->value.type))10887 if (type_is_invalid(cast1->value.type))
...@@ -10910,8 +10925,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10910,8 +10925,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10910 types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,10925 types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
10911 actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)10926 actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
10912 {10927 {
10913 type_ensure_zero_bits_known(ira->codegen, actual_type);10928 if ((err = type_ensure_zero_bits_known(ira->codegen, actual_type))) {
10914 if (type_is_invalid(actual_type)) {
10915 return ira->codegen->invalid_instruction;10929 return ira->codegen->invalid_instruction;
10916 }10930 }
10917 if (!type_has_bits(actual_type)) {10931 if (!type_has_bits(actual_type)) {
...@@ -11310,6 +11324,7 @@ static bool optional_value_is_null(ConstExprValue *val) {...@@ -11310,6 +11324,7 @@ static bool optional_value_is_null(ConstExprValue *val) {
11310}11324}
1131111325
11312static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {11326static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
11327 Error err;
11313 IrInstruction *op1 = bin_op_instruction->op1->other;11328 IrInstruction *op1 = bin_op_instruction->op1->other;
11314 IrInstruction *op2 = bin_op_instruction->op2->other;11329 IrInstruction *op2 = bin_op_instruction->op2->other;
11315 AstNode *source_node = bin_op_instruction->base.source_node;11330 AstNode *source_node = bin_op_instruction->base.source_node;
...@@ -11445,8 +11460,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -11445,8 +11460,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
11445 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2);11460 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2);
11446 if (type_is_invalid(resolved_type))11461 if (type_is_invalid(resolved_type))
11447 return resolved_type;11462 return resolved_type;
11448 type_ensure_zero_bits_known(ira->codegen, resolved_type);11463 if ((err = type_ensure_zero_bits_known(ira->codegen, resolved_type)))
11449 if (type_is_invalid(resolved_type))
11450 return resolved_type;11464 return resolved_type;
1145111465
11452 bool operator_allowed;11466 bool operator_allowed;
...@@ -12393,6 +12407,7 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi...@@ -12393,6 +12407,7 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
12393}12407}
1239412408
12395static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) {12409static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) {
12410 Error err;
12396 VariableTableEntry *var = decl_var_instruction->var;12411 VariableTableEntry *var = decl_var_instruction->var;
1239712412
12398 IrInstruction *init_value = decl_var_instruction->init_value->other;12413 IrInstruction *init_value = decl_var_instruction->init_value->other;
...@@ -12426,8 +12441,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc...@@ -12426,8 +12441,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
12426 if (type_is_invalid(result_type)) {12441 if (type_is_invalid(result_type)) {
12427 result_type = ira->codegen->builtin_types.entry_invalid;12442 result_type = ira->codegen->builtin_types.entry_invalid;
12428 } else {12443 } else {
12429 type_ensure_zero_bits_known(ira->codegen, result_type);12444 if ((err = type_ensure_zero_bits_known(ira->codegen, result_type))) {
12430 if (type_is_invalid(result_type)) {
12431 result_type = ira->codegen->builtin_types.entry_invalid;12445 result_type = ira->codegen->builtin_types.entry_invalid;
12432 }12446 }
12433 }12447 }
...@@ -12945,6 +12959,7 @@ static VariableTableEntry *get_fn_var_by_index(FnTableEntry *fn_entry, size_t in...@@ -12945,6 +12959,7 @@ static VariableTableEntry *get_fn_var_by_index(FnTableEntry *fn_entry, size_t in
12945static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,12959static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
12946 VariableTableEntry *var)12960 VariableTableEntry *var)
12947{12961{
12962 Error err;
12948 if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) {12963 if (var->mem_slot_index != SIZE_MAX && var->owner_exec->analysis == nullptr) {
12949 assert(ira->codegen->errors.length != 0);12964 assert(ira->codegen->errors.length != 0);
12950 return ira->codegen->invalid_instruction;12965 return ira->codegen->invalid_instruction;
...@@ -12999,7 +13014,8 @@ no_mem_slot:...@@ -12999,7 +13014,8 @@ no_mem_slot:
12999 instruction->scope, instruction->source_node, var);13014 instruction->scope, instruction->source_node, var);
13000 var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type,13015 var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type,
13001 var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0);13016 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
13004 bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);13020 bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
13005 var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;13021 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...@@ -13011,6 +13027,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
13011 FnTableEntry *fn_entry, TypeTableEntry *fn_type, IrInstruction *fn_ref,13027 FnTableEntry *fn_entry, TypeTableEntry *fn_type, IrInstruction *fn_ref,
13012 IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline)13028 IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline)
13013{13029{
13030 Error err;
13014 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;13031 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
13015 size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0;13032 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...@@ -13375,8 +13392,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
13375 inst_fn_type_id.return_type = specified_return_type;13392 inst_fn_type_id.return_type = specified_return_type;
13376 }13393 }
1337713394
13378 type_ensure_zero_bits_known(ira->codegen, specified_return_type);13395 if ((err = type_ensure_zero_bits_known(ira->codegen, specified_return_type)))
13379 if (type_is_invalid(specified_return_type))
13380 return ira->codegen->builtin_types.entry_invalid;13396 return ira->codegen->builtin_types.entry_invalid;
1338113397
13382 if (type_requires_comptime(specified_return_type)) {13398 if (type_requires_comptime(specified_return_type)) {
...@@ -13651,12 +13667,12 @@ static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp...@@ -13651,12 +13667,12 @@ static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp
13651}13667}
1365213668
13653static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {13669static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_instruction) {
13670 Error err;
13654 IrInstruction *value = un_op_instruction->value->other;13671 IrInstruction *value = un_op_instruction->value->other;
13655 TypeTableEntry *type_entry = ir_resolve_type(ira, value);13672 TypeTableEntry *type_entry = ir_resolve_type(ira, value);
13656 if (type_is_invalid(type_entry))13673 if (type_is_invalid(type_entry))
13657 return ira->codegen->builtin_types.entry_invalid;13674 return ira->codegen->builtin_types.entry_invalid;
13658 ensure_complete_type(ira->codegen, type_entry);13675 if ((err = ensure_complete_type(ira->codegen, type_entry)))
13659 if (type_is_invalid(type_entry))
13660 return ira->codegen->builtin_types.entry_invalid;13676 return ira->codegen->builtin_types.entry_invalid;
1366113677
13662 switch (type_entry->id) {13678 switch (type_entry->id) {
...@@ -14010,6 +14026,7 @@ static TypeTableEntry *adjust_ptr_len(CodeGen *g, TypeTableEntry *ptr_type, PtrL...@@ -14010,6 +14026,7 @@ static TypeTableEntry *adjust_ptr_len(CodeGen *g, TypeTableEntry *ptr_type, PtrL
14010}14026}
1401114027
14012static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) {14028static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstructionElemPtr *elem_ptr_instruction) {
14029 Error err;
14013 IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other;14030 IrInstruction *array_ptr = elem_ptr_instruction->array_ptr->other;
14014 if (type_is_invalid(array_ptr->value.type))14031 if (type_is_invalid(array_ptr->value.type))
14015 return ira->codegen->builtin_types.entry_invalid;14032 return ira->codegen->builtin_types.entry_invalid;
...@@ -14118,8 +14135,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc...@@ -14118,8 +14135,7 @@ static TypeTableEntry *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruc
14118 return ira->codegen->builtin_types.entry_invalid;14135 return ira->codegen->builtin_types.entry_invalid;
1411914136
14120 bool safety_check_on = elem_ptr_instruction->safety_check_on;14137 bool safety_check_on = elem_ptr_instruction->safety_check_on;
14121 ensure_complete_type(ira->codegen, return_type->data.pointer.child_type);14138 if ((err = ensure_complete_type(ira->codegen, return_type->data.pointer.child_type)))
14122 if (type_is_invalid(return_type->data.pointer.child_type))
14123 return ira->codegen->builtin_types.entry_invalid;14139 return ira->codegen->builtin_types.entry_invalid;
1412414140
14125 uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type);14141 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,...@@ -14339,9 +14355,10 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira,
14339static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,14355static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
14340 IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type)14356 IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type)
14341{14357{
14358 Error err;
14359
14342 TypeTableEntry *bare_type = container_ref_type(container_type);14360 TypeTableEntry *bare_type = container_ref_type(container_type);
14343 ensure_complete_type(ira->codegen, bare_type);14361 if ((err = ensure_complete_type(ira->codegen, bare_type)))
14344 if (type_is_invalid(bare_type))
14345 return ira->codegen->invalid_instruction;14362 return ira->codegen->invalid_instruction;
1434614363
14347 assert(container_ptr->value.type->id == TypeTableEntryIdPointer);14364 assert(container_ptr->value.type->id == TypeTableEntryIdPointer);
...@@ -14540,6 +14557,7 @@ static ErrorTableEntry *find_err_table_entry(TypeTableEntry *err_set_type, Buf *...@@ -14540,6 +14557,7 @@ static ErrorTableEntry *find_err_table_entry(TypeTableEntry *err_set_type, Buf *
14540}14557}
1454114558
14542static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {14559static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstructionFieldPtr *field_ptr_instruction) {
14560 Error err;
14543 IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;14561 IrInstruction *container_ptr = field_ptr_instruction->container_ptr->other;
14544 if (type_is_invalid(container_ptr->value.type))14562 if (type_is_invalid(container_ptr->value.type))
14545 return ira->codegen->builtin_types.entry_invalid;14563 return ira->codegen->builtin_types.entry_invalid;
...@@ -14641,8 +14659,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -14641,8 +14659,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
14641 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);14659 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
14642 }14660 }
14643 if (child_type->id == TypeTableEntryIdEnum) {14661 if (child_type->id == TypeTableEntryIdEnum) {
14644 ensure_complete_type(ira->codegen, child_type);14662 if ((err = ensure_complete_type(ira->codegen, child_type)))
14645 if (type_is_invalid(child_type))
14646 return ira->codegen->builtin_types.entry_invalid;14663 return ira->codegen->builtin_types.entry_invalid;
1464714664
14648 TypeEnumField *field = find_enum_type_field(child_type, field_name);14665 TypeEnumField *field = find_enum_type_field(child_type, field_name);
...@@ -14666,8 +14683,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -14666,8 +14683,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
14666 (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr ||14683 (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr ||
14667 child_type->data.unionation.decl_node->data.container_decl.auto_enum))14684 child_type->data.unionation.decl_node->data.container_decl.auto_enum))
14668 {14685 {
14669 ensure_complete_type(ira->codegen, child_type);14686 if ((err = ensure_complete_type(ira->codegen, child_type)))
14670 if (type_is_invalid(child_type))
14671 return ira->codegen->builtin_types.entry_invalid;14687 return ira->codegen->builtin_types.entry_invalid;
14672 TypeUnionField *field = find_union_type_field(child_type, field_name);14688 TypeUnionField *field = find_union_type_field(child_type, field_name);
14673 if (field) {14689 if (field) {
...@@ -15187,17 +15203,17 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,...@@ -15187,17 +15203,17 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,
15187 return ira->codegen->builtin_types.entry_void;15203 return ira->codegen->builtin_types.entry_void;
15188 }15204 }
1518915205
15190 bool *fast_math_off_ptr;15206 bool *fast_math_on_ptr;
15191 AstNode **fast_math_set_node_ptr;15207 AstNode **fast_math_set_node_ptr;
15192 if (target_type->id == TypeTableEntryIdBlock) {15208 if (target_type->id == TypeTableEntryIdBlock) {
15193 ScopeBlock *block_scope = (ScopeBlock *)target_val->data.x_block;15209 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;
15195 fast_math_set_node_ptr = &block_scope->fast_math_set_node;15211 fast_math_set_node_ptr = &block_scope->fast_math_set_node;
15196 } else if (target_type->id == TypeTableEntryIdFn) {15212 } else if (target_type->id == TypeTableEntryIdFn) {
15197 assert(target_val->data.x_ptr.special == ConstPtrSpecialFunction);15213 assert(target_val->data.x_ptr.special == ConstPtrSpecialFunction);
15198 FnTableEntry *target_fn = target_val->data.x_ptr.data.fn.fn_entry;15214 FnTableEntry *target_fn = target_val->data.x_ptr.data.fn.fn_entry;
15199 assert(target_fn->def_scope);15215 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;
15201 fast_math_set_node_ptr = &target_fn->def_scope->fast_math_set_node;15217 fast_math_set_node_ptr = &target_fn->def_scope->fast_math_set_node;
15202 } else if (target_type->id == TypeTableEntryIdMetaType) {15218 } else if (target_type->id == TypeTableEntryIdMetaType) {
15203 ScopeDecls *decls_scope;15219 ScopeDecls *decls_scope;
...@@ -15213,7 +15229,7 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,...@@ -15213,7 +15229,7 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,
15213 buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&type_arg->name)));15229 buf_sprintf("expected scope reference, found type '%s'", buf_ptr(&type_arg->name)));
15214 return ira->codegen->builtin_types.entry_invalid;15230 return ira->codegen->builtin_types.entry_invalid;
15215 }15231 }
15216 fast_math_off_ptr = &decls_scope->fast_math_off;15232 fast_math_on_ptr = &decls_scope->fast_math_on;
15217 fast_math_set_node_ptr = &decls_scope->fast_math_set_node;15233 fast_math_set_node_ptr = &decls_scope->fast_math_set_node;
15218 } else {15234 } else {
15219 ir_add_error_node(ira, target_instruction->source_node,15235 ir_add_error_node(ira, target_instruction->source_node,
...@@ -15235,7 +15251,7 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,...@@ -15235,7 +15251,7 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,
15235 return ira->codegen->builtin_types.entry_invalid;15251 return ira->codegen->builtin_types.entry_invalid;
15236 }15252 }
15237 *fast_math_set_node_ptr = source_node;15253 *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
15240 ir_build_const_from(ira, &instruction->base);15256 ir_build_const_from(ira, &instruction->base);
15241 return ira->codegen->builtin_types.entry_void;15257 return ira->codegen->builtin_types.entry_void;
...@@ -15244,6 +15260,7 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,...@@ -15244,6 +15260,7 @@ static TypeTableEntry *ir_analyze_instruction_set_float_mode(IrAnalyze *ira,
15244static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,15260static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
15245 IrInstructionSliceType *slice_type_instruction)15261 IrInstructionSliceType *slice_type_instruction)
15246{15262{
15263 Error err;
15247 uint32_t align_bytes;15264 uint32_t align_bytes;
15248 if (slice_type_instruction->align_value != nullptr) {15265 if (slice_type_instruction->align_value != nullptr) {
15249 if (!ir_resolve_align(ira, slice_type_instruction->align_value->other, &align_bytes))15266 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,...@@ -15255,6 +15272,8 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
15255 return ira->codegen->builtin_types.entry_invalid;15272 return ira->codegen->builtin_types.entry_invalid;
1525615273
15257 if (slice_type_instruction->align_value == nullptr) {15274 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;
15258 align_bytes = get_abi_alignment(ira->codegen, child_type);15277 align_bytes = get_abi_alignment(ira->codegen, child_type);
15259 }15278 }
1526015279
...@@ -15293,7 +15312,8 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,...@@ -15293,7 +15312,8 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
15293 case TypeTableEntryIdBoundFn:15312 case TypeTableEntryIdBoundFn:
15294 case TypeTableEntryIdPromise:15313 case TypeTableEntryIdPromise:
15295 {15314 {
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;
15297 TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type,15317 TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, child_type,
15298 is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0);15318 is_const, is_volatile, PtrLenUnknown, align_bytes, 0, 0);
15299 TypeTableEntry *result_type = get_slice_type(ira->codegen, slice_ptr_type);15319 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...@@ -15431,11 +15451,11 @@ static TypeTableEntry *ir_analyze_instruction_promise_type(IrAnalyze *ira, IrIns
15431static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,15451static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
15432 IrInstructionSizeOf *size_of_instruction)15452 IrInstructionSizeOf *size_of_instruction)
15433{15453{
15454 Error err;
15434 IrInstruction *type_value = size_of_instruction->type_value->other;15455 IrInstruction *type_value = size_of_instruction->type_value->other;
15435 TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);15456 TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
1543615457
15437 ensure_complete_type(ira->codegen, type_entry);15458 if ((err = ensure_complete_type(ira->codegen, type_entry)))
15438 if (type_is_invalid(type_entry))
15439 return ira->codegen->builtin_types.entry_invalid;15459 return ira->codegen->builtin_types.entry_invalid;
1544015460
15441 switch (type_entry->id) {15461 switch (type_entry->id) {
...@@ -15806,6 +15826,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,...@@ -15806,6 +15826,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
15806static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,15826static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
15807 IrInstructionSwitchTarget *switch_target_instruction)15827 IrInstructionSwitchTarget *switch_target_instruction)
15808{15828{
15829 Error err;
15809 IrInstruction *target_value_ptr = switch_target_instruction->target_value_ptr->other;15830 IrInstruction *target_value_ptr = switch_target_instruction->target_value_ptr->other;
15810 if (type_is_invalid(target_value_ptr->value.type))15831 if (type_is_invalid(target_value_ptr->value.type))
15811 return ira->codegen->builtin_types.entry_invalid;15832 return ira->codegen->builtin_types.entry_invalid;
...@@ -15832,8 +15853,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -15832,8 +15853,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
15832 if (pointee_val->special == ConstValSpecialRuntime)15853 if (pointee_val->special == ConstValSpecialRuntime)
15833 pointee_val = nullptr;15854 pointee_val = nullptr;
15834 }15855 }
15835 ensure_complete_type(ira->codegen, target_type);15856 if ((err = ensure_complete_type(ira->codegen, target_type)))
15836 if (type_is_invalid(target_type))
15837 return ira->codegen->builtin_types.entry_invalid;15857 return ira->codegen->builtin_types.entry_invalid;
1583815858
15839 switch (target_type->id) {15859 switch (target_type->id) {
...@@ -15897,8 +15917,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -15897,8 +15917,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
15897 return tag_type;15917 return tag_type;
15898 }15918 }
15899 case TypeTableEntryIdEnum: {15919 case TypeTableEntryIdEnum: {
15900 type_ensure_zero_bits_known(ira->codegen, target_type);15920 if ((err = type_ensure_zero_bits_known(ira->codegen, target_type)))
15901 if (type_is_invalid(target_type))
15902 return ira->codegen->builtin_types.entry_invalid;15921 return ira->codegen->builtin_types.entry_invalid;
15903 if (target_type->data.enumeration.src_field_count < 2) {15922 if (target_type->data.enumeration.src_field_count < 2) {
15904 TypeEnumField *only_field = &target_type->data.enumeration.fields[0];15923 TypeEnumField *only_field = &target_type->data.enumeration.fields[0];
...@@ -16100,10 +16119,10 @@ static TypeTableEntry *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionR...@@ -16100,10 +16119,10 @@ static TypeTableEntry *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionR
16100static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction,16119static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction,
16101 TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)16120 TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
16102{16121{
16122 Error err;
16103 assert(container_type->id == TypeTableEntryIdUnion);16123 assert(container_type->id == TypeTableEntryIdUnion);
1610416124
16105 ensure_complete_type(ira->codegen, container_type);16125 if ((err = ensure_complete_type(ira->codegen, container_type)))
16106 if (type_is_invalid(container_type))
16107 return ira->codegen->builtin_types.entry_invalid;16126 return ira->codegen->builtin_types.entry_invalid;
1610816127
16109 if (instr_field_count != 1) {16128 if (instr_field_count != 1) {
...@@ -16132,8 +16151,7 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir...@@ -16132,8 +16151,7 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir
16132 if (casted_field_value == ira->codegen->invalid_instruction)16151 if (casted_field_value == ira->codegen->invalid_instruction)
16133 return ira->codegen->builtin_types.entry_invalid;16152 return ira->codegen->builtin_types.entry_invalid;
1613416153
16135 type_ensure_zero_bits_known(ira->codegen, casted_field_value->value.type);16154 if ((err = type_ensure_zero_bits_known(ira->codegen, casted_field_value->value.type)))
16136 if (type_is_invalid(casted_field_value->value.type))
16137 return ira->codegen->builtin_types.entry_invalid;16155 return ira->codegen->builtin_types.entry_invalid;
1613816156
16139 bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);16157 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...@@ -16167,6 +16185,7 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir
16167static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,16185static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
16168 TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)16186 TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
16169{16187{
16188 Error err;
16170 if (container_type->id == TypeTableEntryIdUnion) {16189 if (container_type->id == TypeTableEntryIdUnion) {
16171 return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields);16190 return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields);
16172 }16191 }
...@@ -16177,8 +16196,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru...@@ -16177,8 +16196,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru
16177 return ira->codegen->builtin_types.entry_invalid;16196 return ira->codegen->builtin_types.entry_invalid;
16178 }16197 }
1617916198
16180 ensure_complete_type(ira->codegen, container_type);16199 if ((err = ensure_complete_type(ira->codegen, container_type)))
16181 if (type_is_invalid(container_type))
16182 return ira->codegen->builtin_types.entry_invalid;16200 return ira->codegen->builtin_types.entry_invalid;
1618316201
16184 size_t actual_field_count = container_type->data.structure.src_field_count;16202 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...@@ -16559,6 +16577,7 @@ static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstruc
16559}16577}
1656016578
16561static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructionTagName *instruction) {16579static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstructionTagName *instruction) {
16580 Error err;
16562 IrInstruction *target = instruction->target->other;16581 IrInstruction *target = instruction->target->other;
16563 if (type_is_invalid(target->value.type))16582 if (type_is_invalid(target->value.type))
16564 return ira->codegen->builtin_types.entry_invalid;16583 return ira->codegen->builtin_types.entry_invalid;
...@@ -16566,8 +16585,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn...@@ -16566,8 +16585,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn
16566 assert(target->value.type->id == TypeTableEntryIdEnum);16585 assert(target->value.type->id == TypeTableEntryIdEnum);
1656716586
16568 if (instr_is_comptime(target)) {16587 if (instr_is_comptime(target)) {
16569 type_ensure_zero_bits_known(ira->codegen, target->value.type);16588 if ((err = type_ensure_zero_bits_known(ira->codegen, target->value.type)))
16570 if (type_is_invalid(target->value.type))
16571 return ira->codegen->builtin_types.entry_invalid;16589 return ira->codegen->builtin_types.entry_invalid;
16572 TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint);16590 TypeEnumField *field = find_enum_field_by_tag(target->value.type, &target->value.data.x_bigint);
16573 ConstExprValue *array_val = create_const_str_lit(ira->codegen, field->name);16591 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...@@ -16591,6 +16609,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn
16591static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,16609static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
16592 IrInstructionFieldParentPtr *instruction)16610 IrInstructionFieldParentPtr *instruction)
16593{16611{
16612 Error err;
16594 IrInstruction *type_value = instruction->type_value->other;16613 IrInstruction *type_value = instruction->type_value->other;
16595 TypeTableEntry *container_type = ir_resolve_type(ira, type_value);16614 TypeTableEntry *container_type = ir_resolve_type(ira, type_value);
16596 if (type_is_invalid(container_type))16615 if (type_is_invalid(container_type))
...@@ -16611,8 +16630,7 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,...@@ -16611,8 +16630,7 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
16611 return ira->codegen->builtin_types.entry_invalid;16630 return ira->codegen->builtin_types.entry_invalid;
16612 }16631 }
1661316632
16614 ensure_complete_type(ira->codegen, container_type);16633 if ((err = ensure_complete_type(ira->codegen, container_type)))
16615 if (type_is_invalid(container_type))
16616 return ira->codegen->builtin_types.entry_invalid;16634 return ira->codegen->builtin_types.entry_invalid;
1661716635
16618 TypeStructField *field = find_struct_type_field(container_type, field_name);16636 TypeStructField *field = find_struct_type_field(container_type, field_name);
...@@ -16684,13 +16702,13 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,...@@ -16684,13 +16702,13 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
16684static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira,16702static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira,
16685 IrInstructionOffsetOf *instruction)16703 IrInstructionOffsetOf *instruction)
16686{16704{
16705 Error err;
16687 IrInstruction *type_value = instruction->type_value->other;16706 IrInstruction *type_value = instruction->type_value->other;
16688 TypeTableEntry *container_type = ir_resolve_type(ira, type_value);16707 TypeTableEntry *container_type = ir_resolve_type(ira, type_value);
16689 if (type_is_invalid(container_type))16708 if (type_is_invalid(container_type))
16690 return ira->codegen->builtin_types.entry_invalid;16709 return ira->codegen->builtin_types.entry_invalid;
1669116710
16692 ensure_complete_type(ira->codegen, container_type);16711 if ((err = ensure_complete_type(ira->codegen, container_type)))
16693 if (type_is_invalid(container_type))
16694 return ira->codegen->builtin_types.entry_invalid;16712 return ira->codegen->builtin_types.entry_invalid;
1669516713
16696 IrInstruction *field_name_value = instruction->field_name->other;16714 IrInstruction *field_name_value = instruction->field_name->other;
...@@ -16735,19 +16753,15 @@ static void ensure_field_index(TypeTableEntry *type, const char *field_name, siz...@@ -16735,19 +16753,15 @@ static void ensure_field_index(TypeTableEntry *type, const char *field_name, siz
16735 (buf_deinit(field_name_buf), true));16753 (buf_deinit(field_name_buf), true));
16736}16754}
1673716755
16738static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, TypeTableEntry *root = nullptr)16756static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, TypeTableEntry *root) {
16739{16757 Error err;
16740 static ConstExprValue *type_info_var = nullptr;16758 static ConstExprValue *type_info_var = nullptr;
16741 static TypeTableEntry *type_info_type = nullptr;16759 static TypeTableEntry *type_info_type = nullptr;
16742 if (type_info_var == nullptr)16760 if (type_info_var == nullptr) {
16743 {
16744 type_info_var = get_builtin_value(ira->codegen, "TypeInfo");16761 type_info_var = get_builtin_value(ira->codegen, "TypeInfo");
16745 assert(type_info_var->type->id == TypeTableEntryIdMetaType);16762 assert(type_info_var->type->id == TypeTableEntryIdMetaType);
1674616763
16747 ensure_complete_type(ira->codegen, type_info_var->data.x_type);16764 assertNoError(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
16751 type_info_type = type_info_var->data.x_type;16765 type_info_type = type_info_var->data.x_type;
16752 assert(type_info_type->id == TypeTableEntryIdUnion);16766 assert(type_info_type->id == TypeTableEntryIdUnion);
16753 }16767 }
...@@ -16772,8 +16786,7 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na...@@ -16772,8 +16786,7 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na
1677216786
16773 VariableTableEntry *var = tld->var;16787 VariableTableEntry *var = tld->var;
1677416788
16775 ensure_complete_type(ira->codegen, var->value->type);16789 if ((err = ensure_complete_type(ira->codegen, var->value->type)))
16776 if (type_is_invalid(var->value->type))
16777 return ira->codegen->builtin_types.entry_invalid;16790 return ira->codegen->builtin_types.entry_invalid;
16778 assert(var->value->type->id == TypeTableEntryIdMetaType);16791 assert(var->value->type->id == TypeTableEntryIdMetaType);
16779 return var->value->data.x_type;16792 return var->value->data.x_type;
...@@ -16781,9 +16794,9 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na...@@ -16781,9 +16794,9 @@ static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_na
1678116794
16782static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope)16795static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope)
16783{16796{
16784 TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition");16797 Error err;
16785 ensure_complete_type(ira->codegen, type_info_definition_type);16798 TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition", nullptr);
16786 if (type_is_invalid(type_info_definition_type))16799 if ((err = ensure_complete_type(ira->codegen, type_info_definition_type)))
16787 return false;16800 return false;
1678816801
16789 ensure_field_index(type_info_definition_type, "name", 0);16802 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...@@ -16791,18 +16804,15 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
16791 ensure_field_index(type_info_definition_type, "data", 2);16804 ensure_field_index(type_info_definition_type, "data", 2);
1679216805
16793 TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type);16806 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);16807 if ((err = ensure_complete_type(ira->codegen, type_info_definition_data_type)))
16795 if (type_is_invalid(type_info_definition_data_type))
16796 return false;16808 return false;
1679716809
16798 TypeTableEntry *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type);16810 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);16811 if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_type)))
16800 if (type_is_invalid(type_info_fn_def_type))
16801 return false;16812 return false;
1680216813
16803 TypeTableEntry *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type);16814 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);16815 if ((err = ensure_complete_type(ira->codegen, type_info_fn_def_inline_type)))
16805 if (type_is_invalid(type_info_fn_def_inline_type))
16806 return false;16816 return false;
1680716817
16808 // Loop through our definitions once to figure out how many definitions we will generate info for.16818 // 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...@@ -16882,8 +16892,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
16882 case TldIdVar:16892 case TldIdVar:
16883 {16893 {
16884 VariableTableEntry *var = ((TldVar *)curr_entry->value)->var;16894 VariableTableEntry *var = ((TldVar *)curr_entry->value)->var;
16885 ensure_complete_type(ira->codegen, var->value->type);16895 if ((err = ensure_complete_type(ira->codegen, var->value->type)))
16886 if (type_is_invalid(var->value->type))
16887 return false;16896 return false;
1688816897
16889 if (var->value->type->id == TypeTableEntryIdMetaType)16898 if (var->value->type->id == TypeTableEntryIdMetaType)
...@@ -16940,7 +16949,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop...@@ -16940,7 +16949,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
16940 // calling_convention: TypeInfo.CallingConvention16949 // calling_convention: TypeInfo.CallingConvention
16941 ensure_field_index(fn_def_val->type, "calling_convention", 2);16950 ensure_field_index(fn_def_val->type, "calling_convention", 2);
16942 fn_def_fields[2].special = ConstValSpecialStatic;16951 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);
16944 bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc);16953 bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc);
16945 // is_var_args: bool16954 // is_var_args: bool
16946 ensure_field_index(fn_def_val->type, "is_var_args", 3);16955 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...@@ -17014,8 +17023,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
17014 case TldIdContainer:17023 case TldIdContainer:
17015 {17024 {
17016 TypeTableEntry *type_entry = ((TldContainer *)curr_entry->value)->type_entry;17025 TypeTableEntry *type_entry = ((TldContainer *)curr_entry->value)->type_entry;
17017 ensure_complete_type(ira->codegen, type_entry);17026 if ((err = ensure_complete_type(ira->codegen, type_entry)))
17018 if (type_is_invalid(type_entry))
17019 return false;17027 return false;
1702017028
17021 // This is a type.17029 // This is a type.
...@@ -17041,12 +17049,67 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop...@@ -17041,12 +17049,67 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
17041 return true;17049 return true;
17042}17050}
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
17044static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) {17107static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) {
17108 Error err;
17045 assert(type_entry != nullptr);17109 assert(type_entry != nullptr);
17046 assert(!type_is_invalid(type_entry));17110 assert(!type_is_invalid(type_entry));
1704717111
17048 ensure_complete_type(ira->codegen, type_entry);17112 if ((err = ensure_complete_type(ira->codegen, type_entry)))
17049 if (type_is_invalid(type_entry))
17050 return nullptr;17113 return nullptr;
1705117114
17052 const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field,17115 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...@@ -17066,63 +17129,6 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17066 enum_field_val->data.x_struct.fields = inner_fields;17129 enum_field_val->data.x_struct.fields = inner_fields;
17067 };17130 };
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
17126 if (type_entry == ira->codegen->builtin_types.entry_global_error_set) {17132 if (type_entry == ira->codegen->builtin_types.entry_global_error_set) {
17127 zig_panic("TODO implement @typeInfo for global error set");17133 zig_panic("TODO implement @typeInfo for global error set");
17128 }17134 }
...@@ -17158,7 +17164,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17158,7 +17164,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17158 {17164 {
17159 result = create_const_vals(1);17165 result = create_const_vals(1);
17160 result->special = ConstValSpecialStatic;17166 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
17163 ConstExprValue *fields = create_const_vals(2);17169 ConstExprValue *fields = create_const_vals(2);
17164 result->data.x_struct.fields = fields;17170 result->data.x_struct.fields = fields;
...@@ -17180,7 +17186,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17180,7 +17186,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17180 {17186 {
17181 result = create_const_vals(1);17187 result = create_const_vals(1);
17182 result->special = ConstValSpecialStatic;17188 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
17185 ConstExprValue *fields = create_const_vals(1);17191 ConstExprValue *fields = create_const_vals(1);
17186 result->data.x_struct.fields = fields;17192 result->data.x_struct.fields = fields;
...@@ -17195,14 +17201,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17195,14 +17201,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17195 }17201 }
17196 case TypeTableEntryIdPointer:17202 case TypeTableEntryIdPointer:
17197 {17203 {
17198 result = create_ptr_like_type_info(type_entry);17204 result = create_ptr_like_type_info(ira, type_entry);
17199 break;17205 break;
17200 }17206 }
17201 case TypeTableEntryIdArray:17207 case TypeTableEntryIdArray:
17202 {17208 {
17203 result = create_const_vals(1);17209 result = create_const_vals(1);
17204 result->special = ConstValSpecialStatic;17210 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
17207 ConstExprValue *fields = create_const_vals(2);17213 ConstExprValue *fields = create_const_vals(2);
17208 result->data.x_struct.fields = fields;17214 result->data.x_struct.fields = fields;
...@@ -17224,7 +17230,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17224,7 +17230,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17224 {17230 {
17225 result = create_const_vals(1);17231 result = create_const_vals(1);
17226 result->special = ConstValSpecialStatic;17232 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
17229 ConstExprValue *fields = create_const_vals(1);17235 ConstExprValue *fields = create_const_vals(1);
17230 result->data.x_struct.fields = fields;17236 result->data.x_struct.fields = fields;
...@@ -17241,7 +17247,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17241,7 +17247,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17241 {17247 {
17242 result = create_const_vals(1);17248 result = create_const_vals(1);
17243 result->special = ConstValSpecialStatic;17249 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
17246 ConstExprValue *fields = create_const_vals(1);17252 ConstExprValue *fields = create_const_vals(1);
17247 result->data.x_struct.fields = fields;17253 result->data.x_struct.fields = fields;
...@@ -17267,7 +17273,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17267,7 +17273,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17267 {17273 {
17268 result = create_const_vals(1);17274 result = create_const_vals(1);
17269 result->special = ConstValSpecialStatic;17275 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
17272 ConstExprValue *fields = create_const_vals(4);17278 ConstExprValue *fields = create_const_vals(4);
17273 result->data.x_struct.fields = fields;17279 result->data.x_struct.fields = fields;
...@@ -17275,7 +17281,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17275,7 +17281,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17275 // layout: ContainerLayout17281 // layout: ContainerLayout
17276 ensure_field_index(result->type, "layout", 0);17282 ensure_field_index(result->type, "layout", 0);
17277 fields[0].special = ConstValSpecialStatic;17283 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);
17279 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.enumeration.layout);17285 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.enumeration.layout);
17280 // tag_type: type17286 // tag_type: type
17281 ensure_field_index(result->type, "tag_type", 1);17287 ensure_field_index(result->type, "tag_type", 1);
...@@ -17285,7 +17291,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17285,7 +17291,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17285 // fields: []TypeInfo.EnumField17291 // fields: []TypeInfo.EnumField
17286 ensure_field_index(result->type, "fields", 2);17292 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);
17289 uint32_t enum_field_count = type_entry->data.enumeration.src_field_count;17295 uint32_t enum_field_count = type_entry->data.enumeration.src_field_count;
1729017296
17291 ConstExprValue *enum_field_array = create_const_vals(1);17297 ConstExprValue *enum_field_array = create_const_vals(1);
...@@ -17317,7 +17323,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17317,7 +17323,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17317 {17323 {
17318 result = create_const_vals(1);17324 result = create_const_vals(1);
17319 result->special = ConstValSpecialStatic;17325 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
17322 ConstExprValue *fields = create_const_vals(1);17328 ConstExprValue *fields = create_const_vals(1);
17323 result->data.x_struct.fields = fields;17329 result->data.x_struct.fields = fields;
...@@ -17325,7 +17331,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17325,7 +17331,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17325 // errors: []TypeInfo.Error17331 // errors: []TypeInfo.Error
17326 ensure_field_index(result->type, "errors", 0);17332 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);
17329 uint32_t error_count = type_entry->data.error_set.err_count;17335 uint32_t error_count = type_entry->data.error_set.err_count;
17330 ConstExprValue *error_array = create_const_vals(1);17336 ConstExprValue *error_array = create_const_vals(1);
17331 error_array->special = ConstValSpecialStatic;17337 error_array->special = ConstValSpecialStatic;
...@@ -17367,7 +17373,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17367,7 +17373,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17367 {17373 {
17368 result = create_const_vals(1);17374 result = create_const_vals(1);
17369 result->special = ConstValSpecialStatic;17375 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
17372 ConstExprValue *fields = create_const_vals(2);17378 ConstExprValue *fields = create_const_vals(2);
17373 result->data.x_struct.fields = fields;17379 result->data.x_struct.fields = fields;
...@@ -17390,7 +17396,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17390,7 +17396,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17390 {17396 {
17391 result = create_const_vals(1);17397 result = create_const_vals(1);
17392 result->special = ConstValSpecialStatic;17398 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
17395 ConstExprValue *fields = create_const_vals(4);17401 ConstExprValue *fields = create_const_vals(4);
17396 result->data.x_struct.fields = fields;17402 result->data.x_struct.fields = fields;
...@@ -17398,7 +17404,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17398,7 +17404,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17398 // layout: ContainerLayout17404 // layout: ContainerLayout
17399 ensure_field_index(result->type, "layout", 0);17405 ensure_field_index(result->type, "layout", 0);
17400 fields[0].special = ConstValSpecialStatic;17406 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);
17402 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.unionation.layout);17408 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.unionation.layout);
17403 // tag_type: ?type17409 // tag_type: ?type
17404 ensure_field_index(result->type, "tag_type", 1);17410 ensure_field_index(result->type, "tag_type", 1);
...@@ -17420,7 +17426,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17420,7 +17426,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17420 // fields: []TypeInfo.UnionField17426 // fields: []TypeInfo.UnionField
17421 ensure_field_index(result->type, "fields", 2);17427 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);
17424 uint32_t union_field_count = type_entry->data.unionation.src_field_count;17430 uint32_t union_field_count = type_entry->data.unionation.src_field_count;
1742517431
17426 ConstExprValue *union_field_array = create_const_vals(1);17432 ConstExprValue *union_field_array = create_const_vals(1);
...@@ -17432,7 +17438,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17432,7 +17438,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1743217438
17433 init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false);17439 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
17437 for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) {17443 for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) {
17438 TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];17444 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...@@ -17474,13 +17480,13 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17474 case TypeTableEntryIdStruct:17480 case TypeTableEntryIdStruct:
17475 {17481 {
17476 if (type_entry->data.structure.is_slice) {17482 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);
17478 break;17484 break;
17479 }17485 }
1748017486
17481 result = create_const_vals(1);17487 result = create_const_vals(1);
17482 result->special = ConstValSpecialStatic;17488 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
17485 ConstExprValue *fields = create_const_vals(3);17491 ConstExprValue *fields = create_const_vals(3);
17486 result->data.x_struct.fields = fields;17492 result->data.x_struct.fields = fields;
...@@ -17488,12 +17494,12 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17488,12 +17494,12 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17488 // layout: ContainerLayout17494 // layout: ContainerLayout
17489 ensure_field_index(result->type, "layout", 0);17495 ensure_field_index(result->type, "layout", 0);
17490 fields[0].special = ConstValSpecialStatic;17496 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);
17492 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.structure.layout);17498 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.structure.layout);
17493 // fields: []TypeInfo.StructField17499 // fields: []TypeInfo.StructField
17494 ensure_field_index(result->type, "fields", 1);17500 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);
17497 uint32_t struct_field_count = type_entry->data.structure.src_field_count;17503 uint32_t struct_field_count = type_entry->data.structure.src_field_count;
1749817504
17499 ConstExprValue *struct_field_array = create_const_vals(1);17505 ConstExprValue *struct_field_array = create_const_vals(1);
...@@ -17549,7 +17555,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17549,7 +17555,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17549 {17555 {
17550 result = create_const_vals(1);17556 result = create_const_vals(1);
17551 result->special = ConstValSpecialStatic;17557 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
17554 ConstExprValue *fields = create_const_vals(6);17560 ConstExprValue *fields = create_const_vals(6);
17555 result->data.x_struct.fields = fields;17561 result->data.x_struct.fields = fields;
...@@ -17557,7 +17563,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17557,7 +17563,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17557 // calling_convention: TypeInfo.CallingConvention17563 // calling_convention: TypeInfo.CallingConvention
17558 ensure_field_index(result->type, "calling_convention", 0);17564 ensure_field_index(result->type, "calling_convention", 0);
17559 fields[0].special = ConstValSpecialStatic;17565 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);
17561 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);17567 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
17562 // is_generic: bool17568 // is_generic: bool
17563 ensure_field_index(result->type, "is_generic", 1);17569 ensure_field_index(result->type, "is_generic", 1);
...@@ -17598,7 +17604,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -17598,7 +17604,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
17598 fields[4].data.x_optional = async_alloc_type;17604 fields[4].data.x_optional = async_alloc_type;
17599 }17605 }
17600 // args: []TypeInfo.FnArg17606 // 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);
17602 size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count -17608 size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count -
17603 (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC);17609 (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,...@@ -17673,7 +17679,7 @@ static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira,
17673 if (type_is_invalid(type_entry))17679 if (type_is_invalid(type_entry))
17674 return ira->codegen->builtin_types.entry_invalid;17680 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
17678 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);17684 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
17679 out_val->type = result_type;17685 out_val->type = result_type;
...@@ -18883,13 +18889,13 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio...@@ -18883,13 +18889,13 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio
18883}18889}
1888418890
18885static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInstructionMemberCount *instruction) {18891static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrInstructionMemberCount *instruction) {
18892 Error err;
18886 IrInstruction *container = instruction->container->other;18893 IrInstruction *container = instruction->container->other;
18887 if (type_is_invalid(container->value.type))18894 if (type_is_invalid(container->value.type))
18888 return ira->codegen->builtin_types.entry_invalid;18895 return ira->codegen->builtin_types.entry_invalid;
18889 TypeTableEntry *container_type = ir_resolve_type(ira, container);18896 TypeTableEntry *container_type = ir_resolve_type(ira, container);
1889018897
18891 ensure_complete_type(ira->codegen, container_type);18898 if ((err = ensure_complete_type(ira->codegen, container_type)))
18892 if (type_is_invalid(container_type))
18893 return ira->codegen->builtin_types.entry_invalid;18899 return ira->codegen->builtin_types.entry_invalid;
1889418900
18895 uint64_t result;18901 uint64_t result;
...@@ -18921,13 +18927,13 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns...@@ -18921,13 +18927,13 @@ static TypeTableEntry *ir_analyze_instruction_member_count(IrAnalyze *ira, IrIns
18921}18927}
1892218928
18923static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstructionMemberType *instruction) {18929static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInstructionMemberType *instruction) {
18930 Error err;
18924 IrInstruction *container_type_value = instruction->container_type->other;18931 IrInstruction *container_type_value = instruction->container_type->other;
18925 TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);18932 TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);
18926 if (type_is_invalid(container_type))18933 if (type_is_invalid(container_type))
18927 return ira->codegen->builtin_types.entry_invalid;18934 return ira->codegen->builtin_types.entry_invalid;
1892818935
18929 ensure_complete_type(ira->codegen, container_type);18936 if ((err = ensure_complete_type(ira->codegen, container_type)))
18930 if (type_is_invalid(container_type))
18931 return ira->codegen->builtin_types.entry_invalid;18937 return ira->codegen->builtin_types.entry_invalid;
1893218938
1893318939
...@@ -18968,13 +18974,13 @@ static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInst...@@ -18968,13 +18974,13 @@ static TypeTableEntry *ir_analyze_instruction_member_type(IrAnalyze *ira, IrInst
18968}18974}
1896918975
18970static TypeTableEntry *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstructionMemberName *instruction) {18976static TypeTableEntry *ir_analyze_instruction_member_name(IrAnalyze *ira, IrInstructionMemberName *instruction) {
18977 Error err;
18971 IrInstruction *container_type_value = instruction->container_type->other;18978 IrInstruction *container_type_value = instruction->container_type->other;
18972 TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);18979 TypeTableEntry *container_type = ir_resolve_type(ira, container_type_value);
18973 if (type_is_invalid(container_type))18980 if (type_is_invalid(container_type))
18974 return ira->codegen->builtin_types.entry_invalid;18981 return ira->codegen->builtin_types.entry_invalid;
1897518982
18976 ensure_complete_type(ira->codegen, container_type);18983 if ((err = ensure_complete_type(ira->codegen, container_type)))
18977 if (type_is_invalid(container_type))
18978 return ira->codegen->builtin_types.entry_invalid;18984 return ira->codegen->builtin_types.entry_invalid;
1897918985
18980 uint64_t member_index;18986 uint64_t member_index;
...@@ -19055,13 +19061,13 @@ static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructi...@@ -19055,13 +19061,13 @@ static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructi
19055}19061}
1905619062
19057static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {19063static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
19064 Error err;
19058 IrInstruction *type_value = instruction->type_value->other;19065 IrInstruction *type_value = instruction->type_value->other;
19059 if (type_is_invalid(type_value->value.type))19066 if (type_is_invalid(type_value->value.type))
19060 return ira->codegen->builtin_types.entry_invalid;19067 return ira->codegen->builtin_types.entry_invalid;
19061 TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);19068 TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
1906219069
19063 type_ensure_zero_bits_known(ira->codegen, type_entry);19070 if ((err = type_ensure_zero_bits_known(ira->codegen, type_entry)))
19064 if (type_is_invalid(type_entry))
19065 return ira->codegen->builtin_types.entry_invalid;19071 return ira->codegen->builtin_types.entry_invalid;
1906619072
19067 switch (type_entry->id) {19073 switch (type_entry->id) {
...@@ -19917,6 +19923,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue...@@ -19917,6 +19923,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
19917}19923}
1991819924
19919static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) {19925static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstructionBitCast *instruction) {
19926 Error err;
19920 IrInstruction *dest_type_value = instruction->dest_type->other;19927 IrInstruction *dest_type_value = instruction->dest_type->other;
19921 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);19928 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
19922 if (type_is_invalid(dest_type))19929 if (type_is_invalid(dest_type))
...@@ -19927,12 +19934,10 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc...@@ -19927,12 +19934,10 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc
19927 if (type_is_invalid(src_type))19934 if (type_is_invalid(src_type))
19928 return ira->codegen->builtin_types.entry_invalid;19935 return ira->codegen->builtin_types.entry_invalid;
1992919936
19930 ensure_complete_type(ira->codegen, dest_type);19937 if ((err = ensure_complete_type(ira->codegen, dest_type)))
19931 if (type_is_invalid(dest_type))
19932 return ira->codegen->builtin_types.entry_invalid;19938 return ira->codegen->builtin_types.entry_invalid;
1993319939
19934 ensure_complete_type(ira->codegen, src_type);19940 if ((err = ensure_complete_type(ira->codegen, src_type)))
19935 if (type_is_invalid(src_type))
19936 return ira->codegen->builtin_types.entry_invalid;19941 return ira->codegen->builtin_types.entry_invalid;
1993719942
19938 if (get_codegen_ptr_type(src_type) != nullptr) {19943 if (get_codegen_ptr_type(src_type) != nullptr) {
...@@ -20018,6 +20023,7 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc...@@ -20018,6 +20023,7 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc
20018}20023}
2001920024
20020static TypeTableEntry *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) {20025static TypeTableEntry *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstructionIntToPtr *instruction) {
20026 Error err;
20021 IrInstruction *dest_type_value = instruction->dest_type->other;20027 IrInstruction *dest_type_value = instruction->dest_type->other;
20022 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);20028 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
20023 if (type_is_invalid(dest_type))20029 if (type_is_invalid(dest_type))
...@@ -20028,7 +20034,8 @@ static TypeTableEntry *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstr...@@ -20028,7 +20034,8 @@ static TypeTableEntry *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstr
20028 return ira->codegen->builtin_types.entry_invalid;20034 return ira->codegen->builtin_types.entry_invalid;
20029 }20035 }
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;
20032 if (!type_has_bits(dest_type)) {20039 if (!type_has_bits(dest_type)) {
20033 ir_add_error(ira, dest_type_value,20040 ir_add_error(ira, dest_type_value,
20034 buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name)));20041 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...@@ -20161,6 +20168,7 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr
20161}20168}
2016220169
20163static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtrType *instruction) {20170static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstructionPtrType *instruction) {
20171 Error err;
20164 TypeTableEntry *child_type = ir_resolve_type(ira, instruction->child_type->other);20172 TypeTableEntry *child_type = ir_resolve_type(ira, instruction->child_type->other);
20165 if (type_is_invalid(child_type))20173 if (type_is_invalid(child_type))
20166 return ira->codegen->builtin_types.entry_invalid;20174 return ira->codegen->builtin_types.entry_invalid;
...@@ -20178,8 +20186,7 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruc...@@ -20178,8 +20186,7 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruc
20178 if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes))20186 if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes))
20179 return ira->codegen->builtin_types.entry_invalid;20187 return ira->codegen->builtin_types.entry_invalid;
20180 } else {20188 } else {
20181 type_ensure_zero_bits_known(ira->codegen, child_type);20189 if ((err = type_ensure_zero_bits_known(ira->codegen, child_type)))
20182 if (type_is_invalid(child_type))
20183 return ira->codegen->builtin_types.entry_invalid;20190 return ira->codegen->builtin_types.entry_invalid;
20184 align_bytes = get_abi_alignment(ira->codegen, child_type);20191 align_bytes = get_abi_alignment(ira->codegen, child_type);
20185 }20192 }
...@@ -20299,22 +20306,21 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc...@@ -20299,22 +20306,21 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc
20299}20306}
2030020307
20301static TypeTableEntry *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstructionTagType *instruction) {20308static TypeTableEntry *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstructionTagType *instruction) {
20309 Error err;
20302 IrInstruction *target_inst = instruction->target->other;20310 IrInstruction *target_inst = instruction->target->other;
20303 TypeTableEntry *enum_type = ir_resolve_type(ira, target_inst);20311 TypeTableEntry *enum_type = ir_resolve_type(ira, target_inst);
20304 if (type_is_invalid(enum_type))20312 if (type_is_invalid(enum_type))
20305 return ira->codegen->builtin_types.entry_invalid;20313 return ira->codegen->builtin_types.entry_invalid;
2030620314
20307 if (enum_type->id == TypeTableEntryIdEnum) {20315 if (enum_type->id == TypeTableEntryIdEnum) {
20308 ensure_complete_type(ira->codegen, enum_type);20316 if ((err = ensure_complete_type(ira->codegen, enum_type)))
20309 if (type_is_invalid(enum_type))
20310 return ira->codegen->builtin_types.entry_invalid;20317 return ira->codegen->builtin_types.entry_invalid;
2031120318
20312 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);20319 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
20313 out_val->data.x_type = enum_type->data.enumeration.tag_int_type;20320 out_val->data.x_type = enum_type->data.enumeration.tag_int_type;
20314 return ira->codegen->builtin_types.entry_type;20321 return ira->codegen->builtin_types.entry_type;
20315 } else if (enum_type->id == TypeTableEntryIdUnion) {20322 } else if (enum_type->id == TypeTableEntryIdUnion) {
20316 ensure_complete_type(ira->codegen, enum_type);20323 if ((err = ensure_complete_type(ira->codegen, enum_type)))
20317 if (type_is_invalid(enum_type))
20318 return ira->codegen->builtin_types.entry_invalid;20324 return ira->codegen->builtin_types.entry_invalid;
2031920325
20320 AstNode *decl_node = enum_type->data.unionation.decl_node;20326 AstNode *decl_node = enum_type->data.unionation.decl_node;
...@@ -20591,7 +20597,7 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr...@@ -20591,7 +20597,7 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr
20591 return ira->codegen->builtin_types.entry_invalid;20597 return ira->codegen->builtin_types.entry_invalid;
2059220598
20593 IrInstruction *casted_operand = ir_implicit_cast(ira, operand, operand_type);20599 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))
20595 return ira->codegen->builtin_types.entry_invalid;20601 return ira->codegen->builtin_types.entry_invalid;
2059620602
20597 AtomicOrder ordering;20603 AtomicOrder ordering;
...@@ -20817,6 +20823,7 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction...@@ -20817,6 +20823,7 @@ static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstruction
20817}20823}
2081820824
20819static TypeTableEntry *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) {20825static TypeTableEntry *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstructionEnumToInt *instruction) {
20826 Error err;
20820 IrInstruction *target = instruction->target->other;20827 IrInstruction *target = instruction->target->other;
20821 if (type_is_invalid(target->value.type))20828 if (type_is_invalid(target->value.type))
20822 return ira->codegen->builtin_types.entry_invalid;20829 return ira->codegen->builtin_types.entry_invalid;
...@@ -20827,8 +20834,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInst...@@ -20827,8 +20834,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInst
20827 return ira->codegen->builtin_types.entry_invalid;20834 return ira->codegen->builtin_types.entry_invalid;
20828 }20835 }
2082920836
20830 type_ensure_zero_bits_known(ira->codegen, target->value.type);20837 if ((err = type_ensure_zero_bits_known(ira->codegen, target->value.type)))
20831 if (type_is_invalid(target->value.type))
20832 return ira->codegen->builtin_types.entry_invalid;20838 return ira->codegen->builtin_types.entry_invalid;
2083320839
20834 TypeTableEntry *tag_type = target->value.type->data.enumeration.tag_int_type;20840 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...@@ -20839,6 +20845,7 @@ static TypeTableEntry *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInst
20839}20845}
2084020846
20841static TypeTableEntry *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) {20847static TypeTableEntry *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstructionIntToEnum *instruction) {
20848 Error err;
20842 IrInstruction *dest_type_value = instruction->dest_type->other;20849 IrInstruction *dest_type_value = instruction->dest_type->other;
20843 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);20850 TypeTableEntry *dest_type = ir_resolve_type(ira, dest_type_value);
20844 if (type_is_invalid(dest_type))20851 if (type_is_invalid(dest_type))
...@@ -20850,8 +20857,7 @@ static TypeTableEntry *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInst...@@ -20850,8 +20857,7 @@ static TypeTableEntry *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInst
20850 return ira->codegen->builtin_types.entry_invalid;20857 return ira->codegen->builtin_types.entry_invalid;
20851 }20858 }
2085220859
20853 type_ensure_zero_bits_known(ira->codegen, dest_type);20860 if ((err = type_ensure_zero_bits_known(ira->codegen, dest_type)))
20854 if (type_is_invalid(dest_type))
20855 return ira->codegen->builtin_types.entry_invalid;20861 return ira->codegen->builtin_types.entry_invalid;
2085620862
20857 TypeTableEntry *tag_type = dest_type->data.enumeration.tag_int_type;20863 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...@@ -2759,7 +2759,9 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
2759 AstNode *child_statement;2759 AstNode *child_statement;
2760 child_scope = trans_stmt(c, &child_block_scope->base, stmt->getBody(), &child_statement);2760 child_scope = trans_stmt(c, &child_block_scope->base, stmt->getBody(), &child_statement);
2761 if (child_scope == nullptr) return nullptr;2761 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 }
2763 }2765 }
27642766
2765 // if (!cond) break;2767 // if (!cond) break;
...@@ -2769,6 +2771,7 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt...@@ -2769,6 +2771,7 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
2769 terminator_node->data.if_bool_expr.condition = trans_create_node_prefix_op(c, PrefixOpBoolNot, condition_node);2771 terminator_node->data.if_bool_expr.condition = trans_create_node_prefix_op(c, PrefixOpBoolNot, condition_node);
2770 terminator_node->data.if_bool_expr.then_block = trans_create_node(c, NodeTypeBreak);2772 terminator_node->data.if_bool_expr.then_block = trans_create_node(c, NodeTypeBreak);
27712773
2774 assert(terminator_node != nullptr);
2772 body_node->data.block.statements.append(terminator_node);2775 body_node->data.block.statements.append(terminator_node);
27732776
2774 while_scope->node->data.while_expr.body = body_node;2777 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...@@ -2832,7 +2835,12 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt
2832 TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(), &body_statement);2835 TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(), &body_statement);
2833 if (body_scope == nullptr)2836 if (body_scope == nullptr)
2834 return nullptr;2837 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
2837 return loop_block_node;2845 return loop_block_node;
2838}2846}
...@@ -3067,9 +3075,14 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,...@@ -3067,9 +3075,14 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
3067 trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt));3075 trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt));
3068 case Stmt::DeclStmtClass:3076 case Stmt::DeclStmtClass:
3069 return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope);3077 return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope);
3070 case Stmt::WhileStmtClass:3078 case Stmt::WhileStmtClass: {
3071 return wrap_stmt(out_node, out_child_scope, scope,3079 AstNode *while_node = trans_while_loop(c, scope, (const WhileStmt *)stmt);
3072 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 }
3073 case Stmt::IfStmtClass:3086 case Stmt::IfStmtClass:
3074 return wrap_stmt(out_node, out_child_scope, scope,3087 return wrap_stmt(out_node, out_child_scope, scope,
3075 trans_if_statement(c, scope, (const IfStmt *)stmt));3088 trans_if_statement(c, scope, (const IfStmt *)stmt));
...@@ -3092,12 +3105,18 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,...@@ -3092,12 +3105,18 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
3092 case Stmt::UnaryExprOrTypeTraitExprClass:3105 case Stmt::UnaryExprOrTypeTraitExprClass:
3093 return wrap_stmt(out_node, out_child_scope, scope,3106 return wrap_stmt(out_node, out_child_scope, scope,
3094 trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt));3107 trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt));
3095 case Stmt::DoStmtClass:3108 case Stmt::DoStmtClass: {
3096 return wrap_stmt(out_node, out_child_scope, scope,3109 AstNode *while_node = trans_do_loop(c, scope, (const DoStmt *)stmt);
3097 trans_do_loop(c, scope, (const DoStmt *)stmt));3110 assert(while_node->type == NodeTypeWhileExpr);
3098 case Stmt::ForStmtClass:3111 if (while_node->data.while_expr.body == nullptr) {
3099 return wrap_stmt(out_node, out_child_scope, scope,3112 while_node->data.while_expr.body = trans_create_node(c, NodeTypeBlock);
3100 trans_for_loop(c, scope, (const ForStmt *)stmt));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 }
3101 case Stmt::StringLiteralClass:3120 case Stmt::StringLiteralClass:
3102 return wrap_stmt(out_node, out_child_scope, scope,3121 return wrap_stmt(out_node, out_child_scope, scope,
3103 trans_string_literal(c, scope, (const StringLiteral *)stmt));3122 trans_string_literal(c, scope, (const StringLiteral *)stmt));
src/util.hpp+2
...@@ -21,6 +21,7 @@...@@ -21,6 +21,7 @@
21#define ATTRIBUTE_PRINTF(a, b)21#define ATTRIBUTE_PRINTF(a, b)
22#define ATTRIBUTE_RETURNS_NOALIAS __declspec(restrict)22#define ATTRIBUTE_RETURNS_NOALIAS __declspec(restrict)
23#define ATTRIBUTE_NORETURN __declspec(noreturn)23#define ATTRIBUTE_NORETURN __declspec(noreturn)
24#define ATTRIBUTE_MUST_USE
2425
25#else26#else
2627
...@@ -28,6 +29,7 @@...@@ -28,6 +29,7 @@
28#define ATTRIBUTE_PRINTF(a, b) __attribute__((format(printf, a, b)))29#define ATTRIBUTE_PRINTF(a, b) __attribute__((format(printf, a, b)))
29#define ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__))30#define ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__))
30#define ATTRIBUTE_NORETURN __attribute__((noreturn))31#define ATTRIBUTE_NORETURN __attribute__((noreturn))
32#define ATTRIBUTE_MUST_USE __attribute__((warn_unused_result))
3133
32#endif34#endif
3335
std/atomic/queue.zig+60-24
...@@ -1,40 +1,38 @@...@@ -1,40 +1,38 @@
1const std = @import("../index.zig");
1const builtin = @import("builtin");2const builtin = @import("builtin");
2const AtomicOrder = builtin.AtomicOrder;3const AtomicOrder = builtin.AtomicOrder;
3const AtomicRmwOp = builtin.AtomicRmwOp;4const AtomicRmwOp = builtin.AtomicRmwOp;
5const assert = std.debug.assert;
46
5/// Many producer, many consumer, non-allocating, thread-safe.7/// Many producer, many consumer, non-allocating, thread-safe.
6/// Uses a spinlock to protect get() and put().8/// Uses a mutex to protect access.
7pub fn Queue(comptime T: type) type {9pub fn Queue(comptime T: type) type {
8 return struct {10 return struct {
9 head: ?*Node,11 head: ?*Node,
10 tail: ?*Node,12 tail: ?*Node,
11 lock: u8,13 mutex: std.Mutex,
1214
13 pub const Self = this;15 pub const Self = this;
1416 pub const Node = std.LinkedList(T).Node;
15 pub const Node = struct {
16 next: ?*Node,
17 data: T,
18 };
1917
20 pub fn init() Self {18 pub fn init() Self {
21 return Self{19 return Self{
22 .head = null,20 .head = null,
23 .tail = null,21 .tail = null,
24 .lock = 0,22 .mutex = std.Mutex.init(),
25 };23 };
26 }24 }
2725
28 pub fn put(self: *Self, node: *Node) void {26 pub fn put(self: *Self, node: *Node) void {
29 node.next = null;27 node.next = null;
3028
31 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}29 const held = self.mutex.acquire();
32 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);30 defer held.release();
3331
34 const opt_tail = self.tail;32 node.prev = self.tail;
35 self.tail = node;33 self.tail = node;
36 if (opt_tail) |tail| {34 if (node.prev) |prev_tail| {
37 tail.next = node;35 prev_tail.next = node;
38 } else {36 } else {
39 assert(self.head == null);37 assert(self.head == null);
40 self.head = node;38 self.head = node;
...@@ -42,18 +40,27 @@ pub fn Queue(comptime T: type) type {...@@ -42,18 +40,27 @@ pub fn Queue(comptime T: type) type {
42 }40 }
4341
44 pub fn get(self: *Self) ?*Node {42 pub fn get(self: *Self) ?*Node {
45 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}43 const held = self.mutex.acquire();
46 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);44 defer held.release();
4745
48 const head = self.head orelse return null;46 const head = self.head orelse return null;
49 self.head = head.next;47 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;
51 return head;56 return head;
52 }57 }
5358
54 pub fn unget(self: *Self, node: *Node) void {59 pub fn unget(self: *Self, node: *Node) void {
55 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}60 node.prev = null;
56 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);61
62 const held = self.mutex.acquire();
63 defer held.release();
5764
58 const opt_head = self.head;65 const opt_head = self.head;
59 self.head = node;66 self.head = node;
...@@ -65,13 +72,39 @@ pub fn Queue(comptime T: type) type {...@@ -65,13 +72,39 @@ pub fn Queue(comptime T: type) type {
65 }72 }
66 }73 }
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
68 pub fn isEmpty(self: *Self) bool {99 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;
70 }103 }
71104
72 pub fn dump(self: *Self) void {105 pub fn dump(self: *Self) void {
73 while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {}106 const held = self.mutex.acquire();
74 defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1);107 defer held.release();
75108
76 std.debug.warn("head: ");109 std.debug.warn("head: ");
77 dumpRecursive(self.head, 0);110 dumpRecursive(self.head, 0);
...@@ -93,9 +126,6 @@ pub fn Queue(comptime T: type) type {...@@ -93,9 +126,6 @@ pub fn Queue(comptime T: type) type {
93 };126 };
94}127}
95128
96const std = @import("../index.zig");
97const assert = std.debug.assert;
98
99const Context = struct {129const Context = struct {
100 allocator: *std.mem.Allocator,130 allocator: *std.mem.Allocator,
101 queue: *Queue(i32),131 queue: *Queue(i32),
...@@ -169,6 +199,7 @@ fn startPuts(ctx: *Context) u8 {...@@ -169,6 +199,7 @@ fn startPuts(ctx: *Context) u8 {
169 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz199 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
170 const x = @bitCast(i32, r.random.scalar(u32));200 const x = @bitCast(i32, r.random.scalar(u32));
171 const node = ctx.allocator.create(Queue(i32).Node{201 const node = ctx.allocator.create(Queue(i32).Node{
202 .prev = undefined,
172 .next = undefined,203 .next = undefined,
173 .data = x,204 .data = x,
174 }) catch unreachable;205 }) catch unreachable;
...@@ -198,12 +229,14 @@ test "std.atomic.Queue single-threaded" {...@@ -198,12 +229,14 @@ test "std.atomic.Queue single-threaded" {
198 var node_0 = Queue(i32).Node{229 var node_0 = Queue(i32).Node{
199 .data = 0,230 .data = 0,
200 .next = undefined,231 .next = undefined,
232 .prev = undefined,
201 };233 };
202 queue.put(&node_0);234 queue.put(&node_0);
203235
204 var node_1 = Queue(i32).Node{236 var node_1 = Queue(i32).Node{
205 .data = 1,237 .data = 1,
206 .next = undefined,238 .next = undefined,
239 .prev = undefined,
207 };240 };
208 queue.put(&node_1);241 queue.put(&node_1);
209242
...@@ -212,12 +245,14 @@ test "std.atomic.Queue single-threaded" {...@@ -212,12 +245,14 @@ test "std.atomic.Queue single-threaded" {
212 var node_2 = Queue(i32).Node{245 var node_2 = Queue(i32).Node{
213 .data = 2,246 .data = 2,
214 .next = undefined,247 .next = undefined,
248 .prev = undefined,
215 };249 };
216 queue.put(&node_2);250 queue.put(&node_2);
217251
218 var node_3 = Queue(i32).Node{252 var node_3 = Queue(i32).Node{
219 .data = 3,253 .data = 3,
220 .next = undefined,254 .next = undefined,
255 .prev = undefined,
221 };256 };
222 queue.put(&node_3);257 queue.put(&node_3);
223258
...@@ -228,6 +263,7 @@ test "std.atomic.Queue single-threaded" {...@@ -228,6 +263,7 @@ test "std.atomic.Queue single-threaded" {
228 var node_4 = Queue(i32).Node{263 var node_4 = Queue(i32).Node{
229 .data = 4,264 .data = 4,
230 .next = undefined,265 .next = undefined,
266 .prev = undefined,
231 };267 };
232 queue.put(&node_4);268 queue.put(&node_4);
233269
std/build.zig+76-61
...@@ -267,7 +267,7 @@ pub const Builder = struct {...@@ -267,7 +267,7 @@ pub const Builder = struct {
267 if (self.verbose) {267 if (self.verbose) {
268 warn("rm {}\n", installed_file);268 warn("rm {}\n", installed_file);
269 }269 }
270 _ = os.deleteFile(self.allocator, installed_file);270 _ = os.deleteFile(installed_file);
271 }271 }
272272
273 // TODO remove empty directories273 // TODO remove empty directories
...@@ -424,60 +424,69 @@ pub const Builder = struct {...@@ -424,60 +424,69 @@ pub const Builder = struct {
424 return mode;424 return mode;
425 }425 }
426426
427 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) bool {427 pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool {
428 if (self.user_input_options.put(name, UserInputOption{428 const gop = try self.user_input_options.getOrPut(name);
429 .name = name,429 if (!gop.found_existing) {
430 .value = UserValue{ .Scalar = value },430 gop.kv.value = UserInputOption{
431 .used = false,431 .name = name,
432 }) catch unreachable) |*prev_value| {432 .value = UserValue{ .Scalar = value },
433 // option already exists433 .used = false,
434 switch (prev_value.value) {434 };
435 UserValue.Scalar => |s| {435 return false;
436 // turn it into a list436 }
437 var list = ArrayList([]const u8).init(self.allocator);437
438 list.append(s) catch unreachable;438 // option already exists
439 list.append(value) catch unreachable;439 switch (gop.kv.value.value) {
440 _ = self.user_input_options.put(name, UserInputOption{440 UserValue.Scalar => |s| {
441 .name = name,441 // turn it into a list
442 .value = UserValue{ .List = list },442 var list = ArrayList([]const u8).init(self.allocator);
443 .used = false,443 list.append(s) catch unreachable;
444 }) catch unreachable;444 list.append(value) catch unreachable;
445 },445 _ = self.user_input_options.put(name, UserInputOption{
446 UserValue.List => |*list| {446 .name = name,
447 // append to the list447 .value = UserValue{ .List = list },
448 list.append(value) catch unreachable;448 .used = false,
449 _ = self.user_input_options.put(name, UserInputOption{449 }) catch unreachable;
450 .name = name,450 },
451 .value = UserValue{ .List = list.* },451 UserValue.List => |*list| {
452 .used = false,452 // append to the list
453 }) catch unreachable;453 list.append(value) catch unreachable;
454 },454 _ = self.user_input_options.put(name, UserInputOption{
455 UserValue.Flag => {455 .name = name,
456 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);456 .value = UserValue{ .List = list.* },
457 return true;457 .used = false,
458 },458 }) catch unreachable;
459 }459 },
460 UserValue.Flag => {
461 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
462 return true;
463 },
460 }464 }
461 return false;465 return false;
462 }466 }
463467
464 pub fn addUserInputFlag(self: *Builder, name: []const u8) bool {468 pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool {
465 if (self.user_input_options.put(name, UserInputOption{469 const gop = try self.user_input_options.getOrPut(name);
466 .name = name,470 if (!gop.found_existing) {
467 .value = UserValue{ .Flag = {} },471 gop.kv.value = UserInputOption{
468 .used = false,472 .name = name,
469 }) catch unreachable) |*prev_value| {473 .value = UserValue{ .Flag = {} },
470 switch (prev_value.value) {474 .used = false,
471 UserValue.Scalar => |s| {475 };
472 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);476 return false;
473 return true;477 }
474 },478
475 UserValue.List => {479 // option already exists
476 warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name);480 switch (gop.kv.value.value) {
477 return true;481 UserValue.Scalar => |s| {
478 },482 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
479 UserValue.Flag => {},483 return true;
480 }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 => {},
481 }490 }
482 return false;491 return false;
483 }492 }
...@@ -603,10 +612,10 @@ pub const Builder = struct {...@@ -603,10 +612,10 @@ pub const Builder = struct {
603 }612 }
604613
605 fn copyFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {614 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);
607 }616 }
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 {
610 if (self.verbose) {619 if (self.verbose) {
611 warn("cp {} {}\n", source_path, dest_path);620 warn("cp {} {}\n", source_path, dest_path);
612 }621 }
...@@ -1173,7 +1182,7 @@ pub const LibExeObjStep = struct {...@@ -1173,7 +1182,7 @@ pub const LibExeObjStep = struct {
11731182
1174 if (self.build_options_contents.len() > 0) {1183 if (self.build_options_contents.len() > 0) {
1175 const build_options_file = try os.path.join(builder.allocator, builder.cache_root, builder.fmt("{}_build_options.zig", self.name));1184 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());
1177 try zig_args.append("--pkg-begin");1186 try zig_args.append("--pkg-begin");
1178 try zig_args.append("build_options");1187 try zig_args.append("build_options");
1179 try zig_args.append(builder.pathFromRoot(build_options_file));1188 try zig_args.append(builder.pathFromRoot(build_options_file));
...@@ -1482,11 +1491,14 @@ pub const LibExeObjStep = struct {...@@ -1482,11 +1491,14 @@ pub const LibExeObjStep = struct {
1482 }1491 }
14831492
1484 if (!is_darwin) {1493 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 ));
1486 defer builder.allocator.free(rpath_arg);1498 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");
1490 }1502 }
14911503
1492 for (self.full_path_libs.toSliceConst()) |full_path_lib| {1504 for (self.full_path_libs.toSliceConst()) |full_path_lib| {
...@@ -1557,11 +1569,14 @@ pub const LibExeObjStep = struct {...@@ -1557,11 +1569,14 @@ pub const LibExeObjStep = struct {
1557 cc_args.append("-o") catch unreachable;1569 cc_args.append("-o") catch unreachable;
1558 cc_args.append(output_path) catch unreachable;1570 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 ));
1561 defer builder.allocator.free(rpath_arg);1576 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
1566 {1581 {
1567 var it = self.link_libs.iterator();1582 var it = self.link_libs.iterator();
...@@ -1908,7 +1923,7 @@ pub const WriteFileStep = struct {...@@ -1908,7 +1923,7 @@ pub const WriteFileStep = struct {
1908 warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));1923 warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));
1909 return err;1924 return err;
1910 };1925 };
1911 io.writeFile(self.builder.allocator, full_path, self.data) catch |err| {1926 io.writeFile(full_path, self.data) catch |err| {
1912 warn("unable to write {}: {}\n", full_path, @errorName(err));1927 warn("unable to write {}: {}\n", full_path, @errorName(err));
1913 return err;1928 return err;
1914 };1929 };
std/c/darwin.zig+38-8
...@@ -1,5 +1,8 @@...@@ -1,5 +1,8 @@
1const macho = @import("../macho.zig");
2
1extern "c" fn __error() *c_int;3extern "c" fn __error() *c_int;
2pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int;4pub extern "c" fn _NSGetExecutablePath(buf: [*]u8, bufsize: *u32) c_int;
5pub extern "c" fn _dyld_get_image_header(image_index: u32) ?*mach_header;
36
4pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize;7pub 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...@@ -30,10 +33,45 @@ pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlen
30pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;33pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
31pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int;34pub 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
33pub use @import("../os/darwin/errno.zig");48pub use @import("../os/darwin/errno.zig");
3449
35pub const _errno = __error;50pub 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
37pub const timeval = extern struct {75pub const timeval = extern struct {
38 tv_sec: isize,76 tv_sec: isize,
39 tv_usec: isize,77 tv_usec: isize,
...@@ -98,14 +136,6 @@ pub const dirent = extern struct {...@@ -98,14 +136,6 @@ pub const dirent = extern struct {
98 d_name: u8, // field address is address of first byte of name136 d_name: u8, // field address is address of first byte of name
99};137};
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
109pub const pthread_attr_t = extern struct {139pub const pthread_attr_t = extern struct {
110 __sig: c_long,140 __sig: c_long,
111 __opaque: [56]u8,141 __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;...@@ -21,8 +21,10 @@ pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize;
21pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int;21pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int;
22pub extern "c" fn raise(sig: c_int) c_int;22pub extern "c" fn raise(sig: c_int) c_int;
23pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize;23pub 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;
24pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;25pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;
25pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize;26pub 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;
26pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void;28pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void;
27pub extern "c" fn munmap(addr: *c_void, len: usize) c_int;29pub extern "c" fn munmap(addr: *c_void, len: usize) c_int;
28pub extern "c" fn unlink(path: [*]const u8) c_int;30pub 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...@@ -58,6 +60,7 @@ pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias at
58pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int;60pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int;
59pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int;61pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int;
60pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;62pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;
63pub extern "pthread" fn pthread_self() pthread_t;
61pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int;64pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int;
6265
63pub const pthread_t = *@OpaqueType();66pub const pthread_t = *@OpaqueType();
std/c/linux.zig+3
...@@ -8,3 +8,6 @@ pub const pthread_attr_t = extern struct {...@@ -8,3 +8,6 @@ pub const pthread_attr_t = extern struct {
8 __size: [56]u8,8 __size: [56]u8,
9 __align: c_long,9 __align: c_long,
10};10};
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) {...@@ -9,10 +9,9 @@ pub const line_sep = switch (builtin.os) {
9 else => "\n",9 else => "\n",
10};10};
1111
12/// Deprecated, use mem.len
12pub fn len(ptr: [*]const u8) usize {13pub fn len(ptr: [*]const u8) usize {
13 var count: usize = 0;14 return mem.len(u8, ptr);
14 while (ptr[count] != 0) : (count += 1) {}
15 return count;
16}15}
1716
18pub fn cmp(a: [*]const u8, b: [*]const u8) i8 {17pub fn cmp(a: [*]const u8, b: [*]const u8) i8 {
...@@ -27,12 +26,14 @@ pub fn cmp(a: [*]const u8, b: [*]const u8) i8 {...@@ -27,12 +26,14 @@ pub fn cmp(a: [*]const u8, b: [*]const u8) i8 {
27 }26 }
28}27}
2928
29/// Deprecated, use mem.toSliceConst
30pub fn toSliceConst(str: [*]const u8) []const u8 {30pub fn toSliceConst(str: [*]const u8) []const u8 {
31 return str[0..len(str)];31 return mem.toSliceConst(u8, str);
32}32}
3333
34/// Deprecated, use mem.toSlice
34pub fn toSlice(str: [*]u8) []u8 {35pub fn toSlice(str: [*]u8) []u8 {
35 return str[0..len(str)];36 return mem.toSlice(u8, str);
36}37}
3738
38test "cstr fns" {39test "cstr fns" {
std/debug/index.zig+636-159
...@@ -4,8 +4,8 @@ const mem = std.mem;...@@ -4,8 +4,8 @@ const mem = std.mem;
4const io = std.io;4const io = std.io;
5const os = std.os;5const os = std.os;
6const elf = std.elf;6const elf = std.elf;
7const DW = std.dwarf;
8const macho = std.macho;7const macho = std.macho;
8const DW = std.dwarf;
9const ArrayList = std.ArrayList;9const ArrayList = std.ArrayList;
10const builtin = @import("builtin");10const builtin = @import("builtin");
1111
...@@ -19,14 +19,19 @@ pub const runtime_safety = switch (builtin.mode) {...@@ -19,14 +19,19 @@ pub const runtime_safety = switch (builtin.mode) {
1919
20/// Tries to write to stderr, unbuffered, and ignores any error returned.20/// Tries to write to stderr, unbuffered, and ignores any error returned.
21/// Does not append a newline.21/// Does not append a newline.
22/// TODO atomic/multithread support
23var stderr_file: os.File = undefined;22var stderr_file: os.File = undefined;
24var stderr_file_out_stream: io.FileOutStream = undefined;23var stderr_file_out_stream: io.FileOutStream = undefined;
24
25/// TODO multithreaded awareness
25var stderr_stream: ?*io.OutStream(io.FileOutStream.Error) = null;26var stderr_stream: ?*io.OutStream(io.FileOutStream.Error) = null;
27var stderr_mutex = std.Mutex.init();
26pub fn warn(comptime fmt: []const u8, args: ...) void {28pub fn warn(comptime fmt: []const u8, args: ...) void {
29 const held = stderr_mutex.acquire();
30 defer held.release();
27 const stderr = getStderrStream() catch return;31 const stderr = getStderrStream() catch return;
28 stderr.print(fmt, args) catch return;32 stderr.print(fmt, args) catch return;
29}33}
34
30pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {35pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
31 if (stderr_stream) |st| {36 if (stderr_stream) |st| {
32 return st;37 return st;
...@@ -39,14 +44,15 @@ pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {...@@ -39,14 +44,15 @@ pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
39 }44 }
40}45}
4146
42var self_debug_info: ?*ElfStackTrace = null;47/// TODO multithreaded awareness
43pub fn getSelfDebugInfo() !*ElfStackTrace {48var self_debug_info: ?DebugInfo = null;
44 if (self_debug_info) |info| {49
50pub fn getSelfDebugInfo() !*DebugInfo {
51 if (self_debug_info) |*info| {
45 return info;52 return info;
46 } else {53 } else {
47 const info = try openSelfDebugInfo(getDebugInfoAllocator());54 self_debug_info = try openSelfDebugInfo(getDebugInfoAllocator());
48 self_debug_info = info;55 return &self_debug_info.?;
49 return info;
50 }56 }
51}57}
5258
...@@ -57,6 +63,7 @@ fn wantTtyColor() bool {...@@ -57,6 +63,7 @@ fn wantTtyColor() bool {
57}63}
5864
59/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.65/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
66/// TODO multithreaded awareness
60pub fn dumpCurrentStackTrace(start_addr: ?usize) void {67pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
61 const stderr = getStderrStream() catch return;68 const stderr = getStderrStream() catch return;
62 const debug_info = getSelfDebugInfo() catch |err| {69 const debug_info = getSelfDebugInfo() catch |err| {
...@@ -70,6 +77,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {...@@ -70,6 +77,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
70}77}
7178
72/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.79/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
80/// TODO multithreaded awareness
73pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void {81pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void {
74 const stderr = getStderrStream() catch return;82 const stderr = getStderrStream() catch return;
75 const debug_info = getSelfDebugInfo() catch |err| {83 const debug_info = getSelfDebugInfo() catch |err| {
...@@ -124,6 +132,7 @@ pub fn panic(comptime format: []const u8, args: ...) noreturn {...@@ -124,6 +132,7 @@ pub fn panic(comptime format: []const u8, args: ...) noreturn {
124 panicExtra(null, first_trace_addr, format, args);132 panicExtra(null, first_trace_addr, format, args);
125}133}
126134
135/// TODO multithreaded awareness
127var panicking: u8 = 0; // TODO make this a bool136var panicking: u8 = 0; // TODO make this a bool
128137
129pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn {138pub 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";...@@ -152,7 +161,7 @@ const WHITE = "\x1b[37;1m";
152const DIM = "\x1b[2m";161const DIM = "\x1b[2m";
153const RESET = "\x1b[0m";162const 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 {
156 var frame_index: usize = undefined;165 var frame_index: usize = undefined;
157 var frames_left: usize = undefined;166 var frames_left: usize = undefined;
158 if (stack_trace.index < stack_trace.instruction_addresses.len) {167 if (stack_trace.index < stack_trace.instruction_addresses.len) {
...@@ -182,7 +191,7 @@ pub inline fn getReturnAddress(frame_count: usize) usize {...@@ -182,7 +191,7 @@ pub inline fn getReturnAddress(frame_count: usize) usize {
182 return @intToPtr(*const usize, fp + @sizeOf(usize)).*;191 return @intToPtr(*const usize, fp + @sizeOf(usize)).*;
183}192}
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 {
186 const AddressState = union(enum) {195 const AddressState = union(enum) {
187 NotLookingForStartAddress,196 NotLookingForStartAddress,
188 LookingForStartAddress: usize,197 LookingForStartAddress: usize,
...@@ -215,130 +224,292 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_...@@ -215,130 +224,292 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_
215 }224 }
216}225}
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 {
219 switch (builtin.os) {228 switch (builtin.os) {
220 builtin.Os.windows => return error.UnsupportedDebugInfo,229 builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color),
221 builtin.Os.macosx => {230 builtin.Os.linux => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color),
222 // TODO(bnoordhuis) It's theoretically possible to obtain the231 builtin.Os.windows => {
223 // compilation unit from the symbtab but it's not that useful232 // TODO https://github.com/ziglang/zig/issues/721
224 // in practice because the compiler dumps everything in a single233 return error.UnsupportedOperatingSystem;
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);
233 },234 },
234 else => {235 else => return error.UnsupportedOperatingSystem,
235 const compile_unit = findCompileUnit(debug_info, address) catch {236 }
236 if (tty_color) {237}
237 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n ???\n\n", address);238
238 } else {239fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
239 try out_stream.print("???:?:?: 0x{x} in ??? (???)\n ???\n\n", address);240 var min: usize = 0;
240 }241 var max: usize = symbols.len - 1; // Exclude sentinel.
241 return;242 while (min < max) {
242 };243 const mid = min + (max - min) / 2;
243 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);244 const curr = &symbols[mid];
244 if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {245 const next = &symbols[mid + 1];
245 defer line_info.deinit();246 if (address >= next.address()) {
246 if (tty_color) {247 min = mid + 1;
247 try out_stream.print(248 } else if (address < curr.address()) {
248 WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n",249 max = mid;
249 line_info.file_name,250 } else {
250 line_info.line,251 return curr;
251 line_info.column,252 }
252 address,253 }
253 compile_unit_name,254 return null;
254 );255}
255 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {256
256 if (line_info.column == 0) {257fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
257 try out_stream.write("\n");258 const base_addr = @ptrToInt(&std.c._mh_execute_header);
258 } else {259 const adjusted_addr = 0x100000000 + (address - base_addr);
259 {260
260 var col_i: usize = 1;261 const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse {
261 while (col_i < line_info.column) : (col_i += 1) {262 if (tty_color) {
262 try out_stream.writeByte(' ');263 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address);
263 }264 } else {
264 }265 try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address);
265 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");266 }
266 }267 return;
267 } else |err| switch (err) {268 };
268 error.EndOfFile => {},269
269 else => return err,270 const symbol_name = mem.toSliceConst(u8, di.strings.ptr + symbol.nlist.n_strx);
270 }271 const compile_unit_name = if (symbol.ofile) |ofile| blk: {
271 } else {272 const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx);
272 try out_stream.print(273 break :blk os.path.basename(ofile_path);
273 "{}:{}:{}: 0x{x} in ??? ({})\n",274 } else "???";
274 line_info.file_name,275 if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| {
275 line_info.line,276 defer line_info.deinit();
276 line_info.column,277 try printLineInfo(di, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color);
277 address,278 } else |err| switch (err) {
278 compile_unit_name,279 error.MissingDebugInfo, error.InvalidDebugInfo => {
279 );280 if (tty_color) {
280 }281 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name);
281 } else |err| switch (err) {282 } else {
282 error.MissingDebugInfo, error.InvalidDebugInfo => {283 try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", address, symbol_name, compile_unit_name);
283 try out_stream.print("0x{x} in ??? ({})\n", address, compile_unit_name);
284 },
285 else => return err,
286 }284 }
287 },285 },
286 else => return err,
288 }287 }
289}288}
290289
291pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace {290pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void {
292 switch (builtin.object_format) {291 const compile_unit = findCompileUnit(debug_info, address) catch {
293 builtin.ObjectFormat.elf => {292 if (tty_color) {
294 const st = try allocator.create(ElfStackTrace{293 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address);
295 .self_exe_file = undefined,294 } else {
296 .elf = undefined,295 try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address);
297 .debug_info = undefined,296 }
298 .debug_abbrev = undefined,297 return;
299 .debug_str = undefined,298 };
300 .debug_line = undefined,299 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
301 .debug_ranges = null,300 if (getLineNumberInfoLinux(debug_info, compile_unit, address - 1)) |line_info| {
302 .abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),301 defer line_info.deinit();
303 .compile_unit_list = ArrayList(CompileUnit).init(allocator),302 const symbol_name = "???";
304 });303 try printLineInfo(debug_info, out_stream, line_info, address, symbol_name, compile_unit_name, tty_color);
305 errdefer allocator.destroy(st);304 } else |err| switch (err) {
306 st.self_exe_file = try os.openSelfExe();305 error.MissingDebugInfo, error.InvalidDebugInfo => {
307 errdefer st.self_exe_file.close();306 if (tty_color) {
308307 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n\n\n", address, compile_unit_name);
309 try st.elf.openFile(allocator, &st.self_exe_file);308 } else {
310 errdefer st.elf.close();309 try out_stream.print("???:?:?: 0x{x} in ??? ({})\n\n\n", address, compile_unit_name);
311310 }
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;
319 },311 },
320 builtin.ObjectFormat.macho => {312 else => return err,
321 var exe_file = try os.openSelfExe();313 }
322 defer exe_file.close();314}
323315
324 const st = try allocator.create(ElfStackTrace{ .symbol_table = try macho.loadSymbols(allocator, &io.FileInStream.init(&exe_file)) });316fn printLineInfo(
325 errdefer allocator.destroy(st);317 debug_info: *DebugInfo,
326 return st;318 out_stream: var,
327 },319 line_info: LineInfo,
328 builtin.ObjectFormat.coff => {320 address: usize,
329 return error.TodoSupportCoffDebugInfo;321 symbol_name: []const u8,
330 },322 compile_unit_name: []const u8,
331 builtin.ObjectFormat.wasm => {323 tty_color: bool,
332 return error.TodoSupportCOFFDebugInfo;324) !void {
333 },325 if (tty_color) {
334 builtin.ObjectFormat.unknown => {326 try out_stream.print(
335 return error.UnknownObjectFormat;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;
336 },378 },
379 else => return error.UnsupportedOperatingSystem,
337 }380 }
338}381}
339382
340fn printLineFromFile(allocator: *mem.Allocator, out_stream: var, line_info: *const LineInfo) !void {383fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DebugInfo {
341 var f = try os.File.openRead(allocator, line_info.file_name);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);
342 defer f.close();513 defer f.close();
343 // TODO fstat and make sure that the file has the correct size514 // 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...@@ -369,12 +540,42 @@ fn printLineFromFile(allocator: *mem.Allocator, out_stream: var, line_info: *con
369 }540 }
370}541}
371542
372pub const ElfStackTrace = switch (builtin.os) {543const MachoSymbol = struct {
373 builtin.Os.macosx => struct {544 nlist: *macho.nlist_64,
374 symbol_table: macho.SymbolTable,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 {564pub const DebugInfo = switch (builtin.os) {
377 self.symbol_table.deinit();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;
378 }579 }
379 },580 },
380 else => struct {581 else => struct {
...@@ -388,17 +589,17 @@ pub const ElfStackTrace = switch (builtin.os) {...@@ -388,17 +589,17 @@ pub const ElfStackTrace = switch (builtin.os) {
388 abbrev_table_list: ArrayList(AbbrevTableHeader),589 abbrev_table_list: ArrayList(AbbrevTableHeader),
389 compile_unit_list: ArrayList(CompileUnit),590 compile_unit_list: ArrayList(CompileUnit),
390591
391 pub fn allocator(self: *const ElfStackTrace) *mem.Allocator {592 pub fn allocator(self: DebugInfo) *mem.Allocator {
392 return self.abbrev_table_list.allocator;593 return self.abbrev_table_list.allocator;
393 }594 }
394595
395 pub fn readString(self: *ElfStackTrace) ![]u8 {596 pub fn readString(self: *DebugInfo) ![]u8 {
396 var in_file_stream = io.FileInStream.init(&self.self_exe_file);597 var in_file_stream = io.FileInStream.init(&self.self_exe_file);
397 const in_stream = &in_file_stream.stream;598 const in_stream = &in_file_stream.stream;
398 return readStringRaw(self.allocator(), in_stream);599 return readStringRaw(self.allocator(), in_stream);
399 }600 }
400601
401 pub fn close(self: *ElfStackTrace) void {602 pub fn close(self: *DebugInfo) void {
402 self.self_exe_file.close();603 self.self_exe_file.close();
403 self.elf.close();604 self.elf.close();
404 }605 }
...@@ -505,7 +706,7 @@ const Die = struct {...@@ -505,7 +706,7 @@ const Die = struct {
505 };706 };
506 }707 }
507708
508 fn getAttrString(self: *const Die, st: *ElfStackTrace, id: u64) ![]u8 {709 fn getAttrString(self: *const Die, st: *DebugInfo, id: u64) ![]u8 {
509 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;710 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
510 return switch (form_value.*) {711 return switch (form_value.*) {
511 FormValue.String => |value| value,712 FormValue.String => |value| value,
...@@ -620,7 +821,7 @@ fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 {...@@ -620,7 +821,7 @@ fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 {
620 return buf.toSlice();821 return buf.toSlice();
621}822}
622823
623fn getString(st: *ElfStackTrace, offset: u64) ![]u8 {824fn getString(st: *DebugInfo, offset: u64) ![]u8 {
624 const pos = st.debug_str.offset + offset;825 const pos = st.debug_str.offset + offset;
625 try st.self_exe_file.seekTo(pos);826 try st.self_exe_file.seekTo(pos);
626 return st.readString();827 return st.readString();
...@@ -672,14 +873,10 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type...@@ -672,14 +873,10 @@ fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type
672873
673const ParseFormValueError = error{874const ParseFormValueError = error{
674 EndOfStream,875 EndOfStream,
675 Io,
676 BadFd,
677 Unexpected,
678 InvalidDebugInfo,876 InvalidDebugInfo,
679 EndOfFile,877 EndOfFile,
680 IsDir,
681 OutOfMemory,878 OutOfMemory,
682};879} || std.os.File.ReadError;
683880
684fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue {881fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) ParseFormValueError!FormValue {
685 return switch (form_id) {882 return switch (form_id) {
...@@ -731,7 +928,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64...@@ -731,7 +928,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
731 };928 };
732}929}
733930
734fn parseAbbrevTable(st: *ElfStackTrace) !AbbrevTable {931fn parseAbbrevTable(st: *DebugInfo) !AbbrevTable {
735 const in_file = &st.self_exe_file;932 const in_file = &st.self_exe_file;
736 var in_file_stream = io.FileInStream.init(in_file);933 var in_file_stream = io.FileInStream.init(in_file);
737 const in_stream = &in_file_stream.stream;934 const in_stream = &in_file_stream.stream;
...@@ -761,7 +958,7 @@ fn parseAbbrevTable(st: *ElfStackTrace) !AbbrevTable {...@@ -761,7 +958,7 @@ fn parseAbbrevTable(st: *ElfStackTrace) !AbbrevTable {
761958
762/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,959/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
763/// seeks in the stream and parses it.960/// 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 {
765 for (st.abbrev_table_list.toSlice()) |*header| {962 for (st.abbrev_table_list.toSlice()) |*header| {
766 if (header.offset == abbrev_offset) {963 if (header.offset == abbrev_offset) {
767 return &header.table;964 return &header.table;
...@@ -782,7 +979,7 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con...@@ -782,7 +979,7 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con
782 return null;979 return null;
783}980}
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 {
786 const in_file = &st.self_exe_file;983 const in_file = &st.self_exe_file;
787 var in_file_stream = io.FileInStream.init(in_file);984 var in_file_stream = io.FileInStream.init(in_file);
788 const in_stream = &in_file_stream.stream;985 const in_stream = &in_file_stream.stream;
...@@ -804,12 +1001,210 @@ fn parseDie(st: *ElfStackTrace, abbrev_table: *const AbbrevTable, is_64: bool) !...@@ -804,12 +1001,210 @@ fn parseDie(st: *ElfStackTrace, abbrev_table: *const AbbrevTable, is_64: bool) !
804 return result;1001 return result;
805}1002}
8061003
807fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, target_address: usize) !LineInfo {1004fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo {
808 const compile_unit_cwd = try compile_unit.die.getAttrString(st, DW.AT_comp_dir);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;1046 break :blk &gop.kv.value;
811 const debug_line_end = st.debug_line.offset + st.debug_line.size;1047 };
812 var this_offset = st.debug_line.offset;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;
813 var this_index: usize = 0;1208 var this_index: usize = 0;
8141209
815 var in_file_stream = io.FileInStream.init(in_file);1210 var in_file_stream = io.FileInStream.init(in_file);
...@@ -828,11 +1223,11 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe...@@ -828,11 +1223,11 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
828 continue;1223 continue;
829 }1224 }
8301225
831 const version = try in_stream.readInt(st.elf.endian, u16);1226 const version = try in_stream.readInt(di.elf.endian, u16);
832 // TODO support 3 and 51227 // TODO support 3 and 5
833 if (version != 2 and version != 4) return error.InvalidDebugInfo;1228 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);
836 const prog_start_offset = (try in_file.getPos()) + prologue_length;1231 const prog_start_offset = (try in_file.getPos()) + prologue_length;
8371232
838 const minimum_instruction_length = try in_stream.readByte();1233 const minimum_instruction_length = try in_stream.readByte();
...@@ -851,7 +1246,7 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe...@@ -851,7 +1246,7 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
8511246
852 const opcode_base = try in_stream.readByte();1247 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
856 {1251 {
857 var i: usize = 0;1252 var i: usize = 0;
...@@ -860,19 +1255,19 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe...@@ -860,19 +1255,19 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
860 }1255 }
861 }1256 }
8621257
863 var include_directories = ArrayList([]u8).init(st.allocator());1258 var include_directories = ArrayList([]u8).init(di.allocator());
864 try include_directories.append(compile_unit_cwd);1259 try include_directories.append(compile_unit_cwd);
865 while (true) {1260 while (true) {
866 const dir = try st.readString();1261 const dir = try di.readString();
867 if (dir.len == 0) break;1262 if (dir.len == 0) break;
868 try include_directories.append(dir);1263 try include_directories.append(dir);
869 }1264 }
8701265
871 var file_entries = ArrayList(FileEntry).init(st.allocator());1266 var file_entries = ArrayList(FileEntry).init(di.allocator());
872 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);1267 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
8731268
874 while (true) {1269 while (true) {
875 const file_name = try st.readString();1270 const file_name = try di.readString();
876 if (file_name.len == 0) break;1271 if (file_name.len == 0) break;
877 const dir_index = try readULeb128(in_stream);1272 const dir_index = try readULeb128(in_stream);
878 const mtime = try readULeb128(in_stream);1273 const mtime = try readULeb128(in_stream);
...@@ -890,11 +1285,10 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe...@@ -890,11 +1285,10 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
890 while (true) {1285 while (true) {
891 const opcode = try in_stream.readByte();1286 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
894 if (opcode == DW.LNS_extended_op) {1288 if (opcode == DW.LNS_extended_op) {
895 const op_size = try readULeb128(in_stream);1289 const op_size = try readULeb128(in_stream);
896 if (op_size < 1) return error.InvalidDebugInfo;1290 if (op_size < 1) return error.InvalidDebugInfo;
897 sub_op = try in_stream.readByte();1291 var sub_op = try in_stream.readByte();
898 switch (sub_op) {1292 switch (sub_op) {
899 DW.LNE_end_sequence => {1293 DW.LNE_end_sequence => {
900 prog.end_sequence = true;1294 prog.end_sequence = true;
...@@ -902,11 +1296,11 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe...@@ -902,11 +1296,11 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
902 return error.MissingDebugInfo;1296 return error.MissingDebugInfo;
903 },1297 },
904 DW.LNE_set_address => {1298 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);
906 prog.address = addr;1300 prog.address = addr;
907 },1301 },
908 DW.LNE_define_file => {1302 DW.LNE_define_file => {
909 const file_name = try st.readString();1303 const file_name = try di.readString();
910 const dir_index = try readULeb128(in_stream);1304 const dir_index = try readULeb128(in_stream);
911 const mtime = try readULeb128(in_stream);1305 const mtime = try readULeb128(in_stream);
912 const len_bytes = try readULeb128(in_stream);1306 const len_bytes = try readULeb128(in_stream);
...@@ -964,7 +1358,7 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe...@@ -964,7 +1358,7 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
964 prog.address += inc_addr;1358 prog.address += inc_addr;
965 },1359 },
966 DW.LNS_fixed_advance_pc => {1360 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);
968 prog.address += arg;1362 prog.address += arg;
969 },1363 },
970 DW.LNS_set_prologue_end => {},1364 DW.LNS_set_prologue_end => {},
...@@ -983,7 +1377,7 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe...@@ -983,7 +1377,7 @@ fn getLineNumberInfo(st: *ElfStackTrace, compile_unit: *const CompileUnit, targe
983 return error.MissingDebugInfo;1377 return error.MissingDebugInfo;
984}1378}
9851379
986fn scanAllCompileUnits(st: *ElfStackTrace) !void {1380fn scanAllCompileUnits(st: *DebugInfo) !void {
987 const debug_info_end = st.debug_info.offset + st.debug_info.size;1381 const debug_info_end = st.debug_info.offset + st.debug_info.size;
988 var this_unit_offset = st.debug_info.offset;1382 var this_unit_offset = st.debug_info.offset;
989 var cu_index: usize = 0;1383 var cu_index: usize = 0;
...@@ -1053,7 +1447,7 @@ fn scanAllCompileUnits(st: *ElfStackTrace) !void {...@@ -1053,7 +1447,7 @@ fn scanAllCompileUnits(st: *ElfStackTrace) !void {
1053 }1447 }
1054}1448}
10551449
1056fn findCompileUnit(st: *ElfStackTrace, target_address: u64) !*const CompileUnit {1450fn findCompileUnit(st: *DebugInfo, target_address: u64) !*const CompileUnit {
1057 var in_file_stream = io.FileInStream.init(&st.self_exe_file);1451 var in_file_stream = io.FileInStream.init(&st.self_exe_file);
1058 const in_stream = &in_file_stream.stream;1452 const in_stream = &in_file_stream.stream;
1059 for (st.compile_unit_list.toSlice()) |*compile_unit| {1453 for (st.compile_unit_list.toSlice()) |*compile_unit| {
...@@ -1087,6 +1481,89 @@ fn findCompileUnit(st: *ElfStackTrace, target_address: u64) !*const CompileUnit...@@ -1087,6 +1481,89 @@ fn findCompileUnit(st: *ElfStackTrace, target_address: u64) !*const CompileUnit
1087 return error.MissingDebugInfo;1481 return error.MissingDebugInfo;
1088}1482}
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
1090fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {1567fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
1091 const first_32_bits = try in_stream.readIntLe(u32);1568 const first_32_bits = try in_stream.readIntLe(u32);
1092 is_64.* = (first_32_bits == 0xffffffff);1569 is_64.* = (first_32_bits == 0xffffffff);
...@@ -1143,7 +1620,7 @@ pub const global_allocator = &global_fixed_allocator.allocator;...@@ -1143,7 +1620,7 @@ pub const global_allocator = &global_fixed_allocator.allocator;
1143var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]);1620var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]);
1144var global_allocator_mem: [100 * 1024]u8 = undefined;1621var global_allocator_mem: [100 * 1024]u8 = undefined;
11451622
1146// TODO make thread safe1623/// TODO multithreaded awareness
1147var debug_info_allocator: ?*mem.Allocator = null;1624var debug_info_allocator: ?*mem.Allocator = null;
1148var debug_info_direct_allocator: std.heap.DirectAllocator = undefined;1625var debug_info_direct_allocator: std.heap.DirectAllocator = undefined;
1149var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;1626var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
std/elf.zig+5
...@@ -869,6 +869,11 @@ pub const Phdr = switch (@sizeOf(usize)) {...@@ -869,6 +869,11 @@ pub const Phdr = switch (@sizeOf(usize)) {
869 8 => Elf64_Phdr,869 8 => Elf64_Phdr,
870 else => @compileError("expected pointer size of 32 or 64"),870 else => @compileError("expected pointer size of 32 or 64"),
871};871};
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};
872pub const Sym = switch (@sizeOf(usize)) {877pub const Sym = switch (@sizeOf(usize)) {
873 4 => Elf32_Sym,878 4 => Elf32_Sym,
874 8 => Elf64_Sym,879 8 => Elf64_Sym,
std/event.zig+14-8
...@@ -1,17 +1,23 @@...@@ -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;
1pub const Locked = @import("event/locked.zig").Locked;5pub const Locked = @import("event/locked.zig").Locked;
6pub const RwLock = @import("event/rwlock.zig").RwLock;
7pub const RwLocked = @import("event/rwlocked.zig").RwLocked;
2pub const Loop = @import("event/loop.zig").Loop;8pub const Loop = @import("event/loop.zig").Loop;
3pub const Lock = @import("event/lock.zig").Lock;9pub const fs = @import("event/fs.zig");
4pub const tcp = @import("event/tcp.zig");10pub 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
9test "import event tests" {12test "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");
10 _ = @import("event/locked.zig");18 _ = @import("event/locked.zig");
19 _ = @import("event/rwlock.zig");
20 _ = @import("event/rwlocked.zig");
11 _ = @import("event/loop.zig");21 _ = @import("event/loop.zig");
12 _ = @import("event/lock.zig");
13 _ = @import("event/tcp.zig");22 _ = @import("event/tcp.zig");
14 _ = @import("event/channel.zig");
15 _ = @import("event/group.zig");
16 _ = @import("event/future.zig");
17}23}
std/event/channel.zig+161-24
...@@ -5,7 +5,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;...@@ -5,7 +5,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
5const AtomicOrder = builtin.AtomicOrder;5const AtomicOrder = builtin.AtomicOrder;
6const Loop = std.event.Loop;6const Loop = std.event.Loop;
77
8/// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size8/// many producer, many consumer, thread-safe, runtime configurable buffer size
9/// when buffer is empty, consumers suspend and are resumed by producers9/// when buffer is empty, consumers suspend and are resumed by producers
10/// when buffer is full, producers suspend and are resumed by consumers10/// when buffer is full, producers suspend and are resumed by consumers
11pub fn Channel(comptime T: type) type {11pub fn Channel(comptime T: type) type {
...@@ -13,6 +13,7 @@ pub fn Channel(comptime T: type) type {...@@ -13,6 +13,7 @@ pub fn Channel(comptime T: type) type {
13 loop: *Loop,13 loop: *Loop,
1414
15 getters: std.atomic.Queue(GetNode),15 getters: std.atomic.Queue(GetNode),
16 or_null_queue: std.atomic.Queue(*std.atomic.Queue(GetNode).Node),
16 putters: std.atomic.Queue(PutNode),17 putters: std.atomic.Queue(PutNode),
17 get_count: usize,18 get_count: usize,
18 put_count: usize,19 put_count: usize,
...@@ -26,8 +27,22 @@ pub fn Channel(comptime T: type) type {...@@ -26,8 +27,22 @@ pub fn Channel(comptime T: type) type {
2627
27 const SelfChannel = this;28 const SelfChannel = this;
28 const GetNode = struct {29 const GetNode = struct {
29 ptr: *T,
30 tick_node: *Loop.NextTickNode,30 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 };
31 };46 };
32 const PutNode = struct {47 const PutNode = struct {
33 data: T,48 data: T,
...@@ -48,6 +63,7 @@ pub fn Channel(comptime T: type) type {...@@ -48,6 +63,7 @@ pub fn Channel(comptime T: type) type {
48 .need_dispatch = 0,63 .need_dispatch = 0,
49 .getters = std.atomic.Queue(GetNode).init(),64 .getters = std.atomic.Queue(GetNode).init(),
50 .putters = std.atomic.Queue(PutNode).init(),65 .putters = std.atomic.Queue(PutNode).init(),
66 .or_null_queue = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).init(),
51 .get_count = 0,67 .get_count = 0,
52 .put_count = 0,68 .put_count = 0,
53 });69 });
...@@ -71,18 +87,29 @@ pub fn Channel(comptime T: type) type {...@@ -71,18 +87,29 @@ pub fn Channel(comptime T: type) type {
71 /// puts a data item in the channel. The promise completes when the value has been added to the87 /// puts a data item in the channel. The promise completes when the value has been added to the
72 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.88 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
73 pub async fn put(self: *SelfChannel, data: T) void {89 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 }
74 suspend {112 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 };
86 self.putters.put(&queue_node);113 self.putters.put(&queue_node);
87 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);114 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
88115
...@@ -93,23 +120,95 @@ pub fn Channel(comptime T: type) type {...@@ -93,23 +120,95 @@ pub fn Channel(comptime T: type) type {
93 /// await this function to get an item from the channel. If the buffer is empty, the promise will120 /// await this function to get an item from the channel. If the buffer is empty, the promise will
94 /// complete when the next item is put in the channel.121 /// complete when the next item is put in the channel.
95 pub async fn get(self: *SelfChannel) T {122 pub async fn get(self: *SelfChannel) T {
123 // TODO fix this workaround
124 suspend {
125 resume @handle();
126 }
127
96 // TODO integrate this function with named return values128 // TODO integrate this function with named return values
97 // so we can get rid of this extra result copy129 // so we can get rid of this extra result copy
98 var result: T = undefined;130 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
99 suspend {175 suspend {
100 var my_tick_node = Loop.NextTickNode{176 resume @handle();
101 .next = undefined,177 }
102 .data = @handle(),178
103 };179 // TODO integrate this function with named return values
104 var queue_node = std.atomic.Queue(GetNode).Node{180 // so we can get rid of this extra result copy
105 .data = GetNode{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{
106 .ptr = &result,188 .ptr = &result,
107 .tick_node = &my_tick_node,189 .or_null = &or_null_node,
108 },190 },
109 .next = undefined,191 },
110 };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 {
111 self.getters.put(&queue_node);209 self.getters.put(&queue_node);
112 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);210 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
211 self.or_null_queue.put(&or_null_node);
113212
114 self.dispatch();213 self.dispatch();
115 }214 }
...@@ -139,7 +238,15 @@ pub fn Channel(comptime T: type) type {...@@ -139,7 +238,15 @@ pub fn Channel(comptime T: type) type {
139 if (get_count == 0) break :one_dispatch;238 if (get_count == 0) break :one_dispatch;
140239
141 const get_node = &self.getters.get().?.data;240 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 }
143 self.loop.onNextTick(get_node.tick_node);250 self.loop.onNextTick(get_node.tick_node);
144 self.buffer_len -= 1;251 self.buffer_len -= 1;
145252
...@@ -151,7 +258,15 @@ pub fn Channel(comptime T: type) type {...@@ -151,7 +258,15 @@ pub fn Channel(comptime T: type) type {
151 const get_node = &self.getters.get().?.data;258 const get_node = &self.getters.get().?.data;
152 const put_node = &self.putters.get().?.data;259 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 }
155 self.loop.onNextTick(get_node.tick_node);270 self.loop.onNextTick(get_node.tick_node);
156 self.loop.onNextTick(put_node.tick_node);271 self.loop.onNextTick(put_node.tick_node);
157272
...@@ -176,6 +291,16 @@ pub fn Channel(comptime T: type) type {...@@ -176,6 +291,16 @@ pub fn Channel(comptime T: type) type {
176 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);291 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
177 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);292 _ = @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
179 // clear need-dispatch flag304 // clear need-dispatch flag
180 const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);305 const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
181 if (need_dispatch != 0) continue;306 if (need_dispatch != 0) continue;
...@@ -226,6 +351,15 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {...@@ -226,6 +351,15 @@ async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {
226 const value2_promise = try async channel.get();351 const value2_promise = try async channel.get();
227 const value2 = await value2_promise;352 const value2 = await value2_promise;
228 assert(value2 == 4567);353 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;
229}363}
230364
231async fn testChannelPutter(channel: *Channel(i32)) void {365async fn testChannelPutter(channel: *Channel(i32)) void {
...@@ -233,3 +367,6 @@ async fn testChannelPutter(channel: *Channel(i32)) void {...@@ -233,3 +367,6 @@ async fn testChannelPutter(channel: *Channel(i32)) void {
233 await (async channel.put(4567) catch @panic("out of memory"));367 await (async channel.put(4567) catch @panic("out of memory"));
234}368}
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 {...@@ -29,6 +29,17 @@ pub fn Group(comptime ReturnType: type) type {
29 };29 };
30 }30 }
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
32 /// Add a promise to the group. Thread-safe.43 /// Add a promise to the group. Thread-safe.
33 pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) {44 pub fn add(self: *Self, handle: promise->ReturnType) (error{OutOfMemory}!void) {
34 const node = try self.lock.loop.allocator.create(Stack.Node{45 const node = try self.lock.loop.allocator.create(Stack.Node{
...@@ -88,7 +99,7 @@ pub fn Group(comptime ReturnType: type) type {...@@ -88,7 +99,7 @@ pub fn Group(comptime ReturnType: type) type {
88 await node.data;99 await node.data;
89 } else {100 } else {
90 (await node.data) catch |err| {101 (await node.data) catch |err| {
91 self.cancelAll();102 self.deinit();
92 return err;103 return err;
93 };104 };
94 }105 }
...@@ -100,25 +111,12 @@ pub fn Group(comptime ReturnType: type) type {...@@ -100,25 +111,12 @@ pub fn Group(comptime ReturnType: type) type {
100 await handle;111 await handle;
101 } else {112 } else {
102 (await handle) catch |err| {113 (await handle) catch |err| {
103 self.cancelAll();114 self.deinit();
104 return err;115 return err;
105 };116 };
106 }117 }
107 }118 }
108 }119 }
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 }
122 };120 };
123}121}
124122
std/event/lock.zig+10-5
...@@ -9,6 +9,7 @@ const Loop = std.event.Loop;...@@ -9,6 +9,7 @@ const Loop = std.event.Loop;
9/// Thread-safe async/await lock.9/// Thread-safe async/await lock.
10/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and10/// 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.11/// are resumed when the lock is released, in order.
12/// Allows only one actor to hold the lock.
12pub const Lock = struct {13pub const Lock = struct {
13 loop: *Loop,14 loop: *Loop,
14 shared_bit: u8, // TODO make this a bool15 shared_bit: u8, // TODO make this a bool
...@@ -90,13 +91,14 @@ pub const Lock = struct {...@@ -90,13 +91,14 @@ pub const Lock = struct {
90 }91 }
9192
92 pub async fn acquire(self: *Lock) Held {93 pub async fn acquire(self: *Lock) Held {
94 // TODO explicitly put this memory in the coroutine frame #1194
93 suspend {95 suspend {
94 // TODO explicitly put this memory in the coroutine frame #119496 resume @handle();
95 var my_tick_node = Loop.NextTickNode{97 }
96 .data = @handle(),98 var my_tick_node = Loop.NextTickNode.init(@handle());
97 .next = undefined,
98 };
9999
100 errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire
101 suspend {
100 self.queue.put(&my_tick_node);102 self.queue.put(&my_tick_node);
101103
102 // At this point, we are in the queue, so we might have already been resumed and this coroutine104 // 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 {...@@ -146,6 +148,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
146 }148 }
147 const handle1 = async lockRunner(lock) catch @panic("out of memory");149 const handle1 = async lockRunner(lock) catch @panic("out of memory");
148 var tick_node1 = Loop.NextTickNode{150 var tick_node1 = Loop.NextTickNode{
151 .prev = undefined,
149 .next = undefined,152 .next = undefined,
150 .data = handle1,153 .data = handle1,
151 };154 };
...@@ -153,6 +156,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {...@@ -153,6 +156,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
153156
154 const handle2 = async lockRunner(lock) catch @panic("out of memory");157 const handle2 = async lockRunner(lock) catch @panic("out of memory");
155 var tick_node2 = Loop.NextTickNode{158 var tick_node2 = Loop.NextTickNode{
159 .prev = undefined,
156 .next = undefined,160 .next = undefined,
157 .data = handle2,161 .data = handle2,
158 };162 };
...@@ -160,6 +164,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {...@@ -160,6 +164,7 @@ async fn testLock(loop: *Loop, lock: *Lock) void {
160164
161 const handle3 = async lockRunner(lock) catch @panic("out of memory");165 const handle3 = async lockRunner(lock) catch @panic("out of memory");
162 var tick_node3 = Loop.NextTickNode{166 var tick_node3 = Loop.NextTickNode{
167 .prev = undefined,
163 .next = undefined,168 .next = undefined,
164 .data = handle3,169 .data = handle3,
165 };170 };
std/event/loop.zig+337-99
...@@ -2,10 +2,12 @@ const std = @import("../index.zig");...@@ -2,10 +2,12 @@ const std = @import("../index.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const mem = std.mem;4const mem = std.mem;
5const posix = std.os.posix;
6const windows = std.os.windows;
7const AtomicRmwOp = builtin.AtomicRmwOp;5const AtomicRmwOp = builtin.AtomicRmwOp;
8const AtomicOrder = builtin.AtomicOrder;6const AtomicOrder = builtin.AtomicOrder;
7const fs = std.event.fs;
8const os = std.os;
9const posix = os.posix;
10const windows = os.windows;
911
10pub const Loop = struct {12pub const Loop = struct {
11 allocator: *mem.Allocator,13 allocator: *mem.Allocator,
...@@ -13,7 +15,7 @@ pub const Loop = struct {...@@ -13,7 +15,7 @@ pub const Loop = struct {
13 os_data: OsData,15 os_data: OsData,
14 final_resume_node: ResumeNode,16 final_resume_node: ResumeNode,
15 pending_event_count: usize,17 pending_event_count: usize,
16 extra_threads: []*std.os.Thread,18 extra_threads: []*os.Thread,
1719
18 // pre-allocated eventfds. all permanently active.20 // pre-allocated eventfds. all permanently active.
19 // this is how we send promises to be resumed on other threads.21 // this is how we send promises to be resumed on other threads.
...@@ -50,6 +52,22 @@ pub const Loop = struct {...@@ -50,6 +52,22 @@ pub const Loop = struct {
50 base: ResumeNode,52 base: ResumeNode,
51 kevent: posix.Kevent,53 kevent: posix.Kevent,
52 };54 };
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 };
53 };71 };
5472
55 /// After initialization, call run().73 /// After initialization, call run().
...@@ -65,7 +83,7 @@ pub const Loop = struct {...@@ -65,7 +83,7 @@ pub const Loop = struct {
65 /// TODO copy elision / named return values so that the threads referencing *Loop83 /// TODO copy elision / named return values so that the threads referencing *Loop
66 /// have the correct pointer value.84 /// have the correct pointer value.
67 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {85 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);
69 return self.initInternal(allocator, core_count);87 return self.initInternal(allocator, core_count);
70 }88 }
7189
...@@ -92,7 +110,7 @@ pub const Loop = struct {...@@ -92,7 +110,7 @@ pub const Loop = struct {
92 );110 );
93 errdefer self.allocator.free(self.eventfd_resume_nodes);111 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);
96 errdefer self.allocator.free(self.extra_threads);114 errdefer self.allocator.free(self.extra_threads);
97115
98 try self.initOsData(extra_thread_count);116 try self.initOsData(extra_thread_count);
...@@ -104,17 +122,30 @@ pub const Loop = struct {...@@ -104,17 +122,30 @@ pub const Loop = struct {
104 self.allocator.free(self.extra_threads);122 self.allocator.free(self.extra_threads);
105 }123 }
106124
107 const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError ||125 const InitOsDataError = os.LinuxEpollCreateError || mem.Allocator.Error || os.LinuxEventFdError ||
108 std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError ||126 os.SpawnThreadError || os.LinuxEpollCtlError || os.BsdKEventError ||
109 std.os.WindowsCreateIoCompletionPortError;127 os.WindowsCreateIoCompletionPortError;
110128
111 const wakeup_bytes = []u8{0x1} ** 8;129 const wakeup_bytes = []u8{0x1} ** 8;
112130
113 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {131 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {
114 switch (builtin.os) {132 switch (builtin.os) {
115 builtin.Os.linux => {133 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
116 errdefer {147 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);
118 }149 }
119 for (self.eventfd_resume_nodes) |*eventfd_node| {150 for (self.eventfd_resume_nodes) |*eventfd_node| {
120 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{151 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
...@@ -123,7 +154,7 @@ pub const Loop = struct {...@@ -123,7 +154,7 @@ pub const Loop = struct {
123 .id = ResumeNode.Id.EventFd,154 .id = ResumeNode.Id.EventFd,
124 .handle = undefined,155 .handle = undefined,
125 },156 },
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),
127 .epoll_op = posix.EPOLL_CTL_ADD,158 .epoll_op = posix.EPOLL_CTL_ADD,
128 },159 },
129 .next = undefined,160 .next = undefined,
...@@ -131,44 +162,62 @@ pub const Loop = struct {...@@ -131,44 +162,62 @@ pub const Loop = struct {
131 self.available_eventfd_resume_nodes.push(eventfd_node);162 self.available_eventfd_resume_nodes.push(eventfd_node);
132 }163 }
133164
134 self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC);165 self.os_data.epollfd = try os.linuxEpollCreate(posix.EPOLL_CLOEXEC);
135 errdefer std.os.close(self.os_data.epollfd);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);168 self.os_data.final_eventfd = try os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK);
138 errdefer std.os.close(self.os_data.final_eventfd);169 errdefer os.close(self.os_data.final_eventfd);
139170
140 self.os_data.final_eventfd_event = posix.epoll_event{171 self.os_data.final_eventfd_event = posix.epoll_event{
141 .events = posix.EPOLLIN,172 .events = posix.EPOLLIN,
142 .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) },173 .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) },
143 };174 };
144 try std.os.linuxEpollCtl(175 try os.linuxEpollCtl(
145 self.os_data.epollfd,176 self.os_data.epollfd,
146 posix.EPOLL_CTL_ADD,177 posix.EPOLL_CTL_ADD,
147 self.os_data.final_eventfd,178 self.os_data.final_eventfd,
148 &self.os_data.final_eventfd_event,179 &self.os_data.final_eventfd_event,
149 );180 );
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
151 var extra_thread_index: usize = 0;188 var extra_thread_index: usize = 0;
152 errdefer {189 errdefer {
153 // writing 8 bytes to an eventfd cannot fail190 // 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;
155 while (extra_thread_index != 0) {192 while (extra_thread_index != 0) {
156 extra_thread_index -= 1;193 extra_thread_index -= 1;
157 self.extra_threads[extra_thread_index].wait();194 self.extra_threads[extra_thread_index].wait();
158 }195 }
159 }196 }
160 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {197 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);
162 }199 }
163 },200 },
164 builtin.Os.macosx => {201 builtin.Os.macosx => {
165 self.os_data.kqfd = try std.os.bsdKQueue();202 self.os_data.kqfd = try os.bsdKQueue();
166 errdefer std.os.close(self.os_data.kqfd);203 errdefer os.close(self.os_data.kqfd);
167204
168 self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count);205 self.os_data.fs_kqfd = try os.bsdKQueue();
169 errdefer self.allocator.free(self.os_data.kevents);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
173 for (self.eventfd_resume_nodes) |*eventfd_node, i| {222 for (self.eventfd_resume_nodes) |*eventfd_node, i| {
174 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{223 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
...@@ -191,18 +240,9 @@ pub const Loop = struct {...@@ -191,18 +240,9 @@ pub const Loop = struct {
191 };240 };
192 self.available_eventfd_resume_nodes.push(eventfd_node);241 self.available_eventfd_resume_nodes.push(eventfd_node);
193 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent);242 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);
195 eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE;244 eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE;
196 eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER;245 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 };
206 }246 }
207247
208 // Pre-add so that we cannot get error.SystemResources248 // Pre-add so that we cannot get error.SystemResources
...@@ -215,31 +255,55 @@ pub const Loop = struct {...@@ -215,31 +255,55 @@ pub const Loop = struct {
215 .data = 0,255 .data = 0,
216 .udata = @ptrToInt(&self.final_resume_node),256 .udata = @ptrToInt(&self.final_resume_node),
217 };257 };
218 const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent);258 const final_kev_arr = (*[1]posix.Kevent)(&self.os_data.final_kevent);
219 _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);259 _ = try os.bsdKEvent(self.os_data.kqfd, final_kev_arr, empty_kevs, null);
220 self.os_data.final_kevent.flags = posix.EV_ENABLE;260 self.os_data.final_kevent.flags = posix.EV_ENABLE;
221 self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER;261 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
223 var extra_thread_index: usize = 0;287 var extra_thread_index: usize = 0;
224 errdefer {288 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;
226 while (extra_thread_index != 0) {290 while (extra_thread_index != 0) {
227 extra_thread_index -= 1;291 extra_thread_index -= 1;
228 self.extra_threads[extra_thread_index].wait();292 self.extra_threads[extra_thread_index].wait();
229 }293 }
230 }294 }
231 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {295 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);
233 }297 }
234 },298 },
235 builtin.Os.windows => {299 builtin.Os.windows => {
236 self.os_data.io_port = try std.os.windowsCreateIoCompletionPort(300 self.os_data.io_port = try os.windowsCreateIoCompletionPort(
237 windows.INVALID_HANDLE_VALUE,301 windows.INVALID_HANDLE_VALUE,
238 null,302 null,
239 undefined,303 undefined,
240 undefined,304 @maxValue(windows.DWORD),
241 );305 );
242 errdefer std.os.close(self.os_data.io_port);306 errdefer os.close(self.os_data.io_port);
243307
244 for (self.eventfd_resume_nodes) |*eventfd_node, i| {308 for (self.eventfd_resume_nodes) |*eventfd_node, i| {
245 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{309 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
...@@ -262,7 +326,7 @@ pub const Loop = struct {...@@ -262,7 +326,7 @@ pub const Loop = struct {
262 while (i < extra_thread_index) : (i += 1) {326 while (i < extra_thread_index) : (i += 1) {
263 while (true) {327 while (true) {
264 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);328 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;
266 break;330 break;
267 }331 }
268 }332 }
...@@ -272,7 +336,7 @@ pub const Loop = struct {...@@ -272,7 +336,7 @@ pub const Loop = struct {
272 }336 }
273 }337 }
274 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {338 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);
276 }340 }
277 },341 },
278 else => {},342 else => {},
...@@ -282,63 +346,113 @@ pub const Loop = struct {...@@ -282,63 +346,113 @@ pub const Loop = struct {
282 fn deinitOsData(self: *Loop) void {346 fn deinitOsData(self: *Loop) void {
283 switch (builtin.os) {347 switch (builtin.os) {
284 builtin.Os.linux => {348 builtin.Os.linux => {
285 std.os.close(self.os_data.final_eventfd);349 os.close(self.os_data.final_eventfd);
286 while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);350 while (self.available_eventfd_resume_nodes.pop()) |node| os.close(node.data.eventfd);
287 std.os.close(self.os_data.epollfd);351 os.close(self.os_data.epollfd);
288 self.allocator.free(self.eventfd_resume_nodes);352 self.allocator.free(self.eventfd_resume_nodes);
289 },353 },
290 builtin.Os.macosx => {354 builtin.Os.macosx => {
291 self.allocator.free(self.os_data.kevents);355 os.close(self.os_data.kqfd);
292 std.os.close(self.os_data.kqfd);356 os.close(self.os_data.fs_kqfd);
293 },357 },
294 builtin.Os.windows => {358 builtin.Os.windows => {
295 std.os.close(self.os_data.io_port);359 os.close(self.os_data.io_port);
296 },360 },
297 else => {},361 else => {},
298 }362 }
299 }363 }
300364
301 /// resume_node must live longer than the promise that it holds a reference to.365 /// 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 {366 /// flags must contain EPOLLET
303 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);367 pub fn linuxAddFd(self: *Loop, fd: i32, resume_node: *ResumeNode, flags: u32) !void {
304 errdefer {368 assert(flags & posix.EPOLLET == posix.EPOLLET);
305 self.finishOneEvent();369 self.beginOneEvent();
306 }370 errdefer self.finishOneEvent();
307 try self.modFd(371 try self.linuxModFd(
308 fd,372 fd,
309 posix.EPOLL_CTL_ADD,373 posix.EPOLL_CTL_ADD,
310 std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET,374 flags,
311 resume_node,375 resume_node,
312 );376 );
313 }377 }
314378
315 pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void {379 pub fn linuxModFd(self: *Loop, fd: i32, op: u32, flags: u32, resume_node: *ResumeNode) !void {
316 var ev = std.os.linux.epoll_event{380 assert(flags & posix.EPOLLET == posix.EPOLLET);
317 .events = events,381 var ev = os.linux.epoll_event{
318 .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },382 .events = flags,
383 .data = os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },
319 };384 };
320 try std.os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);385 try os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);
321 }386 }
322387
323 pub fn removeFd(self: *Loop, fd: i32) void {388 pub fn linuxRemoveFd(self: *Loop, fd: i32) void {
324 self.removeFdNoCounter(fd);389 os.linuxEpollCtl(self.os_data.epollfd, os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
325 self.finishOneEvent();390 self.finishOneEvent();
326 }391 }
327392
328 fn removeFdNoCounter(self: *Loop, fd: i32) void {393 pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
329 std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};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 }
330 }405 }
331406
332 pub async fn waitFd(self: *Loop, fd: i32) !void {407 pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !posix.Kevent {
333 defer self.removeFd(fd);408 // TODO #1194
334 suspend {409 suspend {
335 // TODO explicitly put this memory in the coroutine frame #1194410 resume @handle();
336 var resume_node = ResumeNode{411 }
412 var resume_node = ResumeNode.Basic{
413 .base = ResumeNode{
337 .id = ResumeNode.Id.Basic,414 .id = ResumeNode.Id.Basic,
338 .handle = @handle(),415 .handle = @handle(),
339 };416 },
340 try self.addFd(fd, &resume_node);417 .kev = undefined,
418 };
419 defer self.bsdRemoveKev(ident, filter);
420 suspend {
421 try self.bsdAddKev(&resume_node, ident, filter, fflags);
341 }422 }
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();
342 }456 }
343457
344 fn dispatch(self: *Loop) void {458 fn dispatch(self: *Loop) void {
...@@ -352,8 +466,8 @@ pub const Loop = struct {...@@ -352,8 +466,8 @@ pub const Loop = struct {
352 switch (builtin.os) {466 switch (builtin.os) {
353 builtin.Os.macosx => {467 builtin.Os.macosx => {
354 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);468 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);
355 const eventlist = ([*]posix.Kevent)(undefined)[0..0];469 const empty_kevs = ([*]posix.Kevent)(undefined)[0..0];
356 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {470 _ = os.bsdKEvent(self.os_data.kqfd, kevent_array, empty_kevs, null) catch {
357 self.next_tick_queue.unget(next_tick_node);471 self.next_tick_queue.unget(next_tick_node);
358 self.available_eventfd_resume_nodes.push(resume_stack_node);472 self.available_eventfd_resume_nodes.push(resume_stack_node);
359 return;473 return;
...@@ -361,9 +475,9 @@ pub const Loop = struct {...@@ -361,9 +475,9 @@ pub const Loop = struct {
361 },475 },
362 builtin.Os.linux => {476 builtin.Os.linux => {
363 // the pending count is already accounted for477 // the pending count is already accounted for
364 const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT |478 const epoll_events = posix.EPOLLONESHOT | os.linux.EPOLLIN | os.linux.EPOLLOUT |
365 std.os.linux.EPOLLET;479 os.linux.EPOLLET;
366 self.modFd(480 self.linuxModFd(
367 eventfd_node.eventfd,481 eventfd_node.eventfd,
368 eventfd_node.epoll_op,482 eventfd_node.epoll_op,
369 epoll_events,483 epoll_events,
...@@ -379,7 +493,7 @@ pub const Loop = struct {...@@ -379,7 +493,7 @@ pub const Loop = struct {
379 // the consumer code can decide whether to read the completion key.493 // the consumer code can decide whether to read the completion key.
380 // it has to do this for normal I/O, so we match that behavior here.494 // it has to do this for normal I/O, so we match that behavior here.
381 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);495 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
382 std.os.windowsPostQueuedCompletionStatus(496 os.windowsPostQueuedCompletionStatus(
383 self.os_data.io_port,497 self.os_data.io_port,
384 undefined,498 undefined,
385 eventfd_node.completion_key,499 eventfd_node.completion_key,
...@@ -397,15 +511,29 @@ pub const Loop = struct {...@@ -397,15 +511,29 @@ pub const Loop = struct {
397511
398 /// Bring your own linked list node. This means it can't fail.512 /// Bring your own linked list node. This means it can't fail.
399 pub fn onNextTick(self: *Loop, node: *NextTickNode) void {513 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()
401 self.next_tick_queue.put(node);515 self.next_tick_queue.put(node);
402 self.dispatch();516 self.dispatch();
403 }517 }
404518
519 pub fn cancelOnNextTick(self: *Loop, node: *NextTickNode) void {
520 if (self.next_tick_queue.remove(node)) {
521 self.finishOneEvent();
522 }
523 }
524
405 pub fn run(self: *Loop) void {525 pub fn run(self: *Loop) void {
406 self.finishOneEvent(); // the reference we start with526 self.finishOneEvent(); // the reference we start with
407527
408 self.workerRun();528 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
409 for (self.extra_threads) |extra_thread| {537 for (self.extra_threads) |extra_thread| {
410 extra_thread.wait();538 extra_thread.wait();
411 }539 }
...@@ -420,6 +548,7 @@ pub const Loop = struct {...@@ -420,6 +548,7 @@ pub const Loop = struct {
420 suspend {548 suspend {
421 handle.* = @handle();549 handle.* = @handle();
422 var my_tick_node = Loop.NextTickNode{550 var my_tick_node = Loop.NextTickNode{
551 .prev = undefined,
423 .next = undefined,552 .next = undefined,
424 .data = @handle(),553 .data = @handle(),
425 };554 };
...@@ -441,6 +570,7 @@ pub const Loop = struct {...@@ -441,6 +570,7 @@ pub const Loop = struct {
441 pub async fn yield(self: *Loop) void {570 pub async fn yield(self: *Loop) void {
442 suspend {571 suspend {
443 var my_tick_node = Loop.NextTickNode{572 var my_tick_node = Loop.NextTickNode{
573 .prev = undefined,
444 .next = undefined,574 .next = undefined,
445 .data = @handle(),575 .data = @handle(),
446 };576 };
...@@ -448,20 +578,28 @@ pub const Loop = struct {...@@ -448,20 +578,28 @@ pub const Loop = struct {
448 }578 }
449 }579 }
450580
451 fn finishOneEvent(self: *Loop) void {581 /// call finishOneEvent when done
452 if (@atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst) == 1) {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) {
453 // cause all the threads to stop589 // cause all the threads to stop
454 switch (builtin.os) {590 switch (builtin.os) {
455 builtin.Os.linux => {591 builtin.Os.linux => {
592 self.posixFsRequest(&self.os_data.fs_end_request);
456 // writing 8 bytes to an eventfd cannot fail593 // 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;
458 return;595 return;
459 },596 },
460 builtin.Os.macosx => {597 builtin.Os.macosx => {
598 self.posixFsRequest(&self.os_data.fs_end_request);
461 const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);599 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];
463 // cannot fail because we already added it and this just enables it601 // 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;
465 return;603 return;
466 },604 },
467 builtin.Os.windows => {605 builtin.Os.windows => {
...@@ -469,7 +607,7 @@ pub const Loop = struct {...@@ -469,7 +607,7 @@ pub const Loop = struct {
469 while (i < self.extra_threads.len + 1) : (i += 1) {607 while (i < self.extra_threads.len + 1) : (i += 1) {
470 while (true) {608 while (true) {
471 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);609 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;
473 break;611 break;
474 }612 }
475 }613 }
...@@ -492,8 +630,8 @@ pub const Loop = struct {...@@ -492,8 +630,8 @@ pub const Loop = struct {
492 switch (builtin.os) {630 switch (builtin.os) {
493 builtin.Os.linux => {631 builtin.Os.linux => {
494 // only process 1 event so we don't steal from other threads632 // only process 1 event so we don't steal from other threads
495 var events: [1]std.os.linux.epoll_event = undefined;633 var events: [1]os.linux.epoll_event = undefined;
496 const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);634 const count = os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);
497 for (events[0..count]) |ev| {635 for (events[0..count]) |ev| {
498 const resume_node = @intToPtr(*ResumeNode, ev.data.ptr);636 const resume_node = @intToPtr(*ResumeNode, ev.data.ptr);
499 const handle = resume_node.handle;637 const handle = resume_node.handle;
...@@ -516,13 +654,17 @@ pub const Loop = struct {...@@ -516,13 +654,17 @@ pub const Loop = struct {
516 },654 },
517 builtin.Os.macosx => {655 builtin.Os.macosx => {
518 var eventlist: [1]posix.Kevent = undefined;656 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;
520 for (eventlist[0..count]) |ev| {659 for (eventlist[0..count]) |ev| {
521 const resume_node = @intToPtr(*ResumeNode, ev.udata);660 const resume_node = @intToPtr(*ResumeNode, ev.udata);
522 const handle = resume_node.handle;661 const handle = resume_node.handle;
523 const resume_node_id = resume_node.id;662 const resume_node_id = resume_node.id;
524 switch (resume_node_id) {663 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 },
526 ResumeNode.Id.Stop => return,668 ResumeNode.Id.Stop => return,
527 ResumeNode.Id.EventFd => {669 ResumeNode.Id.EventFd => {
528 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);670 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);
...@@ -541,9 +683,10 @@ pub const Loop = struct {...@@ -541,9 +683,10 @@ pub const Loop = struct {
541 while (true) {683 while (true) {
542 var nbytes: windows.DWORD = undefined;684 var nbytes: windows.DWORD = undefined;
543 var overlapped: ?*windows.OVERLAPPED = undefined;685 var overlapped: ?*windows.OVERLAPPED = undefined;
544 switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) {686 switch (os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) {
545 std.os.WindowsWaitResult.Aborted => return,687 os.WindowsWaitResult.Aborted => return,
546 std.os.WindowsWaitResult.Normal => {},688 os.WindowsWaitResult.Normal => {},
689 os.WindowsWaitResult.Cancelled => continue,
547 }690 }
548 if (overlapped != null) break;691 if (overlapped != null) break;
549 }692 }
...@@ -560,21 +703,101 @@ pub const Loop = struct {...@@ -560,21 +703,101 @@ pub const Loop = struct {
560 },703 },
561 }704 }
562 resume handle;705 resume handle;
563 if (resume_node_id == ResumeNode.Id.EventFd) {706 self.finishOneEvent();
564 self.finishOneEvent();
565 }
566 },707 },
567 else => @compileError("unsupported OS"),708 else => @compileError("unsupported OS"),
568 }709 }
569 }710 }
570 }711 }
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
572 const OsData = switch (builtin.os) {799 const OsData = switch (builtin.os) {
573 builtin.Os.linux => struct {800 builtin.Os.linux => LinuxOsData,
574 epollfd: i32,
575 final_eventfd: i32,
576 final_eventfd_event: std.os.linux.epoll_event,
577 },
578 builtin.Os.macosx => MacOsData,801 builtin.Os.macosx => MacOsData,
579 builtin.Os.windows => struct {802 builtin.Os.windows => struct {
580 io_port: windows.HANDLE,803 io_port: windows.HANDLE,
...@@ -586,7 +809,22 @@ pub const Loop = struct {...@@ -586,7 +809,22 @@ pub const Loop = struct {
586 const MacOsData = struct {809 const MacOsData = struct {
587 kqfd: i32,810 kqfd: i32,
588 final_kevent: posix.Kevent,811 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,
590 };828 };
591};829};
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 {...@@ -55,13 +55,13 @@ pub const Server = struct {
55 errdefer cancel self.accept_coro.?;55 errdefer cancel self.accept_coro.?;
5656
57 self.listen_resume_node.handle = self.accept_coro.?;57 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);
59 errdefer self.loop.removeFd(sockfd);59 errdefer self.loop.removeFd(sockfd);
60 }60 }
6161
62 /// Stop listening62 /// Stop listening
63 pub fn close(self: *Server) void {63 pub fn close(self: *Server) void {
64 self.loop.removeFd(self.sockfd.?);64 self.loop.linuxRemoveFd(self.sockfd.?);
65 std.os.close(self.sockfd.?);65 std.os.close(self.sockfd.?);
66 }66 }
6767
...@@ -116,7 +116,7 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File...@@ -116,7 +116,7 @@ pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File
116 errdefer std.os.close(sockfd);116 errdefer std.os.close(sockfd);
117117
118 try std.os.posixConnectAsync(sockfd, &address.os_addr);118 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);
120 try std.os.posixGetSockOptConnectError(sockfd);120 try std.os.posixGetSockOptConnectError(sockfd);
121121
122 return std.os.File.openHandle(sockfd);122 return std.os.File.openHandle(sockfd);
...@@ -181,4 +181,3 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Serv...@@ -181,4 +181,3 @@ async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Serv
181 assert(mem.eql(u8, msg, "hello from server\n"));181 assert(mem.eql(u8, msg, "hello from server\n"));
182 server.close();182 server.close();
183}183}
184
std/fmt/errol/index.zig-4
...@@ -253,11 +253,7 @@ fn gethi(in: f64) f64 {...@@ -253,11 +253,7 @@ fn gethi(in: f64) f64 {
253/// Normalize the number by factoring in the error.253/// Normalize the number by factoring in the error.
254/// @hp: The float pair.254/// @hp: The float pair.
255fn hpNormalize(hp: *HP) void {255fn 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
259 const val = hp.val;256 const val = hp.val;
260
261 hp.val += hp.off;257 hp.val += hp.off;
262 hp.off += val - hp.val;258 hp.off += val - hp.val;
263}259}
std/fmt/index.zig+65-32
...@@ -146,6 +146,45 @@ pub fn formatType(...@@ -146,6 +146,45 @@ pub fn formatType(
146 builtin.TypeId.Promise => {146 builtin.TypeId.Promise => {
147 return format(context, Errors, output, "promise@{x}", @ptrToInt(value));147 return format(context, Errors, output, "promise@{x}", @ptrToInt(value));
148 },148 },
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 },
149 builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) {188 builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) {
150 builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) {189 builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) {
151 builtin.TypeId.Array => |info| {190 builtin.TypeId.Array => |info| {
...@@ -155,31 +194,13 @@ pub fn formatType(...@@ -155,31 +194,13 @@ pub fn formatType(
155 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));194 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
156 },195 },
157 builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {196 builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
158 const has_cust_fmt = comptime cf: {197 return formatType(value.*, fmt, context, Errors, output);
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));
177 },198 },
178 else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),199 else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
179 },200 },
180 builtin.TypeInfo.Pointer.Size.Many => {201 builtin.TypeInfo.Pointer.Size.Many => {
181 if (ptr_info.child == u8) {202 if (ptr_info.child == u8) {
182 if (fmt[0] == 's') {203 if (fmt.len > 0 and fmt[0] == 's') {
183 const len = std.cstr.len(value);204 const len = std.cstr.len(value);
184 return formatText(value[0..len], fmt, context, Errors, output);205 return formatText(value[0..len], fmt, context, Errors, output);
185 }206 }
...@@ -911,14 +932,21 @@ test "fmt.format" {...@@ -911,14 +932,21 @@ test "fmt.format" {
911 try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));932 try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
912 try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));933 try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));
913 {934 {
914 // Dummy field because of https://github.com/ziglang/zig/issues/557.
915 const Struct = struct {935 const Struct = struct {
916 unused: u8,936 field: u8,
917 };937 };
918 var buf1: [32]u8 = undefined;938 const value = Struct{ .field = 42 };
919 const value = Struct{ .unused = 42 };939 try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", value);
920 const result = try bufPrint(buf1[0..], "pointer: {}\n", &value);940 try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", &value);
921 assert(mem.startsWith(u8, result, "pointer: Struct@"));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);
922 }950 }
923 {951 {
924 var buf1: [32]u8 = undefined;952 var buf1: [32]u8 = undefined;
...@@ -941,6 +969,7 @@ test "fmt.format" {...@@ -941,6 +969,7 @@ test "fmt.format" {
941 {969 {
942 // This fails on release due to a minor rounding difference.970 // This fails on release due to a minor rounding difference.
943 // --release-fast outputs 9.999960000000001e-40 vs. the expected.971 // --release-fast outputs 9.999960000000001e-40 vs. the expected.
972 // TODO fix this, it should be the same in Debug and ReleaseFast
944 if (builtin.mode == builtin.Mode.Debug) {973 if (builtin.mode == builtin.Mode.Debug) {
945 var buf1: [32]u8 = undefined;974 var buf1: [32]u8 = undefined;
946 const value: f64 = 9.999960e-40;975 const value: f64 = 9.999960e-40;
...@@ -1133,23 +1162,23 @@ test "fmt.format" {...@@ -1133,23 +1162,23 @@ test "fmt.format" {
1133 y: f32,1162 y: f32,
11341163
1135 pub fn format(1164 pub fn format(
1136 self: *SelfType,1165 self: SelfType,
1137 comptime fmt: []const u8,1166 comptime fmt: []const u8,
1138 context: var,1167 context: var,
1139 comptime Errors: type,1168 comptime Errors: type,
1140 output: fn (@typeOf(context), []const u8) Errors!void,1169 output: fn (@typeOf(context), []const u8) Errors!void,
1141 ) Errors!void {1170 ) Errors!void {
1142 if (fmt.len > 0) {1171 switch (fmt.len) {
1143 if (fmt.len > 1) unreachable;1172 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
1144 switch (fmt[0]) {1173 1 => switch (fmt[0]) {
1145 //point format1174 //point format
1146 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),1175 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
1147 //dimension format1176 //dimension format
1148 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y),1177 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y),
1149 else => unreachable,1178 else => unreachable,
1150 }1179 },
1180 else => unreachable,
1151 }1181 }
1152 return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y);
1153 }1182 }
1154 };1183 };
11551184
...@@ -1160,6 +1189,10 @@ test "fmt.format" {...@@ -1160,6 +1189,10 @@ test "fmt.format" {
1160 };1189 };
1161 try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value);1190 try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value);
1162 try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value);1191 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);
1163 }1196 }
1164}1197}
11651198
std/hash_map.zig+273-57
...@@ -9,6 +9,10 @@ const builtin = @import("builtin");...@@ -9,6 +9,10 @@ const builtin = @import("builtin");
9const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast;9const want_modification_safety = builtin.mode != builtin.Mode.ReleaseFast;
10const debug_u32 = if (want_modification_safety) u32 else void;10const 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
12pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type {16pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type {
13 return struct {17 return struct {
14 entries: []Entry,18 entries: []Entry,
...@@ -20,13 +24,22 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -20,13 +24,22 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
2024
21 const Self = this;25 const Self = this;
2226
23 pub const Entry = struct {27 pub const KV = struct {
24 used: bool,
25 distance_from_start_index: usize,
26 key: K,28 key: K,
27 value: V,29 value: V,
28 };30 };
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
30 pub const Iterator = struct {43 pub const Iterator = struct {
31 hm: *const Self,44 hm: *const Self,
32 // how many items have we returned45 // 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...@@ -36,7 +49,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
36 // used to detect concurrent modification49 // used to detect concurrent modification
37 initial_modification_count: debug_u32,50 initial_modification_count: debug_u32,
3851
39 pub fn next(it: *Iterator) ?*Entry {52 pub fn next(it: *Iterator) ?*KV {
40 if (want_modification_safety) {53 if (want_modification_safety) {
41 assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification54 assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification
42 }55 }
...@@ -46,7 +59,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -46,7 +59,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
46 if (entry.used) {59 if (entry.used) {
47 it.index += 1;60 it.index += 1;
48 it.count += 1;61 it.count += 1;
49 return entry;62 return &entry.kv;
50 }63 }
51 }64 }
52 unreachable; // no next item65 unreachable; // no next item
...@@ -71,7 +84,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -71,7 +84,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
71 };84 };
72 }85 }
7386
74 pub fn deinit(hm: *const Self) void {87 pub fn deinit(hm: Self) void {
75 hm.allocator.free(hm.entries);88 hm.allocator.free(hm.entries);
76 }89 }
7790
...@@ -84,34 +97,65 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -84,34 +97,65 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
84 hm.incrementModificationCount();97 hm.incrementModificationCount();
85 }98 }
8699
87 pub fn count(hm: *const Self) usize {100 pub fn count(self: Self) usize {
88 return hm.size;101 return self.size;
89 }102 }
90103
91 /// Returns the value that was already there.104 /// If key exists this function cannot fail.
92 pub fn put(hm: *Self, key: K, value: *const V) !?V {105 /// If there is an existing item with `key`, then the result
93 if (hm.entries.len == 0) {106 /// kv pointer points to it, and found_existing is true.
94 try hm.initCapacity(16);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);
95 }132 }
96 hm.incrementModificationCount();
97133
98 // if we get too full (60%), double the capacity134 // if we get too full (60%), double the capacity
99 if (hm.size * 5 >= hm.entries.len * 3) {135 if (self.size * 5 >= self.entries.len * 3) {
100 const old_entries = hm.entries;136 const old_entries = self.entries;
101 try hm.initCapacity(hm.entries.len * 2);137 try self.initCapacity(self.entries.len * 2);
102 // dump all of the old elements into the new table138 // dump all of the old elements into the new table
103 for (old_entries) |*old_entry| {139 for (old_entries) |*old_entry| {
104 if (old_entry.used) {140 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;
106 }142 }
107 }143 }
108 hm.allocator.free(old_entries);144 self.allocator.free(old_entries);
109 }145 }
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;
112 }156 }
113157
114 pub fn get(hm: *const Self, key: K) ?*Entry {158 pub fn get(hm: *const Self, key: K) ?*KV {
115 if (hm.entries.len == 0) {159 if (hm.entries.len == 0) {
116 return null;160 return null;
117 }161 }
...@@ -122,7 +166,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -122,7 +166,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
122 return hm.get(key) != null;166 return hm.get(key) != null;
123 }167 }
124168
125 pub fn remove(hm: *Self, key: K) ?*Entry {169 pub fn remove(hm: *Self, key: K) ?*KV {
126 if (hm.entries.len == 0) return null;170 if (hm.entries.len == 0) return null;
127 hm.incrementModificationCount();171 hm.incrementModificationCount();
128 const start_index = hm.keyToIndex(key);172 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...@@ -134,7 +178,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
134178
135 if (!entry.used) return null;179 if (!entry.used) return null;
136180
137 if (!eql(entry.key, key)) continue;181 if (!eql(entry.kv.key, key)) continue;
138182
139 while (roll_over < hm.entries.len) : (roll_over += 1) {183 while (roll_over < hm.entries.len) : (roll_over += 1) {
140 const next_index = (start_index + roll_over + 1) % hm.entries.len;184 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...@@ -142,7 +186,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
142 if (!next_entry.used or next_entry.distance_from_start_index == 0) {186 if (!next_entry.used or next_entry.distance_from_start_index == 0) {
143 entry.used = false;187 entry.used = false;
144 hm.size -= 1;188 hm.size -= 1;
145 return entry;189 return &entry.kv;
146 }190 }
147 entry.* = next_entry.*;191 entry.* = next_entry.*;
148 entry.distance_from_start_index -= 1;192 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...@@ -163,6 +207,16 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
163 };207 };
164 }208 }
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
166 fn initCapacity(hm: *Self, capacity: usize) !void {220 fn initCapacity(hm: *Self, capacity: usize) !void {
167 hm.entries = try hm.allocator.alloc(Entry, capacity);221 hm.entries = try hm.allocator.alloc(Entry, capacity);
168 hm.size = 0;222 hm.size = 0;
...@@ -178,60 +232,81 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -178,60 +232,81 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
178 }232 }
179 }233 }
180234
181 /// Returns the value that was already there.235 const InternalPutResult = struct {
182 fn internalPut(hm: *Self, orig_key: K, orig_value: *const V) ?V {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 {
183 var key = orig_key;243 var key = orig_key;
184 var value = orig_value.*;244 var value: V = undefined;
185 const start_index = hm.keyToIndex(key);245 const start_index = self.keyToIndex(key);
186 var roll_over: usize = 0;246 var roll_over: usize = 0;
187 var distance_from_start_index: usize = 0;247 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) : ({
189 roll_over += 1;254 roll_over += 1;
190 distance_from_start_index += 1;255 distance_from_start_index += 1;
191 }) {256 }) {
192 const index = (start_index + roll_over) % hm.entries.len;257 const index = (start_index + roll_over) % self.entries.len;
193 const entry = &hm.entries[index];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)) {
196 if (entry.distance_from_start_index < distance_from_start_index) {261 if (entry.distance_from_start_index < distance_from_start_index) {
197 // robin hood to the rescue262 // robin hood to the rescue
198 const tmp = entry.*;263 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 }
200 entry.* = Entry{269 entry.* = Entry{
201 .used = true,270 .used = true,
202 .distance_from_start_index = distance_from_start_index,271 .distance_from_start_index = distance_from_start_index,
203 .key = key,272 .kv = KV{
204 .value = value,273 .key = key,
274 .value = value,
275 },
205 };276 };
206 key = tmp.key;277 key = tmp.kv.key;
207 value = tmp.value;278 value = tmp.kv.value;
208 distance_from_start_index = tmp.distance_from_start_index;279 distance_from_start_index = tmp.distance_from_start_index;
209 }280 }
210 continue;281 continue;
211 }282 }
212283
213 var result: ?V = null;
214 if (entry.used) {284 if (entry.used) {
215 result = entry.value;285 result.old_kv = entry.kv;
216 } else {286 } else {
217 // adding an entry. otherwise overwriting old value with287 // adding an entry. otherwise overwriting old value with
218 // same key288 // same key
219 hm.size += 1;289 self.size += 1;
220 }290 }
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 }
223 entry.* = Entry{296 entry.* = Entry{
224 .used = true,297 .used = true,
225 .distance_from_start_index = distance_from_start_index,298 .distance_from_start_index = distance_from_start_index,
226 .key = key,299 .kv = KV{
227 .value = value,300 .key = key,
301 .value = value,
302 },
228 };303 };
229 return result;304 return result;
230 }305 }
231 unreachable; // put into a full map306 unreachable; // put into a full map
232 }307 }
233308
234 fn internalGet(hm: *const Self, key: K) ?*Entry {309 fn internalGet(hm: Self, key: K) ?*KV {
235 const start_index = hm.keyToIndex(key);310 const start_index = hm.keyToIndex(key);
236 {311 {
237 var roll_over: usize = 0;312 var roll_over: usize = 0;
...@@ -240,13 +315,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3...@@ -240,13 +315,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
240 const entry = &hm.entries[index];315 const entry = &hm.entries[index];
241316
242 if (!entry.used) return null;317 if (!entry.used) return null;
243 if (eql(entry.key, key)) return entry;318 if (eql(entry.kv.key, key)) return &entry.kv;
244 }319 }
245 }320 }
246 return null;321 return null;
247 }322 }
248323
249 fn keyToIndex(hm: *const Self, key: K) usize {324 fn keyToIndex(hm: Self, key: K) usize {
250 return usize(hash(key)) % hm.entries.len;325 return usize(hash(key)) % hm.entries.len;
251 }326 }
252 };327 };
...@@ -256,7 +331,7 @@ test "basic hash map usage" {...@@ -256,7 +331,7 @@ test "basic hash map usage" {
256 var direct_allocator = std.heap.DirectAllocator.init();331 var direct_allocator = std.heap.DirectAllocator.init();
257 defer direct_allocator.deinit();332 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);
260 defer map.deinit();335 defer map.deinit();
261336
262 assert((try map.put(1, 11)) == null);337 assert((try map.put(1, 11)) == null);
...@@ -265,8 +340,19 @@ test "basic hash map usage" {...@@ -265,8 +340,19 @@ test "basic hash map usage" {
265 assert((try map.put(4, 44)) == null);340 assert((try map.put(4, 44)) == null);
266 assert((try map.put(5, 55)) == null);341 assert((try map.put(5, 55)) == null);
267342
268 assert((try map.put(5, 66)).? == 55);343 assert((try map.put(5, 66)).?.value == 55);
269 assert((try map.put(5, 55)).? == 66);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
271 assert(map.contains(2));357 assert(map.contains(2));
272 assert(map.get(2).?.value == 22);358 assert(map.get(2).?.value == 22);
...@@ -279,7 +365,7 @@ test "iterator hash map" {...@@ -279,7 +365,7 @@ test "iterator hash map" {
279 var direct_allocator = std.heap.DirectAllocator.init();365 var direct_allocator = std.heap.DirectAllocator.init();
280 defer direct_allocator.deinit();366 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);
283 defer reset_map.deinit();369 defer reset_map.deinit();
284370
285 assert((try reset_map.put(1, 11)) == null);371 assert((try reset_map.put(1, 11)) == null);
...@@ -287,14 +373,14 @@ test "iterator hash map" {...@@ -287,14 +373,14 @@ test "iterator hash map" {
287 assert((try reset_map.put(3, 33)) == null);373 assert((try reset_map.put(3, 33)) == null);
288374
289 var keys = []i32{375 var keys = []i32{
290 1,
291 2,
292 3,376 3,
377 2,
378 1,
293 };379 };
294 var values = []i32{380 var values = []i32{
295 11,
296 22,
297 33,381 33,
382 22,
383 11,
298 };384 };
299385
300 var it = reset_map.iterator();386 var it = reset_map.iterator();
...@@ -322,10 +408,140 @@ test "iterator hash map" {...@@ -322,10 +408,140 @@ test "iterator hash map" {
322 assert(entry.value == values[0]);408 assert(entry.value == values[0]);
323}409}
324410
325fn hash_i32(x: i32) u32 {411pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) {
326 return @bitCast(u32, x);412 return struct {
413 fn hash(key: K) u32 {
414 return getAutoHashFn(usize)(@ptrToInt(key));
415 }
416 }.hash;
327}417}
328418
329fn eql_i32(a: i32, b: i32) bool {419pub fn getTrivialEqlFn(comptime K: type) (fn (K, K) bool) {
330 return a == b;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 }
331}547}
std/index.zig+5-1
...@@ -5,10 +5,11 @@ pub const BufSet = @import("buf_set.zig").BufSet;...@@ -5,10 +5,11 @@ pub const BufSet = @import("buf_set.zig").BufSet;
5pub const Buffer = @import("buffer.zig").Buffer;5pub const Buffer = @import("buffer.zig").Buffer;
6pub const BufferOutStream = @import("buffer.zig").BufferOutStream;6pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
7pub const HashMap = @import("hash_map.zig").HashMap;7pub const HashMap = @import("hash_map.zig").HashMap;
8pub const AutoHashMap = @import("hash_map.zig").AutoHashMap;
8pub const LinkedList = @import("linked_list.zig").LinkedList;9pub const LinkedList = @import("linked_list.zig").LinkedList;
9pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
10pub const SegmentedList = @import("segmented_list.zig").SegmentedList;10pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
11pub const DynLib = @import("dynamic_library.zig").DynLib;11pub const DynLib = @import("dynamic_library.zig").DynLib;
12pub const Mutex = @import("mutex.zig").Mutex;
1213
13pub const atomic = @import("atomic/index.zig");14pub const atomic = @import("atomic/index.zig");
14pub const base64 = @import("base64.zig");15pub const base64 = @import("base64.zig");
...@@ -23,6 +24,7 @@ pub const empty_import = @import("empty.zig");...@@ -23,6 +24,7 @@ pub const empty_import = @import("empty.zig");
23pub const event = @import("event.zig");24pub const event = @import("event.zig");
24pub const fmt = @import("fmt/index.zig");25pub const fmt = @import("fmt/index.zig");
25pub const hash = @import("hash/index.zig");26pub const hash = @import("hash/index.zig");
27pub const hash_map = @import("hash_map.zig");
26pub const heap = @import("heap.zig");28pub const heap = @import("heap.zig");
27pub const io = @import("io.zig");29pub const io = @import("io.zig");
28pub const json = @import("json.zig");30pub const json = @import("json.zig");
...@@ -32,6 +34,7 @@ pub const mem = @import("mem.zig");...@@ -32,6 +34,7 @@ pub const mem = @import("mem.zig");
32pub const net = @import("net.zig");34pub const net = @import("net.zig");
33pub const os = @import("os/index.zig");35pub const os = @import("os/index.zig");
34pub const rand = @import("rand/index.zig");36pub const rand = @import("rand/index.zig");
37pub const rb = @import("rb.zig");
35pub const sort = @import("sort.zig");38pub const sort = @import("sort.zig");
36pub const unicode = @import("unicode.zig");39pub const unicode = @import("unicode.zig");
37pub const zig = @import("zig/index.zig");40pub const zig = @import("zig/index.zig");
...@@ -48,6 +51,7 @@ test "std" {...@@ -48,6 +51,7 @@ test "std" {
48 _ = @import("hash_map.zig");51 _ = @import("hash_map.zig");
49 _ = @import("linked_list.zig");52 _ = @import("linked_list.zig");
50 _ = @import("segmented_list.zig");53 _ = @import("segmented_list.zig");
54 _ = @import("mutex.zig");
5155
52 _ = @import("base64.zig");56 _ = @import("base64.zig");
53 _ = @import("build.zig");57 _ = @import("build.zig");
std/io.zig+16-13
...@@ -207,6 +207,12 @@ pub fn InStream(comptime ReadError: type) type {...@@ -207,6 +207,12 @@ pub fn InStream(comptime ReadError: type) type {
207 _ = try self.readByte();207 _ = try self.readByte();
208 }208 }
209 }209 }
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 }
210 };216 };
211}217}
212218
...@@ -254,9 +260,8 @@ pub fn OutStream(comptime WriteError: type) type {...@@ -254,9 +260,8 @@ pub fn OutStream(comptime WriteError: type) type {
254 };260 };
255}261}
256262
257/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.263pub fn writeFile(path: []const u8, data: []const u8) !void {
258pub fn writeFile(allocator: *mem.Allocator, path: []const u8, data: []const u8) !void {264 var file = try File.openWrite(path);
259 var file = try File.openWrite(allocator, path);
260 defer file.close();265 defer file.close();
261 try file.write(data);266 try file.write(data);
262}267}
...@@ -268,7 +273,7 @@ pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {...@@ -268,7 +273,7 @@ pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
268273
269/// On success, caller owns returned buffer.274/// On success, caller owns returned buffer.
270pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptime A: u29) ![]align(A) u8 {275pub 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);
272 defer file.close();277 defer file.close();
273278
274 const size = try file.getEndPos();279 const size = try file.getEndPos();
...@@ -415,13 +420,12 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ...@@ -415,13 +420,12 @@ pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) typ
415 self.at_end = (read < left);420 self.at_end = (read < left);
416 return pos + read;421 return pos + read;
417 }422 }
418
419 };423 };
420}424}
421425
422pub const SliceInStream = struct {426pub const SliceInStream = struct {
423 const Self = this;427 const Self = this;
424 pub const Error = error { };428 pub const Error = error{};
425 pub const Stream = InStream(Error);429 pub const Stream = InStream(Error);
426430
427 pub stream: Stream,431 pub stream: Stream,
...@@ -481,13 +485,12 @@ pub const SliceOutStream = struct {...@@ -481,13 +485,12 @@ pub const SliceOutStream = struct {
481485
482 assert(self.pos <= self.slice.len);486 assert(self.pos <= self.slice.len);
483487
484 const n =488 const n = if (self.pos + bytes.len <= self.slice.len)
485 if (self.pos + bytes.len <= self.slice.len)489 bytes.len
486 bytes.len490 else
487 else491 self.slice.len - self.pos;
488 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]);
491 self.pos += n;494 self.pos += n;
492495
493 if (n < bytes.len) {496 if (n < bytes.len) {
...@@ -586,7 +589,7 @@ pub const BufferedAtomicFile = struct {...@@ -586,7 +589,7 @@ pub const BufferedAtomicFile = struct {
586 });589 });
587 errdefer allocator.destroy(self);590 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);
590 errdefer self.atomic_file.deinit();593 errdefer self.atomic_file.deinit();
591594
592 self.file_stream = FileOutStream.init(&self.atomic_file.file);595 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" {...@@ -16,7 +16,7 @@ test "write a file, read it, then delete it" {
16 prng.random.bytes(data[0..]);16 prng.random.bytes(data[0..]);
17 const tmp_file_name = "temp_test_file.txt";17 const tmp_file_name = "temp_test_file.txt";
18 {18 {
19 var file = try os.File.openWrite(allocator, tmp_file_name);19 var file = try os.File.openWrite(tmp_file_name);
20 defer file.close();20 defer file.close();
2121
22 var file_out_stream = io.FileOutStream.init(&file);22 var file_out_stream = io.FileOutStream.init(&file);
...@@ -28,7 +28,7 @@ test "write a file, read it, then delete it" {...@@ -28,7 +28,7 @@ test "write a file, read it, then delete it" {
28 try buf_stream.flush();28 try buf_stream.flush();
29 }29 }
30 {30 {
31 var file = try os.File.openRead(allocator, tmp_file_name);31 var file = try os.File.openRead(tmp_file_name);
32 defer file.close();32 defer file.close();
3333
34 const file_size = try file.getEndPos();34 const file_size = try file.getEndPos();
...@@ -45,7 +45,7 @@ test "write a file, read it, then delete it" {...@@ -45,7 +45,7 @@ test "write a file, read it, then delete it" {
45 assert(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data));45 assert(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data));
46 assert(mem.eql(u8, contents[contents.len - "end".len ..], "end"));46 assert(mem.eql(u8, contents[contents.len - "end".len ..], "end"));
47 }47 }
48 try os.deleteFile(allocator, tmp_file_name);48 try os.deleteFile(tmp_file_name);
49}49}
5050
51test "BufferOutStream" {51test "BufferOutStream" {
...@@ -63,7 +63,7 @@ test "BufferOutStream" {...@@ -63,7 +63,7 @@ test "BufferOutStream" {
63}63}
6464
65test "SliceInStream" {65test "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 };
67 var ss = io.SliceInStream.init(bytes);67 var ss = io.SliceInStream.init(bytes);
6868
69 var dest: [4]u8 = undefined;69 var dest: [4]u8 = undefined;
...@@ -81,7 +81,7 @@ test "SliceInStream" {...@@ -81,7 +81,7 @@ test "SliceInStream" {
81}81}
8282
83test "PeekStream" {83test "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 };
85 var ss = io.SliceInStream.init(bytes);85 var ss = io.SliceInStream.init(bytes);
86 var ps = io.PeekStream(2, io.SliceInStream.Error).init(&ss.stream);86 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 {...@@ -1318,7 +1318,7 @@ pub const Parser = struct {
1318 _ = p.stack.pop();1318 _ = p.stack.pop();
13191319
1320 var object = &p.stack.items[p.stack.len - 1].Object;1320 var object = &p.stack.items[p.stack.len - 1].Object;
1321 _ = try object.put(key, value);1321 _ = try object.put(key, value.*);
1322 p.state = State.ObjectKey;1322 p.state = State.ObjectKey;
1323 },1323 },
1324 // Array Parent -> [ ..., <array>, value ]1324 // Array Parent -> [ ..., <array>, value ]
std/linked_list.zig+4-97
...@@ -4,18 +4,8 @@ const assert = debug.assert;...@@ -4,18 +4,8 @@ const assert = debug.assert;
4const mem = std.mem;4const mem = std.mem;
5const Allocator = mem.Allocator;5const 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
17/// Generic doubly linked list.7/// 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 {
19 return struct {9 return struct {
20 const Self = this;10 const Self = this;
2111
...@@ -25,23 +15,13 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -25,23 +15,13 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
25 next: ?*Node,15 next: ?*Node,
26 data: T,16 data: T,
2717
28 pub fn init(value: *const T) Node {18 pub fn init(data: T) Node {
29 return Node{19 return Node{
30 .prev = null,20 .prev = null,
31 .next = null,21 .next = null,
32 .data = value.*,22 .data = data,
33 };23 };
34 }24 }
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 }
45 };25 };
4626
47 first: ?*Node,27 first: ?*Node,
...@@ -60,10 +40,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -60,10 +40,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
60 };40 };
61 }41 }
6242
63 fn isIntrusive() bool {
64 return ParentType != void or field_name.len != 0;
65 }
66
67 /// Insert a new node after an existing one.43 /// Insert a new node after an existing one.
68 ///44 ///
69 /// Arguments:45 /// Arguments:
...@@ -192,7 +168,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -192,7 +168,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
192 /// Returns:168 /// Returns:
193 /// A pointer to the new node.169 /// A pointer to the new node.
194 pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {170 pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {
195 comptime assert(!isIntrusive());
196 return allocator.create(Node(undefined));171 return allocator.create(Node(undefined));
197 }172 }
198173
...@@ -202,7 +177,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -202,7 +177,6 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
202 /// node: Pointer to the node to deallocate.177 /// node: Pointer to the node to deallocate.
203 /// allocator: Dynamic memory allocator.178 /// allocator: Dynamic memory allocator.
204 pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {179 pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {
205 comptime assert(!isIntrusive());
206 allocator.destroy(node);180 allocator.destroy(node);
207 }181 }
208182
...@@ -214,8 +188,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -214,8 +188,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
214 ///188 ///
215 /// Returns:189 /// Returns:
216 /// A pointer to the new node.190 /// A pointer to the new node.
217 pub fn createNode(list: *Self, data: *const T, allocator: *Allocator) !*Node {191 pub fn createNode(list: *Self, data: T, allocator: *Allocator) !*Node {
218 comptime assert(!isIntrusive());
219 var node = try list.allocateNode(allocator);192 var node = try list.allocateNode(allocator);
220 node.* = Node.init(data);193 node.* = Node.init(data);
221 return node;194 return node;
...@@ -274,69 +247,3 @@ test "basic linked list test" {...@@ -274,69 +247,3 @@ test "basic linked list test" {
274 assert(list.last.?.data == 4);247 assert(list.last.?.data == 4);
275 assert(list.len == 2);248 assert(list.len == 2);
276}249}
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 @@...@@ -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;2pub const mach_header = extern struct {
7const MH_PIE = 0x200000;3 magic: u32,
8const LC_SYMTAB = 2;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 {
11 magic: u32,13 magic: u32,
12 cputype: u32,14 cputype: cpu_type_t,
13 cpusubtype: u32,15 cpusubtype: cpu_subtype_t,
14 filetype: u32,16 filetype: u32,
15 ncmds: u32,17 ncmds: u32,
16 sizeofcmds: u32,18 sizeofcmds: u32,
...@@ -18,19 +20,138 @@ const MachHeader64 = packed struct {...@@ -18,19 +20,138 @@ const MachHeader64 = packed struct {
18 reserved: u32,20 reserved: u32,
19};21};
2022
21const LoadCommand = packed struct {23pub const load_command = extern struct {
22 cmd: u32,24 cmd: u32,
23 cmdsize: u32,25 cmdsize: u32,
24};26};
2527
26const SymtabCommand = packed struct {28
27 symoff: u32,29/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
28 nsyms: u32,30/// "stab" style symbol table information as described in the header files
29 stroff: u32,31/// <nlist.h> and <stab.h>.
30 strsize: u32,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,
31};152};
32153
33const Nlist64 = packed struct {154pub const nlist_64 = extern struct {
34 n_strx: u32,155 n_strx: u32,
35 n_type: u8,156 n_type: u8,
36 n_sect: u8,157 n_sect: u8,
...@@ -38,135 +159,190 @@ const Nlist64 = packed struct {...@@ -38,135 +159,190 @@ const Nlist64 = packed struct {
38 n_value: u64,159 n_value: u64,
39};160};
40161
41pub const Symbol = struct {162/// After MacOS X 10.1 when a new load command is added that is required to be
42 name: []const u8,163/// understood by the dynamic linker for the image to execute properly the
43 address: u64,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 {171pub const LC_SEGMENT = 0x1; /// segment of this file to be mapped
46 return lhs.address < rhs.address;172pub const LC_SYMTAB = 0x2; /// link-edit stab symbol table info
47 }173pub const LC_SYMSEG = 0x3; /// link-edit gdb symbol table info (obsolete)
48};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 {195/// load a dynamically linked shared library that is allowed to be missing
51 allocator: *mem.Allocator,196/// (all symbols are weak imported).
52 symbols: []const Symbol,197pub const LC_LOAD_WEAK_DYLIB = (0x18 | LC_REQ_DYLD);
53 strings: []const u8,198
54199pub const LC_SEGMENT_64 = 0x19; /// 64-bit segment of this file to be mapped
55 // Doubles as an eyecatcher to calculate the PIE slide, see loadSymbols().200pub const LC_ROUTINES_64 = 0x1a; /// 64-bit image routines
56 // Ideally we'd use _mh_execute_header because it's always at 0x100000000201pub const LC_UUID = 0x1b; /// the uuid
57 // in the image but as it's located in a different section than executable202pub const LC_RPATH = (0x1c | LC_REQ_DYLD); /// runpath additions
58 // code, its displacement is different.203pub const LC_CODE_SIGNATURE = 0x1d; /// local of code signature
59 pub fn deinit(self: *SymbolTable) void {204pub const LC_SEGMENT_SPLIT_INFO = 0x1e; /// local of info to split segments
60 self.allocator.free(self.symbols);205pub const LC_REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD); /// load and re-export dylib
61 self.symbols = []const Symbol{};206pub const LC_LAZY_LOAD_DYLIB = 0x20; /// delay load of dylib until first use
62207pub const LC_ENCRYPTION_INFO = 0x21; /// encrypted segment information
63 self.allocator.free(self.strings);208pub const LC_DYLD_INFO = 0x22; /// compressed dyld information
64 self.strings = []const u8{};209pub const LC_DYLD_INFO_ONLY = (0x22|LC_REQ_DYLD); /// compressed dyld information only
65 }210pub const LC_LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD); /// load upward dylib
66211pub const LC_VERSION_MIN_MACOSX = 0x24; /// build for MacOSX min OS version
67 pub fn search(self: *const SymbolTable, address: usize) ?*const Symbol {212pub const LC_VERSION_MIN_IPHONEOS = 0x25; /// build for iPhoneOS min OS version
68 var min: usize = 0;213pub const LC_FUNCTION_STARTS = 0x26; /// compressed table of function start addresses
69 var max: usize = self.symbols.len - 1; // Exclude sentinel.214pub const LC_DYLD_ENVIRONMENT = 0x27; /// string for dyld to treat like environment variable
70 while (min < max) {215pub const LC_MAIN = (0x28|LC_REQ_DYLD); /// replacement for LC_UNIXTHREAD
71 const mid = min + (max - min) / 2;216pub const LC_DATA_IN_CODE = 0x29; /// table of non-instructions in __text
72 const curr = &self.symbols[mid];217pub const LC_SOURCE_VERSION = 0x2A; /// source version used to build binary
73 const next = &self.symbols[mid + 1];218pub const LC_DYLIB_CODE_SIGN_DRS = 0x2B; /// Code signing DRs copied from linked dylibs
74 if (address >= next.address) {219pub const LC_ENCRYPTION_INFO_64 = 0x2C; /// 64-bit encrypted segment information
75 min = mid + 1;220pub const LC_LINKER_OPTION = 0x2D; /// linker options in MH_OBJECT files
76 } else if (address < curr.address) {221pub const LC_LINKER_OPTIMIZATION_HINT = 0x2E; /// optimization hints in MH_OBJECT files
77 max = mid;222pub const LC_VERSION_MIN_TVOS = 0x2F; /// build for AppleTV min OS version
78 } else {223pub const LC_VERSION_MIN_WATCHOS = 0x30; /// build for Watch min OS version
79 return curr;224pub const LC_NOTE = 0x31; /// arbitrary data included within a Mach-O file
80 }225pub const LC_BUILD_VERSION = 0x32; /// build for platform min OS version
81 }226
82 return null;227pub const MH_MAGIC = 0xfeedface; /// the mach magic number
83 }228pub const MH_CIGAM = 0xcefaedfe; /// NXSwapInt(MH_MAGIC)
84};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 {...@@ -61,10 +61,8 @@ fn ceil64(x: f64) f64 {
61 }61 }
6262
63 if (u >> 63 != 0) {63 if (u >> 63 != 0) {
64 @setFloatMode(this, builtin.FloatMode.Strict);
65 y = x - math.f64_toint + math.f64_toint - x;64 y = x - math.f64_toint + math.f64_toint - x;
66 } else {65 } else {
67 @setFloatMode(this, builtin.FloatMode.Strict);
68 y = x + math.f64_toint - math.f64_toint - x;66 y = x + math.f64_toint - math.f64_toint - x;
69 }67 }
7068
std/math/complex/exp.zig-2
...@@ -17,8 +17,6 @@ pub fn exp(z: var) @typeOf(z) {...@@ -17,8 +17,6 @@ pub fn exp(z: var) @typeOf(z) {
17}17}
1818
19fn exp32(z: Complex(f32)) Complex(f32) {19fn exp32(z: Complex(f32)) Complex(f32) {
20 @setFloatMode(this, @import("builtin").FloatMode.Strict);
21
22 const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.7228395520 const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955
23 const cexp_overflow = 0x43400074; // (max_exp - min_denom_exp) * ln221 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;...@@ -37,8 +37,6 @@ const C5 = 4.16666666666665929218E-2;
37//37//
38// This may have slight differences on some edge cases and may need to replaced if so.38// This may have slight differences on some edge cases and may need to replaced if so.
39fn cos32(x_: f32) f32 {39fn cos32(x_: f32) f32 {
40 @setFloatMode(this, @import("builtin").FloatMode.Strict);
41
42 const pi4a = 7.85398125648498535156e-1;40 const pi4a = 7.85398125648498535156e-1;
43 const pi4b = 3.77489470793079817668E-8;41 const pi4b = 3.77489470793079817668E-8;
44 const pi4c = 2.69515142907905952645E-15;42 const pi4c = 2.69515142907905952645E-15;
std/math/exp.zig-4
...@@ -18,8 +18,6 @@ pub fn exp(x: var) @typeOf(x) {...@@ -18,8 +18,6 @@ pub fn exp(x: var) @typeOf(x) {
18}18}
1919
20fn exp32(x_: f32) f32 {20fn exp32(x_: f32) f32 {
21 @setFloatMode(this, builtin.FloatMode.Strict);
22
23 const half = []f32{ 0.5, -0.5 };21 const half = []f32{ 0.5, -0.5 };
24 const ln2hi = 6.9314575195e-1;22 const ln2hi = 6.9314575195e-1;
25 const ln2lo = 1.4286067653e-6;23 const ln2lo = 1.4286067653e-6;
...@@ -95,8 +93,6 @@ fn exp32(x_: f32) f32 {...@@ -95,8 +93,6 @@ fn exp32(x_: f32) f32 {
95}93}
9694
97fn exp64(x_: f64) f64 {95fn exp64(x_: f64) f64 {
98 @setFloatMode(this, builtin.FloatMode.Strict);
99
100 const half = []const f64{ 0.5, -0.5 };96 const half = []const f64{ 0.5, -0.5 };
101 const ln2hi: f64 = 6.93147180369123816490e-01;97 const ln2hi: f64 = 6.93147180369123816490e-01;
102 const ln2lo: f64 = 1.90821492927058770002e-10;98 const ln2lo: f64 = 1.90821492927058770002e-10;
std/math/exp2.zig-4
...@@ -36,8 +36,6 @@ const exp2ft = []const f64{...@@ -36,8 +36,6 @@ const exp2ft = []const f64{
36};36};
3737
38fn exp2_32(x: f32) f32 {38fn exp2_32(x: f32) f32 {
39 @setFloatMode(this, @import("builtin").FloatMode.Strict);
40
41 const tblsiz = @intCast(u32, exp2ft.len);39 const tblsiz = @intCast(u32, exp2ft.len);
42 const redux: f32 = 0x1.8p23 / @intToFloat(f32, tblsiz);40 const redux: f32 = 0x1.8p23 / @intToFloat(f32, tblsiz);
43 const P1: f32 = 0x1.62e430p-1;41 const P1: f32 = 0x1.62e430p-1;
...@@ -353,8 +351,6 @@ const exp2dt = []f64{...@@ -353,8 +351,6 @@ const exp2dt = []f64{
353};351};
354352
355fn exp2_64(x: f64) f64 {353fn exp2_64(x: f64) f64 {
356 @setFloatMode(this, @import("builtin").FloatMode.Strict);
357
358 const tblsiz = @intCast(u32, exp2dt.len / 2);354 const tblsiz = @intCast(u32, exp2dt.len / 2);
359 const redux: f64 = 0x1.8p52 / @intToFloat(f64, tblsiz);355 const redux: f64 = 0x1.8p52 / @intToFloat(f64, tblsiz);
360 const P1: f64 = 0x1.62e42fefa39efp-1;356 const P1: f64 = 0x1.62e42fefa39efp-1;
std/math/expm1.zig-4
...@@ -19,8 +19,6 @@ pub fn expm1(x: var) @typeOf(x) {...@@ -19,8 +19,6 @@ pub fn expm1(x: var) @typeOf(x) {
19}19}
2020
21fn expm1_32(x_: f32) f32 {21fn expm1_32(x_: f32) f32 {
22 @setFloatMode(this, builtin.FloatMode.Strict);
23
24 if (math.isNan(x_))22 if (math.isNan(x_))
25 return math.nan(f32);23 return math.nan(f32);
2624
...@@ -149,8 +147,6 @@ fn expm1_32(x_: f32) f32 {...@@ -149,8 +147,6 @@ fn expm1_32(x_: f32) f32 {
149}147}
150148
151fn expm1_64(x_: f64) f64 {149fn expm1_64(x_: f64) f64 {
152 @setFloatMode(this, builtin.FloatMode.Strict);
153
154 if (math.isNan(x_))150 if (math.isNan(x_))
155 return math.nan(f64);151 return math.nan(f64);
156152
std/math/floor.zig-2
...@@ -97,10 +97,8 @@ fn floor64(x: f64) f64 {...@@ -97,10 +97,8 @@ fn floor64(x: f64) f64 {
97 }97 }
9898
99 if (u >> 63 != 0) {99 if (u >> 63 != 0) {
100 @setFloatMode(this, builtin.FloatMode.Strict);
101 y = x - math.f64_toint + math.f64_toint - x;100 y = x - math.f64_toint + math.f64_toint - x;
102 } else {101 } else {
103 @setFloatMode(this, builtin.FloatMode.Strict);
104 y = x + math.f64_toint - math.f64_toint - x;102 y = x + math.f64_toint - math.f64_toint - x;
105 }103 }
106104
std/math/ln.zig-4
...@@ -35,8 +35,6 @@ pub fn ln(x: var) @typeOf(x) {...@@ -35,8 +35,6 @@ pub fn ln(x: var) @typeOf(x) {
35}35}
3636
37pub fn ln_32(x_: f32) f32 {37pub fn ln_32(x_: f32) f32 {
38 @setFloatMode(this, @import("builtin").FloatMode.Strict);
39
40 const ln2_hi: f32 = 6.9313812256e-01;38 const ln2_hi: f32 = 6.9313812256e-01;
41 const ln2_lo: f32 = 9.0580006145e-06;39 const ln2_lo: f32 = 9.0580006145e-06;
42 const Lg1: f32 = 0xaaaaaa.0p-24;40 const Lg1: f32 = 0xaaaaaa.0p-24;
...@@ -89,8 +87,6 @@ pub fn ln_32(x_: f32) f32 {...@@ -89,8 +87,6 @@ pub fn ln_32(x_: f32) f32 {
89}87}
9088
91pub fn ln_64(x_: f64) f64 {89pub fn ln_64(x_: f64) f64 {
92 @setFloatMode(this, @import("builtin").FloatMode.Strict);
93
94 const ln2_hi: f64 = 6.93147180369123816490e-01;90 const ln2_hi: f64 = 6.93147180369123816490e-01;
95 const ln2_lo: f64 = 1.90821492927058770002e-10;91 const ln2_lo: f64 = 1.90821492927058770002e-10;
96 const Lg1: f64 = 6.666666666666735130e-01;92 const Lg1: f64 = 6.666666666666735130e-01;
std/math/pow.zig-2
...@@ -28,8 +28,6 @@ const assert = std.debug.assert;...@@ -28,8 +28,6 @@ const assert = std.debug.assert;
2828
29// This implementation is taken from the go stlib, musl is a bit more complex.29// This implementation is taken from the go stlib, musl is a bit more complex.
30pub fn pow(comptime T: type, x: T, y: T) T {30pub fn pow(comptime T: type, x: T, y: T) T {
31 @setFloatMode(this, @import("builtin").FloatMode.Strict);
32
33 if (T != f32 and T != f64) {31 if (T != f32 and T != f64) {
34 @compileError("pow not implemented for " ++ @typeName(T));32 @compileError("pow not implemented for " ++ @typeName(T));
35 }33 }
std/math/round.zig+2-10
...@@ -35,11 +35,7 @@ fn round32(x_: f32) f32 {...@@ -35,11 +35,7 @@ fn round32(x_: f32) f32 {
35 return 0 * @bitCast(f32, u);35 return 0 * @bitCast(f32, u);
36 }36 }
3737
38 {38 y = x + math.f32_toint - math.f32_toint - x;
39 @setFloatMode(this, builtin.FloatMode.Strict);
40 y = x + math.f32_toint - math.f32_toint - x;
41 }
42
43 if (y > 0.5) {39 if (y > 0.5) {
44 y = y + x - 1;40 y = y + x - 1;
45 } else if (y <= -0.5) {41 } else if (y <= -0.5) {
...@@ -72,11 +68,7 @@ fn round64(x_: f64) f64 {...@@ -72,11 +68,7 @@ fn round64(x_: f64) f64 {
72 return 0 * @bitCast(f64, u);68 return 0 * @bitCast(f64, u);
73 }69 }
7470
75 {71 y = x + math.f64_toint - math.f64_toint - x;
76 @setFloatMode(this, builtin.FloatMode.Strict);
77 y = x + math.f64_toint - math.f64_toint - x;
78 }
79
80 if (y > 0.5) {72 if (y > 0.5) {
81 y = y + x - 1;73 y = y + x - 1;
82 } else if (y <= -0.5) {74 } else if (y <= -0.5) {
std/math/sin.zig-2
...@@ -38,8 +38,6 @@ const C5 = 4.16666666666665929218E-2;...@@ -38,8 +38,6 @@ const C5 = 4.16666666666665929218E-2;
38//38//
39// This may have slight differences on some edge cases and may need to replaced if so.39// This may have slight differences on some edge cases and may need to replaced if so.
40fn sin32(x_: f32) f32 {40fn sin32(x_: f32) f32 {
41 @setFloatMode(this, @import("builtin").FloatMode.Strict);
42
43 const pi4a = 7.85398125648498535156e-1;41 const pi4a = 7.85398125648498535156e-1;
44 const pi4b = 3.77489470793079817668E-8;42 const pi4b = 3.77489470793079817668E-8;
45 const pi4c = 2.69515142907905952645E-15;43 const pi4c = 2.69515142907905952645E-15;
std/math/sinh.zig-2
...@@ -54,8 +54,6 @@ fn sinh32(x: f32) f32 {...@@ -54,8 +54,6 @@ fn sinh32(x: f32) f32 {
54}54}
5555
56fn sinh64(x: f64) f64 {56fn sinh64(x: f64) f64 {
57 @setFloatMode(this, @import("builtin").FloatMode.Strict);
58
59 const u = @bitCast(u64, x);57 const u = @bitCast(u64, x);
60 const w = @intCast(u32, u >> 32);58 const w = @intCast(u32, u >> 32);
61 const ax = @bitCast(f64, u & (@maxValue(u64) >> 1));59 const ax = @bitCast(f64, u & (@maxValue(u64) >> 1));
std/math/tan.zig-2
...@@ -31,8 +31,6 @@ const Tq4 = -5.38695755929454629881E7;...@@ -31,8 +31,6 @@ const Tq4 = -5.38695755929454629881E7;
31//31//
32// This may have slight differences on some edge cases and may need to replaced if so.32// This may have slight differences on some edge cases and may need to replaced if so.
33fn tan32(x_: f32) f32 {33fn tan32(x_: f32) f32 {
34 @setFloatMode(this, @import("builtin").FloatMode.Strict);
35
36 const pi4a = 7.85398125648498535156e-1;34 const pi4a = 7.85398125648498535156e-1;
37 const pi4b = 3.77489470793079817668E-8;35 const pi4b = 3.77489470793079817668E-8;
38 const pi4c = 2.69515142907905952645E-15;36 const pi4c = 2.69515142907905952645E-15;
std/mem.zig+116-7
...@@ -135,6 +135,12 @@ pub const Allocator = struct {...@@ -135,6 +135,12 @@ pub const Allocator = struct {
135 }135 }
136};136};
137137
138pub const Compare = enum {
139 LessThan,
140 Equal,
141 GreaterThan,
142};
143
138/// Copy all of source into dest at position 0.144/// Copy all of source into dest at position 0.
139/// dest.len must be >= source.len.145/// dest.len must be >= source.len.
140/// dest.ptr must be <= src.ptr.146/// dest.ptr must be <= src.ptr.
...@@ -169,16 +175,64 @@ pub fn set(comptime T: type, dest: []T, value: T) void {...@@ -169,16 +175,64 @@ pub fn set(comptime T: type, dest: []T, value: T) void {
169 d.* = value;175 d.* = value;
170}176}
171177
172/// Returns true if lhs < rhs, false otherwise178pub fn secureZero(comptime T: type, s: []T) void {
173pub fn lessThan(comptime T: type, lhs: []const T, rhs: []const T) bool {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 {
174 const n = math.min(lhs.len, rhs.len);197 const n = math.min(lhs.len, rhs.len);
175 var i: usize = 0;198 var i: usize = 0;
176 while (i < n) : (i += 1) {199 while (i < n) : (i += 1) {
177 if (lhs[i] == rhs[i]) continue;200 if (lhs[i] == rhs[i]) {
178 return 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 }
179 }209 }
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;
182}236}
183237
184test "mem.lessThan" {238test "mem.lessThan" {
...@@ -198,6 +252,20 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {...@@ -198,6 +252,20 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
198 return true;252 return true;
199}253}
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
201/// Returns true if all elements in a slice are equal to the scalar value provided269/// Returns true if all elements in a slice are equal to the scalar value provided
202pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool {270pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool {
203 for (slice) |item| {271 for (slice) |item| {
...@@ -541,7 +609,7 @@ pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {...@@ -541,7 +609,7 @@ pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {
541 }609 }
542 }610 }
543611
544 return buf[0..buf_index];612 return allocator.shrink(u8, buf, buf_index);
545}613}
546614
547test "mem.join" {615test "mem.join" {
...@@ -611,10 +679,38 @@ test "testWriteInt" {...@@ -611,10 +679,38 @@ test "testWriteInt" {
611 comptime testWriteIntImpl();679 comptime testWriteIntImpl();
612}680}
613fn testWriteIntImpl() void {681fn 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
616 writeInt(bytes[0..], u32(0x12345678), builtin.Endian.Big);708 writeInt(bytes[0..], u32(0x12345678), builtin.Endian.Big);
617 assert(eql(u8, bytes, []u8{709 assert(eql(u8, bytes, []u8{
710 0x00,
711 0x00,
712 0x00,
713 0x00,
618 0x12,714 0x12,
619 0x34,715 0x34,
620 0x56,716 0x56,
...@@ -627,10 +723,18 @@ fn testWriteIntImpl() void {...@@ -627,10 +723,18 @@ fn testWriteIntImpl() void {
627 0x34,723 0x34,
628 0x56,724 0x56,
629 0x78,725 0x78,
726 0x00,
727 0x00,
728 0x00,
729 0x00,
630 }));730 }));
631731
632 writeInt(bytes[0..], u16(0x1234), builtin.Endian.Big);732 writeInt(bytes[0..], u16(0x1234), builtin.Endian.Big);
633 assert(eql(u8, bytes, []u8{733 assert(eql(u8, bytes, []u8{
734 0x00,
735 0x00,
736 0x00,
737 0x00,
634 0x00,738 0x00,
635 0x00,739 0x00,
636 0x12,740 0x12,
...@@ -643,6 +747,10 @@ fn testWriteIntImpl() void {...@@ -643,6 +747,10 @@ fn testWriteIntImpl() void {
643 0x12,747 0x12,
644 0x00,748 0x00,
645 0x00,749 0x00,
750 0x00,
751 0x00,
752 0x00,
753 0x00,
646 }));754 }));
647}755}
648756
...@@ -755,3 +863,4 @@ pub fn endianSwap(comptime T: type, x: T) T {...@@ -755,3 +863,4 @@ pub fn endianSwap(comptime T: type, x: T) T {
755test "std.mem.endianSwap" {863test "std.mem.endianSwap" {
756 assert(endianSwap(u32, 0xDEADBEEF) == 0xEFBEADDE);864 assert(endianSwap(u32, 0xDEADBEEF) == 0xEFBEADDE);
757}865}
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 {...@@ -349,14 +349,7 @@ pub const ChildProcess = struct {
349 };349 };
350350
351 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);351 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: {352 const dev_null_fd = if (any_ignore) try os.posixOpenC(c"/dev/null", posix.O_RDWR, 0) else undefined;
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 };
360 defer {353 defer {
361 if (any_ignore) os.close(dev_null_fd);354 if (any_ignore) os.close(dev_null_fd);
362 }355 }
...@@ -453,10 +446,7 @@ pub const ChildProcess = struct {...@@ -453,10 +446,7 @@ pub const ChildProcess = struct {
453 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);446 const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
454447
455 const nul_handle = if (any_ignore) blk: {448 const nul_handle = if (any_ignore) blk: {
456 const nul_file_path = "NUL";449 break :blk try os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL);
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);
460 } else blk: {450 } else blk: {
461 break :blk undefined;451 break :blk undefined;
462 };452 };
std/os/darwin.zig+124-85
...@@ -482,91 +482,98 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;...@@ -482,91 +482,98 @@ pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;
482/// data is mach absolute time units482/// data is mach absolute time units
483pub const NOTE_MACHTIME = 0x00000100;483pub const NOTE_MACHTIME = 0x00000100;
484484
485pub const AF_UNSPEC: c_int = 0;485pub const AF_UNSPEC = 0;
486pub const AF_LOCAL: c_int = 1;486pub const AF_LOCAL = 1;
487pub const AF_UNIX: c_int = AF_LOCAL;487pub const AF_UNIX = AF_LOCAL;
488pub const AF_INET: c_int = 2;488pub const AF_INET = 2;
489pub const AF_SYS_CONTROL: c_int = 2;489pub const AF_SYS_CONTROL = 2;
490pub const AF_IMPLINK: c_int = 3;490pub const AF_IMPLINK = 3;
491pub const AF_PUP: c_int = 4;491pub const AF_PUP = 4;
492pub const AF_CHAOS: c_int = 5;492pub const AF_CHAOS = 5;
493pub const AF_NS: c_int = 6;493pub const AF_NS = 6;
494pub const AF_ISO: c_int = 7;494pub const AF_ISO = 7;
495pub const AF_OSI: c_int = AF_ISO;495pub const AF_OSI = AF_ISO;
496pub const AF_ECMA: c_int = 8;496pub const AF_ECMA = 8;
497pub const AF_DATAKIT: c_int = 9;497pub const AF_DATAKIT = 9;
498pub const AF_CCITT: c_int = 10;498pub const AF_CCITT = 10;
499pub const AF_SNA: c_int = 11;499pub const AF_SNA = 11;
500pub const AF_DECnet: c_int = 12;500pub const AF_DECnet = 12;
501pub const AF_DLI: c_int = 13;501pub const AF_DLI = 13;
502pub const AF_LAT: c_int = 14;502pub const AF_LAT = 14;
503pub const AF_HYLINK: c_int = 15;503pub const AF_HYLINK = 15;
504pub const AF_APPLETALK: c_int = 16;504pub const AF_APPLETALK = 16;
505pub const AF_ROUTE: c_int = 17;505pub const AF_ROUTE = 17;
506pub const AF_LINK: c_int = 18;506pub const AF_LINK = 18;
507pub const AF_XTP: c_int = 19;507pub const AF_XTP = 19;
508pub const AF_COIP: c_int = 20;508pub const AF_COIP = 20;
509pub const AF_CNT: c_int = 21;509pub const AF_CNT = 21;
510pub const AF_RTIP: c_int = 22;510pub const AF_RTIP = 22;
511pub const AF_IPX: c_int = 23;511pub const AF_IPX = 23;
512pub const AF_SIP: c_int = 24;512pub const AF_SIP = 24;
513pub const AF_PIP: c_int = 25;513pub const AF_PIP = 25;
514pub const AF_ISDN: c_int = 28;514pub const AF_ISDN = 28;
515pub const AF_E164: c_int = AF_ISDN;515pub const AF_E164 = AF_ISDN;
516pub const AF_KEY: c_int = 29;516pub const AF_KEY = 29;
517pub const AF_INET6: c_int = 30;517pub const AF_INET6 = 30;
518pub const AF_NATM: c_int = 31;518pub const AF_NATM = 31;
519pub const AF_SYSTEM: c_int = 32;519pub const AF_SYSTEM = 32;
520pub const AF_NETBIOS: c_int = 33;520pub const AF_NETBIOS = 33;
521pub const AF_PPP: c_int = 34;521pub const AF_PPP = 34;
522pub const AF_MAX: c_int = 40;522pub const AF_MAX = 40;
523523
524pub const PF_UNSPEC: c_int = AF_UNSPEC;524pub const PF_UNSPEC = AF_UNSPEC;
525pub const PF_LOCAL: c_int = AF_LOCAL;525pub const PF_LOCAL = AF_LOCAL;
526pub const PF_UNIX: c_int = PF_LOCAL;526pub const PF_UNIX = PF_LOCAL;
527pub const PF_INET: c_int = AF_INET;527pub const PF_INET = AF_INET;
528pub const PF_IMPLINK: c_int = AF_IMPLINK;528pub const PF_IMPLINK = AF_IMPLINK;
529pub const PF_PUP: c_int = AF_PUP;529pub const PF_PUP = AF_PUP;
530pub const PF_CHAOS: c_int = AF_CHAOS;530pub const PF_CHAOS = AF_CHAOS;
531pub const PF_NS: c_int = AF_NS;531pub const PF_NS = AF_NS;
532pub const PF_ISO: c_int = AF_ISO;532pub const PF_ISO = AF_ISO;
533pub const PF_OSI: c_int = AF_ISO;533pub const PF_OSI = AF_ISO;
534pub const PF_ECMA: c_int = AF_ECMA;534pub const PF_ECMA = AF_ECMA;
535pub const PF_DATAKIT: c_int = AF_DATAKIT;535pub const PF_DATAKIT = AF_DATAKIT;
536pub const PF_CCITT: c_int = AF_CCITT;536pub const PF_CCITT = AF_CCITT;
537pub const PF_SNA: c_int = AF_SNA;537pub const PF_SNA = AF_SNA;
538pub const PF_DECnet: c_int = AF_DECnet;538pub const PF_DECnet = AF_DECnet;
539pub const PF_DLI: c_int = AF_DLI;539pub const PF_DLI = AF_DLI;
540pub const PF_LAT: c_int = AF_LAT;540pub const PF_LAT = AF_LAT;
541pub const PF_HYLINK: c_int = AF_HYLINK;541pub const PF_HYLINK = AF_HYLINK;
542pub const PF_APPLETALK: c_int = AF_APPLETALK;542pub const PF_APPLETALK = AF_APPLETALK;
543pub const PF_ROUTE: c_int = AF_ROUTE;543pub const PF_ROUTE = AF_ROUTE;
544pub const PF_LINK: c_int = AF_LINK;544pub const PF_LINK = AF_LINK;
545pub const PF_XTP: c_int = AF_XTP;545pub const PF_XTP = AF_XTP;
546pub const PF_COIP: c_int = AF_COIP;546pub const PF_COIP = AF_COIP;
547pub const PF_CNT: c_int = AF_CNT;547pub const PF_CNT = AF_CNT;
548pub const PF_SIP: c_int = AF_SIP;548pub const PF_SIP = AF_SIP;
549pub const PF_IPX: c_int = AF_IPX;549pub const PF_IPX = AF_IPX;
550pub const PF_RTIP: c_int = AF_RTIP;550pub const PF_RTIP = AF_RTIP;
551pub const PF_PIP: c_int = AF_PIP;551pub const PF_PIP = AF_PIP;
552pub const PF_ISDN: c_int = AF_ISDN;552pub const PF_ISDN = AF_ISDN;
553pub const PF_KEY: c_int = AF_KEY;553pub const PF_KEY = AF_KEY;
554pub const PF_INET6: c_int = AF_INET6;554pub const PF_INET6 = AF_INET6;
555pub const PF_NATM: c_int = AF_NATM;555pub const PF_NATM = AF_NATM;
556pub const PF_SYSTEM: c_int = AF_SYSTEM;556pub const PF_SYSTEM = AF_SYSTEM;
557pub const PF_NETBIOS: c_int = AF_NETBIOS;557pub const PF_NETBIOS = AF_NETBIOS;
558pub const PF_PPP: c_int = AF_PPP;558pub const PF_PPP = AF_PPP;
559pub const PF_MAX: c_int = AF_MAX;559pub const PF_MAX = AF_MAX;
560560
561pub const SYSPROTO_EVENT: c_int = 1;561pub const SYSPROTO_EVENT = 1;
562pub const SYSPROTO_CONTROL: c_int = 2;562pub const SYSPROTO_CONTROL = 2;
563563
564pub const SOCK_STREAM: c_int = 1;564pub const SOCK_STREAM = 1;
565pub const SOCK_DGRAM: c_int = 2;565pub const SOCK_DGRAM = 2;
566pub const SOCK_RAW: c_int = 3;566pub const SOCK_RAW = 3;
567pub const SOCK_RDM: c_int = 4;567pub const SOCK_RDM = 4;
568pub const SOCK_SEQPACKET: c_int = 5;568pub const SOCK_SEQPACKET = 5;
569pub const SOCK_MAXADDRLEN: c_int = 255;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
571fn wstatus(x: i32) i32 {578fn wstatus(x: i32) i32 {
572 return x & 0o177;579 return x & 0o177;
...@@ -605,6 +612,11 @@ pub fn abort() noreturn {...@@ -605,6 +612,11 @@ pub fn abort() noreturn {
605 c.abort();612 c.abort();
606}613}
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
608pub fn exit(code: i32) noreturn {620pub fn exit(code: i32) noreturn {
609 c.exit(code);621 c.exit(code);
610}622}
...@@ -634,6 +646,10 @@ pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize {...@@ -634,6 +646,10 @@ pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize {
634 return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte));646 return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte));
635}647}
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
637pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {653pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {
638 return errnoWrap(c.stat(path, buf));654 return errnoWrap(c.stat(path, buf));
639}655}
...@@ -642,6 +658,10 @@ pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize {...@@ -642,6 +658,10 @@ pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize {
642 return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte));658 return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte));
643}659}
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
645pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {665pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
646 const ptr_result = c.mmap(666 const ptr_result = c.mmap(
647 @ptrCast(*c_void, address),667 @ptrCast(*c_void, address),
...@@ -805,6 +825,20 @@ pub fn sigaction(sig: u5, noalias act: *const Sigaction, noalias oact: ?*Sigacti...@@ -805,6 +825,20 @@ pub fn sigaction(sig: u5, noalias act: *const Sigaction, noalias oact: ?*Sigacti
805 return result;825 return result;
806}826}
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
808pub const sigset_t = c.sigset_t;842pub const sigset_t = c.sigset_t;
809pub const empty_sigset = sigset_t(0);843pub const empty_sigset = sigset_t(0);
810844
...@@ -812,8 +846,13 @@ pub const timespec = c.timespec;...@@ -812,8 +846,13 @@ pub const timespec = c.timespec;
812pub const Stat = c.Stat;846pub const Stat = c.Stat;
813pub const dirent = c.dirent;847pub const dirent = c.dirent;
814848
849pub const in_port_t = c.in_port_t;
815pub const sa_family_t = c.sa_family_t;850pub const sa_family_t = c.sa_family_t;
851pub const socklen_t = c.socklen_t;
852
816pub const sockaddr = c.sockaddr;853pub const sockaddr = c.sockaddr;
854pub const sockaddr_in = c.sockaddr_in;
855pub const sockaddr_in6 = c.sockaddr_in6;
817856
818/// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall.857/// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall.
819pub const Kevent = c.Kevent;858pub const Kevent = c.Kevent;
std/os/file.zig+111-61
...@@ -7,6 +7,7 @@ const assert = std.debug.assert;...@@ -7,6 +7,7 @@ const assert = std.debug.assert;
7const posix = os.posix;7const posix = os.posix;
8const windows = os.windows;8const windows = os.windows;
9const Os = builtin.Os;9const Os = builtin.Os;
10const windows_util = @import("windows/util.zig");
1011
11const is_posix = builtin.os != builtin.Os.windows;12const is_posix = builtin.os != builtin.Os.windows;
12const is_windows = builtin.os == builtin.Os.windows;13const is_windows = builtin.os == builtin.Os.windows;
...@@ -15,18 +16,39 @@ pub const File = struct {...@@ -15,18 +16,39 @@ pub const File = struct {
15 /// The OS-specific file descriptor or file handle.16 /// The OS-specific file descriptor or file handle.
16 handle: os.FileHandle,17 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
18 pub const OpenError = os.WindowsOpenError || os.PosixOpenError;29 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.31 /// `openRead` except with a null terminated path
21 /// Call close to clean up.32 pub fn openReadC(path: [*]const u8) OpenError!File {
22 pub fn openRead(allocator: *mem.Allocator, path: []const u8) OpenError!File {
23 if (is_posix) {33 if (is_posix) {
24 const flags = posix.O_LARGEFILE | posix.O_RDONLY;34 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);
26 return openHandle(fd);36 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) {
28 const handle = try os.windowsOpen(51 const handle = try os.windowsOpen(
29 allocator,
30 path,52 path,
31 windows.GENERIC_READ,53 windows.GENERIC_READ,
32 windows.FILE_SHARE_READ,54 windows.FILE_SHARE_READ,
...@@ -34,28 +56,25 @@ pub const File = struct {...@@ -34,28 +56,25 @@ pub const File = struct {
34 windows.FILE_ATTRIBUTE_NORMAL,56 windows.FILE_ATTRIBUTE_NORMAL,
35 );57 );
36 return openHandle(handle);58 return openHandle(handle);
37 } else {
38 @compileError("TODO implement openRead for this OS");
39 }59 }
60 @compileError("Unsupported OS");
40 }61 }
4162
42 /// Calls `openWriteMode` with os.default_file_mode for the mode.63 /// Calls `openWriteMode` with os.File.default_mode for the mode.
43 pub fn openWrite(allocator: *mem.Allocator, path: []const u8) OpenError!File {64 pub fn openWrite(path: []const u8) OpenError!File {
44 return openWriteMode(allocator, path, os.default_file_mode);65 return openWriteMode(path, os.File.default_mode);
45 }66 }
4667
47 /// If the path does not exist it will be created.68 /// If the path does not exist it will be created.
48 /// If a file already exists in the destination it will be truncated.69 /// 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.
50 /// Call close to clean up.70 /// 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 {
52 if (is_posix) {72 if (is_posix) {
53 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_TRUNC;73 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);
55 return openHandle(fd);75 return openHandle(fd);
56 } else if (is_windows) {76 } else if (is_windows) {
57 const handle = try os.windowsOpen(77 const handle = try os.windowsOpen(
58 allocator,
59 path,78 path,
60 windows.GENERIC_WRITE,79 windows.GENERIC_WRITE,
61 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,80 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
...@@ -70,16 +89,14 @@ pub const File = struct {...@@ -70,16 +89,14 @@ pub const File = struct {
7089
71 /// If the path does not exist it will be created.90 /// If the path does not exist it will be created.
72 /// If a file already exists in the destination this returns OpenError.PathAlreadyExists91 /// 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.
74 /// Call close to clean up.92 /// 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 {
76 if (is_posix) {94 if (is_posix) {
77 const flags = posix.O_LARGEFILE | posix.O_WRONLY | posix.O_CREAT | posix.O_CLOEXEC | posix.O_EXCL;95 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);
79 return openHandle(fd);97 return openHandle(fd);
80 } else if (is_windows) {98 } else if (is_windows) {
81 const handle = try os.windowsOpen(99 const handle = try os.windowsOpen(
82 allocator,
83 path,100 path,
84 windows.GENERIC_WRITE,101 windows.GENERIC_WRITE,
85 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,102 windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
...@@ -98,23 +115,43 @@ pub const File = struct {...@@ -98,23 +115,43 @@ pub const File = struct {
98115
99 pub const AccessError = error{116 pub const AccessError = error{
100 PermissionDenied,117 PermissionDenied,
101 NotFound,118 FileNotFound,
102 NameTooLong,119 NameTooLong,
103 BadMode,120 InputOutput,
104 BadPathName,
105 Io,
106 SystemResources,121 SystemResources,
107 OutOfMemory,122 BadPathName,
123
124 /// On Windows, file paths must be valid Unicode.
125 InvalidUtf8,
108126
109 Unexpected,127 Unexpected,
110 };128 };
111129
112 pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void {130 /// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
113 const path_with_null = try std.cstr.addNullByte(allocator, path);131 /// Otherwise use `access` or `accessC`.
114 defer allocator.free(path_with_null);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 }
116 if (is_posix) {153 if (is_posix) {
117 const result = posix.access(path_with_null.ptr, posix.F_OK);154 const result = posix.access(path, posix.F_OK);
118 const err = posix.getErrno(result);155 const err = posix.getErrno(result);
119 switch (err) {156 switch (err) {
120 0 => return,157 0 => return,
...@@ -122,32 +159,33 @@ pub const File = struct {...@@ -122,32 +159,33 @@ pub const File = struct {
122 posix.EROFS => return error.PermissionDenied,159 posix.EROFS => return error.PermissionDenied,
123 posix.ELOOP => return error.PermissionDenied,160 posix.ELOOP => return error.PermissionDenied,
124 posix.ETXTBSY => return error.PermissionDenied,161 posix.ETXTBSY => return error.PermissionDenied,
125 posix.ENOTDIR => return error.NotFound,162 posix.ENOTDIR => return error.FileNotFound,
126 posix.ENOENT => return error.NotFound,163 posix.ENOENT => return error.FileNotFound,
127164
128 posix.ENAMETOOLONG => return error.NameTooLong,165 posix.ENAMETOOLONG => return error.NameTooLong,
129 posix.EINVAL => unreachable,166 posix.EINVAL => unreachable,
130 posix.EFAULT => return error.BadPathName,167 posix.EFAULT => unreachable,
131 posix.EIO => return error.Io,168 posix.EIO => return error.InputOutput,
132 posix.ENOMEM => return error.SystemResources,169 posix.ENOMEM => return error.SystemResources,
133 else => return os.unexpectedErrorPosix(err),170 else => return os.unexpectedErrorPosix(err),
134 }171 }
135 } else if (is_windows) {172 }
136 if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {173 @compileError("Unsupported OS");
137 return;174 }
138 }
139175
140 const err = windows.GetLastError();176 pub fn access(path: []const u8) AccessError!void {
141 switch (err) {177 if (is_windows) {
142 windows.ERROR.FILE_NOT_FOUND,178 const path_w = try windows_util.sliceToPrefixedFileW(path);
143 windows.ERROR.PATH_NOT_FOUND,179 return accessW(&path_w);
144 => return error.NotFound,180 }
145 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,181 if (is_posix) {
146 else => return os.unexpectedErrorWindows(err),182 var path_with_null: [posix.PATH_MAX]u8 = undefined;
147 }183 if (path.len >= posix.PATH_MAX) return error.NameTooLong;
148 } else {184 mem.copy(u8, path_with_null[0..], path);
149 @compileError("TODO implement access for this OS");185 path_with_null[path.len] = 0;
186 return accessC(&path_with_null);
150 }187 }
188 @compileError("Unsupported OS");
151 }189 }
152190
153 /// Upon success, the stream is in an uninitialized state. To continue using it,191 /// Upon success, the stream is in an uninitialized state. To continue using it,
...@@ -169,7 +207,9 @@ pub const File = struct {...@@ -169,7 +207,9 @@ pub const File = struct {
169 const err = posix.getErrno(result);207 const err = posix.getErrno(result);
170 if (err > 0) {208 if (err > 0) {
171 return switch (err) {209 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,
173 posix.EINVAL => error.Unseekable,213 posix.EINVAL => error.Unseekable,
174 posix.EOVERFLOW => error.Unseekable,214 posix.EOVERFLOW => error.Unseekable,
175 posix.ESPIPE => error.Unseekable,215 posix.ESPIPE => error.Unseekable,
...@@ -182,7 +222,7 @@ pub const File = struct {...@@ -182,7 +222,7 @@ pub const File = struct {
182 if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) {222 if (windows.SetFilePointerEx(self.handle, amount, null, windows.FILE_CURRENT) == 0) {
183 const err = windows.GetLastError();223 const err = windows.GetLastError();
184 return switch (err) {224 return switch (err) {
185 windows.ERROR.INVALID_PARAMETER => error.BadFd,225 windows.ERROR.INVALID_PARAMETER => unreachable,
186 else => os.unexpectedErrorWindows(err),226 else => os.unexpectedErrorWindows(err),
187 };227 };
188 }228 }
...@@ -199,7 +239,9 @@ pub const File = struct {...@@ -199,7 +239,9 @@ pub const File = struct {
199 const err = posix.getErrno(result);239 const err = posix.getErrno(result);
200 if (err > 0) {240 if (err > 0) {
201 return switch (err) {241 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,
203 posix.EINVAL => error.Unseekable,245 posix.EINVAL => error.Unseekable,
204 posix.EOVERFLOW => error.Unseekable,246 posix.EOVERFLOW => error.Unseekable,
205 posix.ESPIPE => error.Unseekable,247 posix.ESPIPE => error.Unseekable,
...@@ -213,7 +255,7 @@ pub const File = struct {...@@ -213,7 +255,7 @@ pub const File = struct {
213 if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) {255 if (windows.SetFilePointerEx(self.handle, ipos, null, windows.FILE_BEGIN) == 0) {
214 const err = windows.GetLastError();256 const err = windows.GetLastError();
215 return switch (err) {257 return switch (err) {
216 windows.ERROR.INVALID_PARAMETER => error.BadFd,258 windows.ERROR.INVALID_PARAMETER => unreachable,
217 else => os.unexpectedErrorWindows(err),259 else => os.unexpectedErrorWindows(err),
218 };260 };
219 }261 }
...@@ -229,7 +271,9 @@ pub const File = struct {...@@ -229,7 +271,9 @@ pub const File = struct {
229 const err = posix.getErrno(result);271 const err = posix.getErrno(result);
230 if (err > 0) {272 if (err > 0) {
231 return switch (err) {273 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,
233 posix.EINVAL => error.Unseekable,277 posix.EINVAL => error.Unseekable,
234 posix.EOVERFLOW => error.Unseekable,278 posix.EOVERFLOW => error.Unseekable,
235 posix.ESPIPE => error.Unseekable,279 posix.ESPIPE => error.Unseekable,
...@@ -244,7 +288,7 @@ pub const File = struct {...@@ -244,7 +288,7 @@ pub const File = struct {
244 if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {288 if (windows.SetFilePointerEx(self.handle, 0, &pos, windows.FILE_CURRENT) == 0) {
245 const err = windows.GetLastError();289 const err = windows.GetLastError();
246 return switch (err) {290 return switch (err) {
247 windows.ERROR.INVALID_PARAMETER => error.BadFd,291 windows.ERROR.INVALID_PARAMETER => unreachable,
248 else => os.unexpectedErrorWindows(err),292 else => os.unexpectedErrorWindows(err),
249 };293 };
250 }294 }
...@@ -277,18 +321,19 @@ pub const File = struct {...@@ -277,18 +321,19 @@ pub const File = struct {
277 }321 }
278322
279 pub const ModeError = error{323 pub const ModeError = error{
280 BadFd,
281 SystemResources,324 SystemResources,
282 Unexpected,325 Unexpected,
283 };326 };
284327
285 pub fn mode(self: *File) ModeError!os.FileMode {328 pub fn mode(self: *File) ModeError!Mode {
286 if (is_posix) {329 if (is_posix) {
287 var stat: posix.Stat = undefined;330 var stat: posix.Stat = undefined;
288 const err = posix.getErrno(posix.fstat(self.handle, &stat));331 const err = posix.getErrno(posix.fstat(self.handle, &stat));
289 if (err > 0) {332 if (err > 0) {
290 return switch (err) {333 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,
292 posix.ENOMEM => error.SystemResources,337 posix.ENOMEM => error.SystemResources,
293 else => os.unexpectedErrorPosix(err),338 else => os.unexpectedErrorPosix(err),
294 };339 };
...@@ -296,7 +341,7 @@ pub const File = struct {...@@ -296,7 +341,7 @@ pub const File = struct {
296341
297 // TODO: we should be able to cast u16 to ModeError!u32, making this342 // TODO: we should be able to cast u16 to ModeError!u32, making this
298 // explicit cast not necessary343 // explicit cast not necessary
299 return os.FileMode(stat.mode);344 return Mode(stat.mode);
300 } else if (is_windows) {345 } else if (is_windows) {
301 return {};346 return {};
302 } else {347 } else {
...@@ -305,9 +350,11 @@ pub const File = struct {...@@ -305,9 +350,11 @@ pub const File = struct {
305 }350 }
306351
307 pub const ReadError = error{352 pub const ReadError = error{
308 BadFd,353 FileClosed,
309 Io,354 InputOutput,
310 IsDir,355 IsDir,
356 WouldBlock,
357 SystemResources,
311358
312 Unexpected,359 Unexpected,
313 };360 };
...@@ -323,9 +370,12 @@ pub const File = struct {...@@ -323,9 +370,12 @@ pub const File = struct {
323 posix.EINTR => continue,370 posix.EINTR => continue,
324 posix.EINVAL => unreachable,371 posix.EINVAL => unreachable,
325 posix.EFAULT => unreachable,372 posix.EFAULT => unreachable,
326 posix.EBADF => return error.BadFd,373 posix.EAGAIN => return error.WouldBlock,
327 posix.EIO => return error.Io,374 posix.EBADF => return error.FileClosed,
375 posix.EIO => return error.InputOutput,
328 posix.EISDIR => return error.IsDir,376 posix.EISDIR => return error.IsDir,
377 posix.ENOBUFS => return error.SystemResources,
378 posix.ENOMEM => return error.SystemResources,
329 else => return os.unexpectedErrorPosix(read_err),379 else => return os.unexpectedErrorPosix(read_err),
330 }380 }
331 }381 }
...@@ -338,7 +388,7 @@ pub const File = struct {...@@ -338,7 +388,7 @@ pub const File = struct {
338 while (index < buffer.len) {388 while (index < buffer.len) {
339 const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));389 const want_read_count = @intCast(windows.DWORD, math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));
340 var amt_read: windows.DWORD = undefined;390 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) {
342 const err = windows.GetLastError();392 const err = windows.GetLastError();
343 return switch (err) {393 return switch (err) {
344 windows.ERROR.OPERATION_ABORTED => continue,394 windows.ERROR.OPERATION_ABORTED => continue,
std/os/get_app_data_dir.zig+2-1
...@@ -10,6 +10,7 @@ pub const GetAppDataDirError = error{...@@ -10,6 +10,7 @@ pub const GetAppDataDirError = error{
10};10};
1111
12/// Caller owns returned memory.12/// Caller owns returned memory.
13/// TODO determine if we can remove the allocator requirement
13pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {14pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataDirError![]u8 {
14 switch (builtin.os) {15 switch (builtin.os) {
15 builtin.Os.windows => {16 builtin.Os.windows => {
...@@ -22,7 +23,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD...@@ -22,7 +23,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
22 )) {23 )) {
23 os.windows.S_OK => {24 os.windows.S_OK => {
24 defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr));25 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) {
26 error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,27 error.UnexpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
27 error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,28 error.ExpectedSecondSurrogateHalf => return error.AppDataDirUnavailable,
28 error.DanglingSurrogateHalf => return error.AppDataDirUnavailable,29 error.DanglingSurrogateHalf => return error.AppDataDirUnavailable,
std/os/index.zig+544-267
...@@ -38,17 +38,16 @@ pub const path = @import("path.zig");...@@ -38,17 +38,16 @@ pub const path = @import("path.zig");
38pub const File = @import("file.zig").File;38pub const File = @import("file.zig").File;
39pub const time = @import("time.zig");39pub 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
51pub const page_size = 4 * 1024;41pub 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
53pub const UserInfo = @import("get_user_id.zig").UserInfo;52pub const UserInfo = @import("get_user_id.zig").UserInfo;
54pub const getUserInfo = @import("get_user_id.zig").getUserInfo;53pub const getUserInfo = @import("get_user_id.zig").getUserInfo;
...@@ -160,7 +159,7 @@ test "os.getRandomBytes" {...@@ -160,7 +159,7 @@ test "os.getRandomBytes" {
160 try getRandomBytes(buf_b[0..]);159 try getRandomBytes(buf_b[0..]);
161160
162 // Check if random (not 100% conclusive)161 // Check if random (not 100% conclusive)
163 assert( !mem.eql(u8, buf_a, buf_b) );162 assert(!mem.eql(u8, buf_a, buf_b));
164}163}
165164
166/// Raises a signal in the current kernel thread, ending its execution.165/// Raises a signal in the current kernel thread, ending its execution.
...@@ -256,6 +255,67 @@ pub fn posixRead(fd: i32, buf: []u8) !void {...@@ -256,6 +255,67 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
256 }255 }
257}256}
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
259pub const PosixWriteError = error{319pub const PosixWriteError = error{
260 WouldBlock,320 WouldBlock,
261 FileClosed,321 FileClosed,
...@@ -266,6 +326,8 @@ pub const PosixWriteError = error{...@@ -266,6 +326,8 @@ pub const PosixWriteError = error{
266 NoSpaceLeft,326 NoSpaceLeft,
267 AccessDenied,327 AccessDenied,
268 BrokenPipe,328 BrokenPipe,
329
330 /// See https://github.com/ziglang/zig/issues/1396
269 Unexpected,331 Unexpected,
270};332};
271333
...@@ -300,8 +362,72 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {...@@ -300,8 +362,72 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
300 }362 }
301}363}
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
303pub const PosixOpenError = error{430pub const PosixOpenError = error{
304 OutOfMemory,
305 AccessDenied,431 AccessDenied,
306 FileTooBig,432 FileTooBig,
307 IsDir,433 IsDir,
...@@ -310,22 +436,22 @@ pub const PosixOpenError = error{...@@ -310,22 +436,22 @@ pub const PosixOpenError = error{
310 NameTooLong,436 NameTooLong,
311 SystemFdQuotaExceeded,437 SystemFdQuotaExceeded,
312 NoDevice,438 NoDevice,
313 PathNotFound,439 FileNotFound,
314 SystemResources,440 SystemResources,
315 NoSpaceLeft,441 NoSpaceLeft,
316 NotDir,442 NotDir,
317 PathAlreadyExists,443 PathAlreadyExists,
444
445 /// See https://github.com/ziglang/zig/issues/1396
318 Unexpected,446 Unexpected,
319};447};
320448
321/// ::file_path needs to be copied in memory to add a null terminating byte.449/// ::file_path needs to be copied in memory to add a null terminating byte.
322/// Calls POSIX open, keeps trying if it gets interrupted, and translates450/// Calls POSIX open, keeps trying if it gets interrupted, and translates
323/// the return value into zig errors.451/// the return value into zig errors.
324pub fn posixOpen(allocator: *Allocator, file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 {452pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 {
325 const path_with_null = try cstr.addNullByte(allocator, file_path);453 const file_path_c = try toPosixPath(file_path);
326 defer allocator.free(path_with_null);454 return posixOpenC(&file_path_c, flags, perm);
327
328 return posixOpenC(path_with_null.ptr, flags, perm);
329}455}
330456
331// TODO https://github.com/ziglang/zig/issues/265457// TODO https://github.com/ziglang/zig/issues/265
...@@ -347,7 +473,7 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 {...@@ -347,7 +473,7 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 {
347 posix.ENAMETOOLONG => return PosixOpenError.NameTooLong,473 posix.ENAMETOOLONG => return PosixOpenError.NameTooLong,
348 posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded,474 posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded,
349 posix.ENODEV => return PosixOpenError.NoDevice,475 posix.ENODEV => return PosixOpenError.NoDevice,
350 posix.ENOENT => return PosixOpenError.PathNotFound,476 posix.ENOENT => return PosixOpenError.FileNotFound,
351 posix.ENOMEM => return PosixOpenError.SystemResources,477 posix.ENOMEM => return PosixOpenError.SystemResources,
352 posix.ENOSPC => return PosixOpenError.NoSpaceLeft,478 posix.ENOSPC => return PosixOpenError.NoSpaceLeft,
353 posix.ENOTDIR => return PosixOpenError.NotDir,479 posix.ENOTDIR => return PosixOpenError.NotDir,
...@@ -360,6 +486,16 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 {...@@ -360,6 +486,16 @@ pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 {
360 }486 }
361}487}
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
363pub fn posixDup2(old_fd: i32, new_fd: i32) !void {499pub fn posixDup2(old_fd: i32, new_fd: i32) !void {
364 while (true) {500 while (true) {
365 const err = posix.getErrno(posix.dup2(old_fd, new_fd));501 const err = posix.getErrno(posix.dup2(old_fd, new_fd));
...@@ -475,6 +611,8 @@ pub const PosixExecveError = error{...@@ -475,6 +611,8 @@ pub const PosixExecveError = error{
475 FileNotFound,611 FileNotFound,
476 NotDir,612 NotDir,
477 FileBusy,613 FileBusy,
614
615 /// See https://github.com/ziglang/zig/issues/1396
478 Unexpected,616 Unexpected,
479};617};
480618
...@@ -497,6 +635,35 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {...@@ -497,6 +635,35 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
497pub var linux_aux_raw = []usize{0} ** 38;635pub var linux_aux_raw = []usize{0} ** 38;
498pub var posix_environ_raw: [][*]u8 = undefined;636pub 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
500/// Caller must free result when done.667/// Caller must free result when done.
501/// TODO make this go through libc when we have it668/// TODO make this go through libc when we have it
502pub fn getEnvMap(allocator: *Allocator) !BufMap {669pub fn getEnvMap(allocator: *Allocator) !BufMap {
...@@ -603,43 +770,39 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned...@@ -603,43 +770,39 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwned
603}770}
604771
605/// Caller must free the returned memory.772/// Caller must free the returned memory.
606pub fn getCwd(allocator: *Allocator) ![]u8 {773pub fn getCwdAlloc(allocator: *Allocator) ![]u8 {
607 switch (builtin.os) {774 var buf: [MAX_PATH_BYTES]u8 = undefined;
608 Os.windows => {775 return mem.dupe(allocator, u8, try getCwd(&buf));
609 var buf = try allocator.alloc(u8, 256);776}
610 errdefer allocator.free(buf);
611
612 while (true) {
613 const result = windows.GetCurrentDirectoryA(@intCast(windows.WORD, buf.len), buf.ptr);
614777
615 if (result == 0) {778pub const GetCwdError = error{Unexpected};
616 const err = windows.GetLastError();
617 return switch (err) {
618 else => unexpectedErrorWindows(err),
619 };
620 }
621779
622 if (result > buf.len) {780/// The result is a slice of out_buffer.
623 buf = try allocator.realloc(u8, buf, result);781pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 {
624 continue;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),
625 }792 }
626
627 return allocator.shrink(u8, buf, result);
628 }793 }
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];
629 },799 },
630 else => {800 else => {
631 var buf = try allocator.alloc(u8, 1024);801 const err = posix.getErrno(posix.getcwd(out_buffer, out_buffer.len));
632 errdefer allocator.free(buf);802 switch (err) {
633 while (true) {803 0 => return cstr.toSlice(out_buffer),
634 const err = posix.getErrno(posix.getcwd(buf.ptr, buf.len));804 posix.ERANGE => unreachable,
635 if (err == posix.ERANGE) {805 else => return unexpectedErrorPosix(err),
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));
643 }806 }
644 },807 },
645 }808 }
...@@ -647,7 +810,9 @@ pub fn getCwd(allocator: *Allocator) ![]u8 {...@@ -647,7 +810,9 @@ pub fn getCwd(allocator: *Allocator) ![]u8 {
647810
648test "os.getCwd" {811test "os.getCwd" {
649 // at least call it so it gets compiled812 // 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);
651}816}
652817
653pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;818pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError;
...@@ -662,6 +827,8 @@ pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []con...@@ -662,6 +827,8 @@ pub fn symLink(allocator: *Allocator, existing_path: []const u8, new_path: []con
662827
663pub const WindowsSymLinkError = error{828pub const WindowsSymLinkError = error{
664 OutOfMemory,829 OutOfMemory,
830
831 /// See https://github.com/ziglang/zig/issues/1396
665 Unexpected,832 Unexpected,
666};833};
667834
...@@ -692,6 +859,8 @@ pub const PosixSymLinkError = error{...@@ -692,6 +859,8 @@ pub const PosixSymLinkError = error{
692 NoSpaceLeft,859 NoSpaceLeft,
693 ReadOnlyFileSystem,860 ReadOnlyFileSystem,
694 NotDir,861 NotDir,
862
863 /// See https://github.com/ziglang/zig/issues/1396
695 Unexpected,864 Unexpected,
696};865};
697866
...@@ -750,7 +919,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:...@@ -750,7 +919,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
750 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);919 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
751920
752 if (symLink(allocator, existing_path, tmp_path)) {921 if (symLink(allocator, existing_path, tmp_path)) {
753 return rename(allocator, tmp_path, new_path);922 return rename(tmp_path, new_path);
754 } else |err| switch (err) {923 } else |err| switch (err) {
755 error.PathAlreadyExists => continue,924 error.PathAlreadyExists => continue,
756 else => return err, // TODO zig should know this set does not include PathAlreadyExists925 else => return err, // TODO zig should know this set does not include PathAlreadyExists
...@@ -769,70 +938,75 @@ pub const DeleteFileError = error{...@@ -769,70 +938,75 @@ pub const DeleteFileError = error{
769 NotDir,938 NotDir,
770 SystemResources,939 SystemResources,
771 ReadOnlyFileSystem,940 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
774 Unexpected,950 Unexpected,
775};951};
776952
777pub fn deleteFile(allocator: *Allocator, file_path: []const u8) DeleteFileError!void {953pub fn deleteFile(file_path: []const u8) DeleteFileError!void {
778 if (builtin.os == Os.windows) {954 if (builtin.os == Os.windows) {
779 return deleteFileWindows(allocator, file_path);955 return deleteFileWindows(file_path);
780 } else {956 } else {
781 return deleteFilePosix(allocator, file_path);957 return deleteFilePosix(file_path);
782 }958 }
783}959}
784960
785pub fn deleteFileWindows(allocator: *Allocator, file_path: []const u8) !void {961pub fn deleteFileWindows(file_path: []const u8) !void {
786 const buf = try allocator.alloc(u8, file_path.len + 1);962 const file_path_w = try windows_util.sliceToPrefixedFileW(file_path);
787 defer allocator.free(buf);
788963
789 mem.copy(u8, buf, file_path);964 if (windows.DeleteFileW(&file_path_w) == 0) {
790 buf[file_path.len] = 0;
791
792 if (windows.DeleteFileA(buf.ptr) == 0) {
793 const err = windows.GetLastError();965 const err = windows.GetLastError();
794 return switch (err) {966 switch (err) {
795 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,967 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
796 windows.ERROR.ACCESS_DENIED => error.AccessDenied,968 windows.ERROR.ACCESS_DENIED => return error.AccessDenied,
797 windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong,969 windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong,
798 else => unexpectedErrorWindows(err),970 windows.ERROR.INVALID_PARAMETER => return error.NameTooLong,
799 };971 else => return unexpectedErrorWindows(err),
972 }
800 }973 }
801}974}
802975
803pub fn deleteFilePosix(allocator: *Allocator, file_path: []const u8) !void {976pub fn deleteFilePosixC(file_path: [*]const u8) !void {
804 const buf = try allocator.alloc(u8, file_path.len + 1);977 const err = posix.getErrno(posix.unlink(file_path));
805 defer allocator.free(buf);978 switch (err) {
806979 0 => return,
807 mem.copy(u8, buf, file_path);980 posix.EACCES => return error.AccessDenied,
808 buf[file_path.len] = 0;981 posix.EPERM => return error.AccessDenied,
809982 posix.EBUSY => return error.FileBusy,
810 const err = posix.getErrno(posix.unlink(buf.ptr));983 posix.EFAULT => unreachable,
811 if (err > 0) {984 posix.EINVAL => unreachable,
812 return switch (err) {985 posix.EIO => return error.FileSystem,
813 posix.EACCES, posix.EPERM => error.AccessDenied,986 posix.EISDIR => return error.IsDir,
814 posix.EBUSY => error.FileBusy,987 posix.ELOOP => return error.SymLinkLoop,
815 posix.EFAULT, posix.EINVAL => unreachable,988 posix.ENAMETOOLONG => return error.NameTooLong,
816 posix.EIO => error.FileSystem,989 posix.ENOENT => return error.FileNotFound,
817 posix.EISDIR => error.IsDir,990 posix.ENOTDIR => return error.NotDir,
818 posix.ELOOP => error.SymLinkLoop,991 posix.ENOMEM => return error.SystemResources,
819 posix.ENAMETOOLONG => error.NameTooLong,992 posix.EROFS => return error.ReadOnlyFileSystem,
820 posix.ENOENT => error.FileNotFound,993 else => return unexpectedErrorPosix(err),
821 posix.ENOTDIR => error.NotDir,
822 posix.ENOMEM => error.SystemResources,
823 posix.EROFS => error.ReadOnlyFileSystem,
824 else => unexpectedErrorPosix(err),
825 };
826 }994 }
827}995}
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
829/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is1002/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
830/// merged and readily available,1003/// merged and readily available,
831/// there is a possibility of power loss or application termination leaving temporary files present1004/// there is a possibility of power loss or application termination leaving temporary files present
832/// in the same directory as dest_path.1005/// in the same directory as dest_path.
833/// Destination file will have the same mode as the source file.1006/// Destination file will have the same mode as the source file.
1007/// TODO investigate if this can work with no allocator
834pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []const u8) !void {1008pub 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);
836 defer in_file.close();1010 defer in_file.close();
8371011
838 const mode = try in_file.mode();1012 const mode = try in_file.mode();
...@@ -853,8 +1027,9 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con...@@ -853,8 +1027,9 @@ pub fn copyFile(allocator: *Allocator, source_path: []const u8, dest_path: []con
853/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is1027/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is
854/// merged and readily available,1028/// merged and readily available,
855/// there is a possibility of power loss or application termination leaving temporary files present1029/// 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 {1030/// TODO investigate if this can work with no allocator
857 var in_file = try os.File.openRead(allocator, source_path);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);
858 defer in_file.close();1033 defer in_file.close();
8591034
860 var atomic_file = try AtomicFile.init(allocator, dest_path, mode);1035 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: [...@@ -871,6 +1046,7 @@ pub fn copyFileMode(allocator: *Allocator, source_path: []const u8, dest_path: [
871}1046}
8721047
873pub const AtomicFile = struct {1048pub const AtomicFile = struct {
1049 /// TODO investigate if we can make this work with no allocator
874 allocator: *Allocator,1050 allocator: *Allocator,
875 file: os.File,1051 file: os.File,
876 tmp_path: []u8,1052 tmp_path: []u8,
...@@ -879,7 +1055,7 @@ pub const AtomicFile = struct {...@@ -879,7 +1055,7 @@ pub const AtomicFile = struct {
8791055
880 /// dest_path must remain valid for the lifetime of AtomicFile1056 /// dest_path must remain valid for the lifetime of AtomicFile
881 /// call finish to atomically replace dest_path with contents1057 /// 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 {
883 const dirname = os.path.dirname(dest_path);1059 const dirname = os.path.dirname(dest_path);
8841060
885 var rand_buf: [12]u8 = undefined;1061 var rand_buf: [12]u8 = undefined;
...@@ -898,7 +1074,7 @@ pub const AtomicFile = struct {...@@ -898,7 +1074,7 @@ pub const AtomicFile = struct {
898 try getRandomBytes(rand_buf[0..]);1074 try getRandomBytes(rand_buf[0..]);
899 b64_fs_encoder.encode(tmp_path[dirname_component_len..], rand_buf);1075 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) {
902 error.PathAlreadyExists => continue,1078 error.PathAlreadyExists => continue,
903 // TODO zig should figure out that this error set does not include PathAlreadyExists since1079 // TODO zig should figure out that this error set does not include PathAlreadyExists since
904 // it is handled in the above switch1080 // it is handled in the above switch
...@@ -919,7 +1095,7 @@ pub const AtomicFile = struct {...@@ -919,7 +1095,7 @@ pub const AtomicFile = struct {
919 pub fn deinit(self: *AtomicFile) void {1095 pub fn deinit(self: *AtomicFile) void {
920 if (!self.finished) {1096 if (!self.finished) {
921 self.file.close();1097 self.file.close();
922 deleteFile(self.allocator, self.tmp_path) catch {};1098 deleteFile(self.tmp_path) catch {};
923 self.allocator.free(self.tmp_path);1099 self.allocator.free(self.tmp_path);
924 self.finished = true;1100 self.finished = true;
925 }1101 }
...@@ -928,70 +1104,72 @@ pub const AtomicFile = struct {...@@ -928,70 +1104,72 @@ pub const AtomicFile = struct {
928 pub fn finish(self: *AtomicFile) !void {1104 pub fn finish(self: *AtomicFile) !void {
929 assert(!self.finished);1105 assert(!self.finished);
930 self.file.close();1106 self.file.close();
931 try rename(self.allocator, self.tmp_path, self.dest_path);1107 try rename(self.tmp_path, self.dest_path);
932 self.allocator.free(self.tmp_path);1108 self.allocator.free(self.tmp_path);
933 self.finished = true;1109 self.finished = true;
934 }1110 }
935};1111};
9361112
937pub fn rename(allocator: *Allocator, old_path: []const u8, new_path: []const u8) !void {1113pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void {
938 const full_buf = try allocator.alloc(u8, old_path.len + new_path.len + 2);1114 if (is_windows) {
939 defer allocator.free(full_buf);1115 @compileError("TODO implement for windows");
9401116 } else {
941 const old_buf = full_buf;1117 const err = posix.getErrno(posix.rename(old_path, new_path));
942 mem.copy(u8, old_buf, old_path);1118 switch (err) {
943 old_buf[old_path.len] = 0;1119 0 => return,
9441120 posix.EACCES => return error.AccessDenied,
945 const new_buf = full_buf[old_path.len + 1 ..];1121 posix.EPERM => return error.AccessDenied,
946 mem.copy(u8, new_buf, new_path);1122 posix.EBUSY => return error.FileBusy,
947 new_buf[new_path.len] = 0;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 {
949 if (is_windows) {1144 if (is_windows) {
950 const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;1145 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) {
952 const err = windows.GetLastError();1149 const err = windows.GetLastError();
953 return switch (err) {1150 switch (err) {
954 else => unexpectedErrorWindows(err),1151 else => return unexpectedErrorWindows(err),
955 };1152 }
956 }1153 }
957 } else {1154 } else {
958 const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));1155 const old_path_c = try toPosixPath(old_path);
959 if (err > 0) {1156 const new_path_c = try toPosixPath(new_path);
960 return switch (err) {1157 return renameC(&old_path_c, &new_path_c);
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 }
979 }1158 }
980}1159}
9811160
982pub fn makeDir(allocator: *Allocator, dir_path: []const u8) !void {1161pub fn makeDir(dir_path: []const u8) !void {
983 if (is_windows) {1162 if (is_windows) {
984 return makeDirWindows(allocator, dir_path);1163 return makeDirWindows(dir_path);
985 } else {1164 } else {
986 return makeDirPosix(allocator, dir_path);1165 return makeDirPosix(dir_path);
987 }1166 }
988}1167}
9891168
990pub fn makeDirWindows(allocator: *Allocator, dir_path: []const u8) !void {1169pub fn makeDirWindows(dir_path: []const u8) !void {
991 const path_buf = try cstr.addNullByte(allocator, dir_path);1170 const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path);
992 defer allocator.free(path_buf);
9931171
994 if (windows.CreateDirectoryA(path_buf.ptr, null) == 0) {1172 if (windows.CreateDirectoryW(&dir_path_w, null) == 0) {
995 const err = windows.GetLastError();1173 const err = windows.GetLastError();
996 return switch (err) {1174 return switch (err) {
997 windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists,1175 windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists,
...@@ -1001,54 +1179,57 @@ pub fn makeDirWindows(allocator: *Allocator, dir_path: []const u8) !void {...@@ -1001,54 +1179,57 @@ pub fn makeDirWindows(allocator: *Allocator, dir_path: []const u8) !void {
1001 }1179 }
1002}1180}
10031181
1004pub fn makeDirPosix(allocator: *Allocator, dir_path: []const u8) !void {1182pub fn makeDirPosixC(dir_path: [*]const u8) !void {
1005 const path_buf = try cstr.addNullByte(allocator, dir_path);1183 const err = posix.getErrno(posix.mkdir(dir_path, 0o755));
1006 defer allocator.free(path_buf);1184 switch (err) {
10071185 0 => return,
1008 const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));1186 posix.EACCES => return error.AccessDenied,
1009 if (err > 0) {1187 posix.EPERM => return error.AccessDenied,
1010 return switch (err) {1188 posix.EDQUOT => return error.DiskQuota,
1011 posix.EACCES, posix.EPERM => error.AccessDenied,1189 posix.EEXIST => return error.PathAlreadyExists,
1012 posix.EDQUOT => error.DiskQuota,1190 posix.EFAULT => unreachable,
1013 posix.EEXIST => error.PathAlreadyExists,1191 posix.ELOOP => return error.SymLinkLoop,
1014 posix.EFAULT => unreachable,1192 posix.EMLINK => return error.LinkQuotaExceeded,
1015 posix.ELOOP => error.SymLinkLoop,1193 posix.ENAMETOOLONG => return error.NameTooLong,
1016 posix.EMLINK => error.LinkQuotaExceeded,1194 posix.ENOENT => return error.FileNotFound,
1017 posix.ENAMETOOLONG => error.NameTooLong,1195 posix.ENOMEM => return error.SystemResources,
1018 posix.ENOENT => error.FileNotFound,1196 posix.ENOSPC => return error.NoSpaceLeft,
1019 posix.ENOMEM => error.SystemResources,1197 posix.ENOTDIR => return error.NotDir,
1020 posix.ENOSPC => error.NoSpaceLeft,1198 posix.EROFS => return error.ReadOnlyFileSystem,
1021 posix.ENOTDIR => error.NotDir,1199 else => return unexpectedErrorPosix(err),
1022 posix.EROFS => error.ReadOnlyFileSystem,
1023 else => unexpectedErrorPosix(err),
1024 };
1025 }1200 }
1026}1201}
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
1028/// Calls makeDir recursively to make an entire path. Returns success if the path1208/// Calls makeDir recursively to make an entire path. Returns success if the path
1029/// already exists and is a directory.1209/// already exists and is a directory.
1210/// TODO determine if we can remove the allocator requirement from this function
1030pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {1211pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
1031 const resolved_path = try path.resolve(allocator, full_path);1212 const resolved_path = try path.resolve(allocator, full_path);
1032 defer allocator.free(resolved_path);1213 defer allocator.free(resolved_path);
10331214
1034 var end_index: usize = resolved_path.len;1215 var end_index: usize = resolved_path.len;
1035 while (true) {1216 while (true) {
1036 makeDir(allocator, resolved_path[0..end_index]) catch |err| {1217 makeDir(resolved_path[0..end_index]) catch |err| switch (err) {
1037 if (err == error.PathAlreadyExists) {1218 error.PathAlreadyExists => {
1038 // TODO stat the file and return an error if it's not a directory1219 // TODO stat the file and return an error if it's not a directory
1039 // this is important because otherwise a dangling symlink1220 // this is important because otherwise a dangling symlink
1040 // could cause an infinite loop1221 // could cause an infinite loop
1041 if (end_index == resolved_path.len) return;1222 if (end_index == resolved_path.len) return;
1042 } else if (err == error.FileNotFound) {1223 },
1224 error.FileNotFound => {
1043 // march end_index backward until next path component1225 // march end_index backward until next path component
1044 while (true) {1226 while (true) {
1045 end_index -= 1;1227 end_index -= 1;
1046 if (os.path.isSep(resolved_path[end_index])) break;1228 if (os.path.isSep(resolved_path[end_index])) break;
1047 }1229 }
1048 continue;1230 continue;
1049 } else {1231 },
1050 return err;1232 else => return err,
1051 }
1052 };1233 };
1053 if (end_index == resolved_path.len) return;1234 if (end_index == resolved_path.len) return;
1054 // march end_index forward until next path component1235 // march end_index forward until next path component
...@@ -1071,6 +1252,7 @@ pub const DeleteDirError = error{...@@ -1071,6 +1252,7 @@ pub const DeleteDirError = error{
1071 ReadOnlyFileSystem,1252 ReadOnlyFileSystem,
1072 OutOfMemory,1253 OutOfMemory,
10731254
1255 /// See https://github.com/ziglang/zig/issues/1396
1074 Unexpected,1256 Unexpected,
1075};1257};
10761258
...@@ -1129,7 +1311,6 @@ const DeleteTreeError = error{...@@ -1129,7 +1311,6 @@ const DeleteTreeError = error{
1129 NameTooLong,1311 NameTooLong,
1130 SystemFdQuotaExceeded,1312 SystemFdQuotaExceeded,
1131 NoDevice,1313 NoDevice,
1132 PathNotFound,
1133 SystemResources,1314 SystemResources,
1134 NoSpaceLeft,1315 NoSpaceLeft,
1135 PathAlreadyExists,1316 PathAlreadyExists,
...@@ -1139,20 +1320,30 @@ const DeleteTreeError = error{...@@ -1139,20 +1320,30 @@ const DeleteTreeError = error{
1139 FileSystem,1320 FileSystem,
1140 FileBusy,1321 FileBusy,
1141 DirNotEmpty,1322 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
1142 Unexpected,1332 Unexpected,
1143};1333};
1334
1335/// TODO determine if we can remove the allocator requirement
1144pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void {1336pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void {
1145 start_over: while (true) {1337 start_over: while (true) {
1146 var got_access_denied = false;1338 var got_access_denied = false;
1147 // First, try deleting the item as a file. This way we don't follow sym links.1339 // 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)) {
1149 return;1341 return;
1150 } else |err| switch (err) {1342 } else |err| switch (err) {
1151 error.FileNotFound => return,1343 error.FileNotFound => return,
1152 error.IsDir => {},1344 error.IsDir => {},
1153 error.AccessDenied => got_access_denied = true,1345 error.AccessDenied => got_access_denied = true,
11541346
1155 error.OutOfMemory,
1156 error.SymLinkLoop,1347 error.SymLinkLoop,
1157 error.NameTooLong,1348 error.NameTooLong,
1158 error.SystemResources,1349 error.SystemResources,
...@@ -1160,6 +1351,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -1160,6 +1351,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
1160 error.NotDir,1351 error.NotDir,
1161 error.FileSystem,1352 error.FileSystem,
1162 error.FileBusy,1353 error.FileBusy,
1354 error.InvalidUtf8,
1355 error.BadPathName,
1163 error.Unexpected,1356 error.Unexpected,
1164 => return err,1357 => return err,
1165 }1358 }
...@@ -1181,7 +1374,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -1181,7 +1374,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
1181 error.NameTooLong,1374 error.NameTooLong,
1182 error.SystemFdQuotaExceeded,1375 error.SystemFdQuotaExceeded,
1183 error.NoDevice,1376 error.NoDevice,
1184 error.PathNotFound,1377 error.FileNotFound,
1185 error.SystemResources,1378 error.SystemResources,
1186 error.NoSpaceLeft,1379 error.NoSpaceLeft,
1187 error.PathAlreadyExists,1380 error.PathAlreadyExists,
...@@ -1251,7 +1444,7 @@ pub const Dir = struct {...@@ -1251,7 +1444,7 @@ pub const Dir = struct {
1251 };1444 };
12521445
1253 pub const OpenError = error{1446 pub const OpenError = error{
1254 PathNotFound,1447 FileNotFound,
1255 NotDir,1448 NotDir,
1256 AccessDenied,1449 AccessDenied,
1257 FileTooBig,1450 FileTooBig,
...@@ -1266,9 +1459,11 @@ pub const Dir = struct {...@@ -1266,9 +1459,11 @@ pub const Dir = struct {
1266 PathAlreadyExists,1459 PathAlreadyExists,
1267 OutOfMemory,1460 OutOfMemory,
12681461
1462 /// See https://github.com/ziglang/zig/issues/1396
1269 Unexpected,1463 Unexpected,
1270 };1464 };
12711465
1466 /// TODO remove the allocator requirement from this API
1272 pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {1467 pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
1273 return Dir{1468 return Dir{
1274 .allocator = allocator,1469 .allocator = allocator,
...@@ -1284,7 +1479,6 @@ pub const Dir = struct {...@@ -1284,7 +1479,6 @@ pub const Dir = struct {
1284 },1479 },
1285 Os.macosx, Os.ios => Handle{1480 Os.macosx, Os.ios => Handle{
1286 .fd = try posixOpen(1481 .fd = try posixOpen(
1287 allocator,
1288 dir_path,1482 dir_path,
1289 posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,1483 posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,
1290 0,1484 0,
...@@ -1296,7 +1490,6 @@ pub const Dir = struct {...@@ -1296,7 +1490,6 @@ pub const Dir = struct {
1296 },1490 },
1297 Os.linux => Handle{1491 Os.linux => Handle{
1298 .fd = try posixOpen(1492 .fd = try posixOpen(
1299 allocator,
1300 dir_path,1493 dir_path,
1301 posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC,1494 posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC,
1302 0,1495 0,
...@@ -1493,39 +1686,32 @@ pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void {...@@ -1493,39 +1686,32 @@ pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void {
1493}1686}
14941687
1495/// Read value of a symbolic link.1688/// Read value of a symbolic link.
1496pub fn readLink(allocator: *Allocator, pathname: []const u8) ![]u8 {1689/// The return value is a slice of out_buffer.
1497 const path_buf = try allocator.alloc(u8, pathname.len + 1);1690pub fn readLinkC(out_buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 {
1498 defer allocator.free(path_buf);1691 const rc = posix.readlink(pathname, out_buffer, out_buffer.len);
14991692 const err = posix.getErrno(rc);
1500 mem.copy(u8, path_buf, pathname);1693 switch (err) {
1501 path_buf[pathname.len] = 0;1694 0 => return out_buffer[0..rc],
15021695 posix.EACCES => return error.AccessDenied,
1503 var result_buf = try allocator.alloc(u8, 1024);1696 posix.EFAULT => unreachable,
1504 errdefer allocator.free(result_buf);1697 posix.EINVAL => unreachable,
1505 while (true) {1698 posix.EIO => return error.FileSystem,
1506 const ret_val = posix.readlink(path_buf.ptr, result_buf.ptr, result_buf.len);1699 posix.ELOOP => return error.SymLinkLoop,
1507 const err = posix.getErrno(ret_val);1700 posix.ENAMETOOLONG => unreachable, // out_buffer is at least PATH_MAX
1508 if (err > 0) {1701 posix.ENOENT => return error.FileNotFound,
1509 return switch (err) {1702 posix.ENOMEM => return error.SystemResources,
1510 posix.EACCES => error.AccessDenied,1703 posix.ENOTDIR => return error.NotDir,
1511 posix.EFAULT, posix.EINVAL => unreachable,1704 else => return unexpectedErrorPosix(err),
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);
1526 }1705 }
1527}1706}
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
1529pub fn posix_setuid(uid: u32) !void {1715pub fn posix_setuid(uid: u32) !void {
1530 const err = posix.getErrno(posix.setuid(uid));1716 const err = posix.getErrno(posix.setuid(uid));
1531 if (err == 0) return;1717 if (err == 0) return;
...@@ -1572,6 +1758,8 @@ pub fn posix_setregid(rgid: u32, egid: u32) !void {...@@ -1572,6 +1758,8 @@ pub fn posix_setregid(rgid: u32, egid: u32) !void {
15721758
1573pub const WindowsGetStdHandleErrs = error{1759pub const WindowsGetStdHandleErrs = error{
1574 NoStdHandles,1760 NoStdHandles,
1761
1762 /// See https://github.com/ziglang/zig/issues/1396
1575 Unexpected,1763 Unexpected,
1576};1764};
15771765
...@@ -1899,7 +2087,7 @@ pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {...@@ -1899,7 +2087,7 @@ pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {
1899/// Call this when you made a windows DLL call or something that does SetLastError2087/// Call this when you made a windows DLL call or something that does SetLastError
1900/// and you get an unexpected error.2088/// and you get an unexpected error.
1901pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {2089pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
1902 if (unexpected_error_tracing) {2090 if (true) {
1903 debug.warn("unexpected GetLastError(): {}\n", err);2091 debug.warn("unexpected GetLastError(): {}\n", err);
1904 debug.dumpCurrentStackTrace(null);2092 debug.dumpCurrentStackTrace(null);
1905 }2093 }
...@@ -1908,17 +2096,12 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {...@@ -1908,17 +2096,12 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
19082096
1909pub fn openSelfExe() !os.File {2097pub fn openSelfExe() !os.File {
1910 switch (builtin.os) {2098 switch (builtin.os) {
1911 Os.linux => {2099 Os.linux => return os.File.openReadC(c"/proc/self/exe"),
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 },
1917 Os.macosx, Os.ios => {2100 Os.macosx, Os.ios => {
1918 var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;2101 var buf: [MAX_PATH_BYTES]u8 = undefined;
1919 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);2102 const self_exe_path = try selfExePath(&buf);
1920 const self_exe_path = try selfExePath(&fixed_allocator.allocator);2103 buf[self_exe_path.len] = 0;
1921 return os.File.openRead(&fixed_allocator.allocator, self_exe_path);2104 return os.File.openReadC(self_exe_path.ptr);
1922 },2105 },
1923 else => @compileError("Unsupported OS"),2106 else => @compileError("Unsupported OS"),
1924 }2107 }
...@@ -1927,7 +2110,7 @@ pub fn openSelfExe() !os.File {...@@ -1927,7 +2110,7 @@ pub fn openSelfExe() !os.File {
1927test "openSelfExe" {2110test "openSelfExe" {
1928 switch (builtin.os) {2111 switch (builtin.os) {
1929 Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),2112 Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
1930 else => return, // Unsupported OS.2113 else => return error.SkipZigTest, // Unsupported OS
1931 }2114 }
1932}2115}
19332116
...@@ -1936,69 +2119,68 @@ test "openSelfExe" {...@@ -1936,69 +2119,68 @@ test "openSelfExe" {
1936/// If you only want an open file handle, use openSelfExe.2119/// If you only want an open file handle, use openSelfExe.
1937/// This function may return an error if the current executable2120/// This function may return an error if the current executable
1938/// was deleted after spawning.2121/// was deleted after spawning.
1939/// Caller owns returned memory.2122/// Returned value is a slice of out_buffer.
1940pub fn selfExePath(allocator: *mem.Allocator) ![]u8 {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 {
1941 switch (builtin.os) {2128 switch (builtin.os) {
1942 Os.linux => {2129 Os.linux => return readLink(out_buffer, "/proc/self/exe"),
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 },
1947 Os.windows => {2130 Os.windows => {
1948 var out_path = try Buffer.initSize(allocator, 0xff);2131 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
1949 errdefer out_path.deinit();2132 const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast
1950 while (true) {2133 const rc = windows.GetModuleFileNameW(null, &utf16le_buf, casted_len);
1951 const dword_len = try math.cast(windows.DWORD, out_path.len());2134 assert(rc <= utf16le_buf.len);
1952 const copied_amt = windows.GetModuleFileNameA(null, out_path.ptr(), dword_len);2135 if (rc == 0) {
1953 if (copied_amt <= 0) {2136 const err = windows.GetLastError();
1954 const err = windows.GetLastError();2137 switch (err) {
1955 return switch (err) {2138 else => return unexpectedErrorWindows(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();
1962 }2139 }
1963 const new_len = (out_path.len() << 1) | 0b1;
1964 try out_path.resize(new_len);
1965 }2140 }
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];
1966 },2145 },
1967 Os.macosx, Os.ios => {2146 Os.macosx, Os.ios => {
1968 var u32_len: u32 = 0;2147 var u32_len: u32 = @intCast(u32, out_buffer.len); // TODO shouldn't need this cast
1969 const ret1 = c._NSGetExecutablePath(undefined, &u32_len);2148 const rc = c._NSGetExecutablePath(out_buffer, &u32_len);
1970 assert(ret1 != 0);2149 if (rc != 0) return error.NameTooLong;
1971 const bytes = try allocator.alloc(u8, u32_len);2150 return mem.toSlice(u8, out_buffer);
1972 errdefer allocator.free(bytes);
1973 const ret2 = c._NSGetExecutablePath(bytes.ptr, &u32_len);
1974 assert(ret2 == 0);
1975 return bytes;
1976 },2151 },
1977 else => @compileError("Unsupported OS"),2152 else => @compileError("Unsupported OS"),
1978 }2153 }
1979}2154}
19802155
1981/// Get the directory path that contains the current executable.2156/// `selfExeDirPath` except allocates the result on the heap.
1982/// Caller owns returned memory.2157/// 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 {
1984 switch (builtin.os) {2166 switch (builtin.os) {
1985 Os.linux => {2167 Os.linux => {
1986 // If the currently executing binary has been deleted,2168 // If the currently executing binary has been deleted,
1987 // the file path looks something like `/a/b/c/exe (deleted)`2169 // the file path looks something like `/a/b/c/exe (deleted)`
1988 // This path cannot be opened, but it's valid for determining the directory2170 // This path cannot be opened, but it's valid for determining the directory
1989 // the executable was in when it was run.2171 // the executable was in when it was run.
1990 const full_exe_path = try readLink(allocator, "/proc/self/exe");2172 const full_exe_path = try readLinkC(out_buffer, c"/proc/self/exe");
1991 errdefer allocator.free(full_exe_path);2173 // Assume that /proc/self/exe has an absolute path, and therefore dirname
1992 const dir = path.dirname(full_exe_path) orelse ".";2174 // will not return null.
1993 return allocator.shrink(u8, full_exe_path, dir.len);2175 return path.dirname(full_exe_path).?;
1994 },2176 },
1995 Os.windows, Os.macosx, Os.ios => {2177 Os.windows, Os.macosx, Os.ios => {
1996 const self_exe_path = try selfExePath(allocator);2178 const self_exe_path = try selfExePath(out_buffer);
1997 errdefer allocator.free(self_exe_path);2179 // Assume that the OS APIs return absolute paths, and therefore dirname
1998 const dirname = os.path.dirname(self_exe_path) orelse ".";2180 // will not return null.
1999 return allocator.shrink(u8, self_exe_path, dirname.len);2181 return path.dirname(self_exe_path).?;
2000 },2182 },
2001 else => @compileError("unimplemented: std.os.selfExeDirPath for " ++ @tagName(builtin.os)),2183 else => @compileError("Unsupported OS"),
2002 }2184 }
2003}2185}
20042186
...@@ -2102,6 +2284,7 @@ pub const PosixBindError = error{...@@ -2102,6 +2284,7 @@ pub const PosixBindError = error{
2102 /// The socket inode would reside on a read-only filesystem.2284 /// The socket inode would reside on a read-only filesystem.
2103 ReadOnlyFileSystem,2285 ReadOnlyFileSystem,
21042286
2287 /// See https://github.com/ziglang/zig/issues/1396
2105 Unexpected,2288 Unexpected,
2106};2289};
21072290
...@@ -2145,6 +2328,7 @@ const PosixListenError = error{...@@ -2145,6 +2328,7 @@ const PosixListenError = error{
2145 /// The socket is not of a type that supports the listen() operation.2328 /// The socket is not of a type that supports the listen() operation.
2146 OperationNotSupported,2329 OperationNotSupported,
21472330
2331 /// See https://github.com/ziglang/zig/issues/1396
2148 Unexpected,2332 Unexpected,
2149};2333};
21502334
...@@ -2198,6 +2382,7 @@ pub const PosixAcceptError = error{...@@ -2198,6 +2382,7 @@ pub const PosixAcceptError = error{
2198 /// Firewall rules forbid connection.2382 /// Firewall rules forbid connection.
2199 BlockedByFirewall,2383 BlockedByFirewall,
22002384
2385 /// See https://github.com/ziglang/zig/issues/1396
2201 Unexpected,2386 Unexpected,
2202};2387};
22032388
...@@ -2243,6 +2428,7 @@ pub const LinuxEpollCreateError = error{...@@ -2243,6 +2428,7 @@ pub const LinuxEpollCreateError = error{
2243 /// There was insufficient memory to create the kernel object.2428 /// There was insufficient memory to create the kernel object.
2244 SystemResources,2429 SystemResources,
22452430
2431 /// See https://github.com/ziglang/zig/issues/1396
2246 Unexpected,2432 Unexpected,
2247};2433};
22482434
...@@ -2297,6 +2483,7 @@ pub const LinuxEpollCtlError = error{...@@ -2297,6 +2483,7 @@ pub const LinuxEpollCtlError = error{
2297 /// for example, a regular file or a directory.2483 /// for example, a regular file or a directory.
2298 FileDescriptorIncompatibleWithEpoll,2484 FileDescriptorIncompatibleWithEpoll,
22992485
2486 /// See https://github.com/ziglang/zig/issues/1396
2300 Unexpected,2487 Unexpected,
2301};2488};
23022489
...@@ -2339,6 +2526,7 @@ pub const LinuxEventFdError = error{...@@ -2339,6 +2526,7 @@ pub const LinuxEventFdError = error{
2339 ProcessFdQuotaExceeded,2526 ProcessFdQuotaExceeded,
2340 SystemFdQuotaExceeded,2527 SystemFdQuotaExceeded,
23412528
2529 /// See https://github.com/ziglang/zig/issues/1396
2342 Unexpected,2530 Unexpected,
2343};2531};
23442532
...@@ -2361,6 +2549,7 @@ pub const PosixGetSockNameError = error{...@@ -2361,6 +2549,7 @@ pub const PosixGetSockNameError = error{
2361 /// Insufficient resources were available in the system to perform the operation.2549 /// Insufficient resources were available in the system to perform the operation.
2362 SystemResources,2550 SystemResources,
23632551
2552 /// See https://github.com/ziglang/zig/issues/1396
2364 Unexpected,2553 Unexpected,
2365};2554};
23662555
...@@ -2414,6 +2603,7 @@ pub const PosixConnectError = error{...@@ -2414,6 +2603,7 @@ pub const PosixConnectError = error{
2414 /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.2603 /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
2415 ConnectionTimedOut,2604 ConnectionTimedOut,
24162605
2606 /// See https://github.com/ziglang/zig/issues/1396
2417 Unexpected,2607 Unexpected,
2418};2608};
24192609
...@@ -2516,26 +2706,66 @@ pub const Thread = struct {...@@ -2516,26 +2706,66 @@ pub const Thread = struct {
2516 data: Data,2706 data: Data,
25172707
2518 pub const use_pthreads = is_posix and builtin.link_libc;2708 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
2519 pub const Data = if (use_pthreads)2729 pub const Data = if (use_pthreads)
2520 struct {2730 struct {
2521 handle: c.pthread_t,2731 handle: Thread.Handle,
2522 stack_addr: usize,2732 stack_addr: usize,
2523 stack_len: usize,2733 stack_len: usize,
2524 }2734 }
2525 else switch (builtin.os) {2735 else switch (builtin.os) {
2526 builtin.Os.linux => struct {2736 builtin.Os.linux => struct {
2527 pid: i32,2737 handle: Thread.Handle,
2528 stack_addr: usize,2738 stack_addr: usize,
2529 stack_len: usize,2739 stack_len: usize,
2530 },2740 },
2531 builtin.Os.windows => struct {2741 builtin.Os.windows => struct {
2532 handle: windows.HANDLE,2742 handle: Thread.Handle,
2533 alloc_start: *c_void,2743 alloc_start: *c_void,
2534 heap_handle: windows.HANDLE,2744 heap_handle: windows.HANDLE,
2535 },2745 },
2536 else => @compileError("Unsupported OS"),2746 else => @compileError("Unsupported OS"),
2537 };2747 };
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
2539 pub fn wait(self: *const Thread) void {2769 pub fn wait(self: *const Thread) void {
2540 if (use_pthreads) {2770 if (use_pthreads) {
2541 const err = c.pthread_join(self.data.handle, null);2771 const err = c.pthread_join(self.data.handle, null);
...@@ -2550,9 +2780,9 @@ pub const Thread = struct {...@@ -2550,9 +2780,9 @@ pub const Thread = struct {
2550 } else switch (builtin.os) {2780 } else switch (builtin.os) {
2551 builtin.Os.linux => {2781 builtin.Os.linux => {
2552 while (true) {2782 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);
2554 if (pid_value == 0) break;2784 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);
2556 switch (linux.getErrno(rc)) {2786 switch (linux.getErrno(rc)) {
2557 0 => continue,2787 0 => continue,
2558 posix.EINTR => continue,2788 posix.EINTR => continue,
...@@ -2595,6 +2825,7 @@ pub const SpawnThreadError = error{...@@ -2595,6 +2825,7 @@ pub const SpawnThreadError = error{
2595 /// Not enough userland memory to spawn the thread.2825 /// Not enough userland memory to spawn the thread.
2596 OutOfMemory,2826 OutOfMemory,
25972827
2828 /// See https://github.com/ziglang/zig/issues/1396
2598 Unexpected,2829 Unexpected,
2599};2830};
26002831
...@@ -2734,7 +2965,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -2734,7 +2965,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
2734 // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly2965 // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
2735 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;2966 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;
2736 const newtls: usize = 0;2967 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);
2738 const err = posix.getErrno(rc);2969 const err = posix.getErrno(rc);
2739 switch (err) {2970 switch (err) {
2740 0 => return thread_ptr,2971 0 => return thread_ptr,
...@@ -2770,7 +3001,9 @@ pub fn posixFStat(fd: i32) !posix.Stat {...@@ -2770,7 +3001,9 @@ pub fn posixFStat(fd: i32) !posix.Stat {
2770 const err = posix.getErrno(posix.fstat(fd, &stat));3001 const err = posix.getErrno(posix.fstat(fd, &stat));
2771 if (err > 0) {3002 if (err > 0) {
2772 return switch (err) {3003 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,
2774 posix.ENOMEM => error.SystemResources,3007 posix.ENOMEM => error.SystemResources,
2775 else => os.unexpectedErrorPosix(err),3008 else => os.unexpectedErrorPosix(err),
2776 };3009 };
...@@ -2782,6 +3015,8 @@ pub fn posixFStat(fd: i32) !posix.Stat {...@@ -2782,6 +3015,8 @@ pub fn posixFStat(fd: i32) !posix.Stat {
2782pub const CpuCountError = error{3015pub const CpuCountError = error{
2783 OutOfMemory,3016 OutOfMemory,
2784 PermissionDenied,3017 PermissionDenied,
3018
3019 /// See https://github.com/ziglang/zig/issues/1396
2785 Unexpected,3020 Unexpected,
2786};3021};
27873022
...@@ -2852,6 +3087,7 @@ pub const BsdKQueueError = error{...@@ -2852,6 +3087,7 @@ pub const BsdKQueueError = error{
2852 /// The system-wide limit on the total number of open files has been reached.3087 /// The system-wide limit on the total number of open files has been reached.
2853 SystemFdQuotaExceeded,3088 SystemFdQuotaExceeded,
28543089
3090 /// See https://github.com/ziglang/zig/issues/1396
2855 Unexpected,3091 Unexpected,
2856};3092};
28573093
...@@ -2903,3 +3139,44 @@ pub fn bsdKEvent(...@@ -2903,3 +3139,44 @@ pub fn bsdKEvent(
2903 }3139 }
2904 }3140 }
2905}3141}
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;...@@ -567,6 +567,37 @@ pub const MNT_DETACH = 2;
567pub const MNT_EXPIRE = 4;567pub const MNT_EXPIRE = 4;
568pub const UMOUNT_NOFOLLOW = 8;568pub 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
570pub const S_IFMT = 0o170000;601pub const S_IFMT = 0o170000;
571602
572pub const S_IFDIR = 0o040000;603pub const S_IFDIR = 0o040000;
...@@ -692,6 +723,10 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us...@@ -692,6 +723,10 @@ pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?*timespec) us
692 return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));723 return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));
693}724}
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
695pub fn getcwd(buf: [*]u8, size: usize) usize {730pub fn getcwd(buf: [*]u8, size: usize) usize {
696 return syscall2(SYS_getcwd, @ptrToInt(buf), size);731 return syscall2(SYS_getcwd, @ptrToInt(buf), size);
697}732}
...@@ -700,6 +735,18 @@ pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize {...@@ -700,6 +735,18 @@ pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize {
700 return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count);735 return syscall3(SYS_getdents, @intCast(usize, fd), @ptrToInt(dirp), count);
701}736}
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
703pub fn isatty(fd: i32) bool {750pub fn isatty(fd: i32) bool {
704 var wsz: winsize = undefined;751 var wsz: winsize = undefined;
705 return syscall3(SYS_ioctl, @intCast(usize, fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;752 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 {...@@ -742,6 +789,14 @@ pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
742 return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count);789 return syscall3(SYS_read, @intCast(usize, fd), @ptrToInt(buf), count);
743}790}
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
745// TODO https://github.com/ziglang/zig/issues/265800// TODO https://github.com/ziglang/zig/issues/265
746pub fn rmdir(path: [*]const u8) usize {801pub fn rmdir(path: [*]const u8) usize {
747 return syscall1(SYS_rmdir, @ptrToInt(path));802 return syscall1(SYS_rmdir, @ptrToInt(path));
...@@ -947,6 +1002,10 @@ pub fn getpid() i32 {...@@ -947,6 +1002,10 @@ pub fn getpid() i32 {
947 return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid)));1002 return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid)));
948}1003}
9491004
1005pub fn gettid() i32 {
1006 return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid)));
1007}
1008
950pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize {1009pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize {
951 return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8);1010 return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8);
952}1011}
...@@ -1060,6 +1119,11 @@ pub const iovec = extern struct {...@@ -1060,6 +1119,11 @@ pub const iovec = extern struct {
1060 iov_len: usize,1119 iov_len: usize,
1061};1120};
10621121
1122pub const iovec_const = extern struct {
1123 iov_base: [*]const u8,
1124 iov_len: usize,
1125};
1126
1063pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {1127pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
1064 return syscall3(SYS_getsockname, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len));1128 return syscall3(SYS_getsockname, @intCast(usize, fd), @ptrToInt(addr), @ptrToInt(len));
1065}1129}
...@@ -1368,6 +1432,14 @@ pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize {...@@ -1368,6 +1432,14 @@ pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize {
1368 return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));1432 return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
1369}1433}
13701434
1435pub const inotify_event = extern struct {
1436 wd: i32,
1437 mask: u32,
1438 cookie: u32,
1439 len: u32,
1440 //name: [?]u8,
1441};
1442
1371test "import" {1443test "import" {
1372 if (builtin.os == builtin.Os.linux) {1444 if (builtin.os == builtin.Os.linux) {
1373 _ = @import("test.zig");1445 _ = @import("test.zig");
std/os/path.zig+138-99
...@@ -11,11 +11,14 @@ const math = std.math;...@@ -11,11 +11,14 @@ const math = std.math;
11const posix = os.posix;11const posix = os.posix;
12const windows = os.windows;12const windows = os.windows;
13const cstr = std.cstr;13const cstr = std.cstr;
14const windows_util = @import("windows/util.zig");
1415
15pub const sep_windows = '\\';16pub const sep_windows = '\\';
16pub const sep_posix = '/';17pub const sep_posix = '/';
17pub const sep = if (is_windows) sep_windows else sep_posix;18pub const sep = if (is_windows) sep_windows else sep_posix;
1819
20pub const sep_str = [1]u8{sep};
21
19pub const delimiter_windows = ';';22pub const delimiter_windows = ';';
20pub const delimiter_posix = ':';23pub const delimiter_posix = ':';
21pub const delimiter = if (is_windows) delimiter_windows else delimiter_posix;24pub const delimiter = if (is_windows) delimiter_windows else delimiter_posix;
...@@ -337,7 +340,7 @@ pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -337,7 +340,7 @@ pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 {
337pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {340pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
338 if (paths.len == 0) {341 if (paths.len == 0) {
339 assert(is_windows); // resolveWindows called on non windows can't use getCwd342 assert(is_windows); // resolveWindows called on non windows can't use getCwd
340 return os.getCwd(allocator);343 return os.getCwdAlloc(allocator);
341 }344 }
342345
343 // determine which disk designator we will result with, if any346 // determine which disk designator we will result with, if any
...@@ -432,7 +435,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -432,7 +435,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
432 },435 },
433 WindowsPath.Kind.None => {436 WindowsPath.Kind.None => {
434 assert(is_windows); // resolveWindows called on non windows can't use getCwd437 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);
436 defer allocator.free(cwd);439 defer allocator.free(cwd);
437 const parsed_cwd = windowsParsePath(cwd);440 const parsed_cwd = windowsParsePath(cwd);
438 result = try allocator.alloc(u8, max_size + parsed_cwd.disk_designator.len + 1);441 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 {...@@ -448,7 +451,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
448 } else {451 } else {
449 assert(is_windows); // resolveWindows called on non windows can't use getCwd452 assert(is_windows); // resolveWindows called on non windows can't use getCwd
450 // TODO call get cwd for the result_disk_designator instead of the global one453 // 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);
452 defer allocator.free(cwd);455 defer allocator.free(cwd);
453456
454 result = try allocator.alloc(u8, max_size + cwd.len + 1);457 result = try allocator.alloc(u8, max_size + cwd.len + 1);
...@@ -506,7 +509,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -506,7 +509,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
506 result_index += 1;509 result_index += 1;
507 }510 }
508511
509 return result[0..result_index];512 return allocator.shrink(u8, result, result_index);
510}513}
511514
512/// This function is like a series of `cd` statements executed one after another.515/// 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 {...@@ -516,7 +519,7 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
516pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {519pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
517 if (paths.len == 0) {520 if (paths.len == 0) {
518 assert(!is_windows); // resolvePosix called on windows can't use getCwd521 assert(!is_windows); // resolvePosix called on windows can't use getCwd
519 return os.getCwd(allocator);522 return os.getCwdAlloc(allocator);
520 }523 }
521524
522 var first_index: usize = 0;525 var first_index: usize = 0;
...@@ -538,7 +541,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -538,7 +541,7 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
538 result = try allocator.alloc(u8, max_size);541 result = try allocator.alloc(u8, max_size);
539 } else {542 } else {
540 assert(!is_windows); // resolvePosix called on windows can't use getCwd543 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);
542 defer allocator.free(cwd);545 defer allocator.free(cwd);
543 result = try allocator.alloc(u8, max_size + cwd.len + 1);546 result = try allocator.alloc(u8, max_size + cwd.len + 1);
544 mem.copy(u8, result, cwd);547 mem.copy(u8, result, cwd);
...@@ -573,11 +576,11 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -573,11 +576,11 @@ pub fn resolvePosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
573 result_index += 1;576 result_index += 1;
574 }577 }
575578
576 return result[0..result_index];579 return allocator.shrink(u8, result, result_index);
577}580}
578581
579test "os.path.resolve" {582test "os.path.resolve" {
580 const cwd = try os.getCwd(debug.global_allocator);583 const cwd = try os.getCwdAlloc(debug.global_allocator);
581 if (is_windows) {584 if (is_windows) {
582 if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) {585 if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) {
583 cwd[0] = asciiUpper(cwd[0]);586 cwd[0] = asciiUpper(cwd[0]);
...@@ -591,7 +594,7 @@ test "os.path.resolve" {...@@ -591,7 +594,7 @@ test "os.path.resolve" {
591594
592test "os.path.resolveWindows" {595test "os.path.resolveWindows" {
593 if (is_windows) {596 if (is_windows) {
594 const cwd = try os.getCwd(debug.global_allocator);597 const cwd = try os.getCwdAlloc(debug.global_allocator);
595 const parsed_cwd = windowsParsePath(cwd);598 const parsed_cwd = windowsParsePath(cwd);
596 {599 {
597 const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" });600 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...@@ -1073,112 +1076,148 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons
1073 assert(mem.eql(u8, result, expected_output));1076 assert(mem.eql(u8, result, expected_output));
1074}1077}
10751078
1076/// Return the canonicalized absolute pathname.1079pub const RealError = error{
1077/// Expands all symbolic links and resolves references to `.`, `..`, and1080 FileNotFound,
1078/// extra `/` characters in ::pathname.1081 AccessDenied,
1079/// Caller must deallocate result.1082 NameTooLong,
1080pub fn real(allocator: *Allocator, pathname: []const u8) ![]u8 {1083 NotSupported,
1081 switch (builtin.os) {1084 NotDir,
1082 Os.windows => {1085 SymLinkLoop,
1083 const pathname_buf = try allocator.alloc(u8, pathname.len + 1);1086 InputOutput,
1084 defer allocator.free(pathname_buf);1087 FileTooBig,
10851088 IsDir,
1086 mem.copy(u8, pathname_buf, pathname);1089 ProcessFdQuotaExceeded,
1087 pathname_buf[pathname.len] = 0;1090 SystemFdQuotaExceeded,
10881091 NoDevice,
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);1092 SystemResources,
1090 if (h_file == windows.INVALID_HANDLE_VALUE) {1093 NoSpaceLeft,
1091 const err = windows.GetLastError();1094 FileSystem,
1092 return switch (err) {1095 BadPathName,
1093 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,1096
1094 windows.ERROR.ACCESS_DENIED => error.AccessDenied,1097 /// On Windows, file paths must be valid Unicode.
1095 windows.ERROR.FILENAME_EXCED_RANGE => error.NameTooLong,1098 InvalidUtf8,
1096 else => os.unexpectedErrorWindows(err),1099
1097 };1100 /// TODO remove this possibility
1098 }1101 PathAlreadyExists,
1099 defer os.close(h_file);1102
1100 var buf = try allocator.alloc(u8, 256);1103 /// TODO remove this possibility
1101 errdefer allocator.free(buf);1104 Unexpected,
1102 while (true) {1105};
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 }
11151106
1116 if (result > buf.len) {1107/// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
1117 buf = try allocator.realloc(u8, buf, result);1108/// Otherwise use `real` or `realC`.
1118 continue;1109pub fn realW(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u16) RealError![]u8 {
1119 }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 path1146 // windows returns \\?\ prepended to the path
1122 // we strip it because nobody wants \\?\ prepended to their path1147 // we strip it because nobody wants \\?\ prepended to their path
1123 const final_len = x: {1148 const prefix = []u16{ '\\', '\\', '?', '\\' };
1124 if (result > 4 and mem.startsWith(u8, buf, "\\\\?\\")) {1149 const start_index = if (mem.startsWith(u16, utf16le_slice, prefix)) prefix.len else 0;
1125 var i: usize = 4;1150
1126 while (i < result) : (i += 1) {1151 // Trust that Windows gives us valid UTF-16LE.
1127 buf[i - 4] = buf[i];1152 const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice[start_index..]) catch unreachable;
1128 }1153 return out_buffer[0..end_index];
1129 break :x result - 4;1154}
1130 } else {1155
1131 break :x result;1156/// See `real`
1132 }1157/// Use this when you have a null terminated pointer path.
1133 };1158pub fn realC(out_buffer: *[os.MAX_PATH_BYTES]u8, pathname: [*]const u8) RealError![]u8 {
11341159 switch (builtin.os) {
1135 return allocator.shrink(u8, buf, final_len);1160 Os.windows => {
1136 }1161 const pathname_w = try windows_util.cStrToPrefixedFileW(pathname);
1162 return realW(out_buffer, pathname_w);
1137 },1163 },
1138 Os.macosx, Os.ios => {1164 Os.macosx, Os.ios => {
1139 // TODO instead of calling the libc function here, port the implementation1165 // TODO instead of calling the libc function here, port the implementation to Zig
1140 // to Zig, and then remove the NameTooLong error possibility.1166 const err = posix.getErrno(posix.realpath(pathname, out_buffer));
1141 const pathname_buf = try allocator.alloc(u8, pathname.len + 1);1167 switch (err) {
1142 defer allocator.free(pathname_buf);1168 0 => return mem.toSlice(u8, out_buffer),
11431169 posix.EINVAL => unreachable,
1144 const result_buf = try allocator.alloc(u8, posix.PATH_MAX);1170 posix.EBADF => unreachable,
1145 errdefer allocator.free(result_buf);1171 posix.EFAULT => unreachable,
11461172 posix.EACCES => return error.AccessDenied,
1147 mem.copy(u8, pathname_buf, pathname);1173 posix.ENOENT => return error.FileNotFound,
1148 pathname_buf[pathname.len] = 0;1174 posix.ENOTSUP => return error.NotSupported,
11491175 posix.ENOTDIR => return error.NotDir,
1150 const err = posix.getErrno(posix.realpath(pathname_buf.ptr, result_buf.ptr));1176 posix.ENAMETOOLONG => return error.NameTooLong,
1151 if (err > 0) {1177 posix.ELOOP => return error.SymLinkLoop,
1152 return switch (err) {1178 posix.EIO => return error.InputOutput,
1153 posix.EINVAL => unreachable,1179 else => return os.unexpectedErrorPosix(err),
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 }1180 }
1166 return allocator.shrink(u8, result_buf, cstr.len(result_buf.ptr));
1167 },1181 },
1168 Os.linux => {1182 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);
1170 defer os.close(fd);1184 defer os.close(fd);
11711185
1172 var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;1186 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);
1176 },1190 },
1177 else => @compileError("TODO implement os.path.real for " ++ @tagName(builtin.os)),1191 else => @compileError("TODO implement os.path.real for " ++ @tagName(builtin.os)),
1178 }1192 }
1179}1193}
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
1181test "os.path.real" {1219test "os.path.real" {
1182 // at least call it so it gets compiled1220 // 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);
1184}1223}
std/os/test.zig+25-8
...@@ -10,30 +10,47 @@ const AtomicRmwOp = builtin.AtomicRmwOp;...@@ -10,30 +10,47 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
10const AtomicOrder = builtin.AtomicOrder;10const AtomicOrder = builtin.AtomicOrder;
1111
12test "makePath, put some files in it, deleteTree" {12test "makePath, put some files in it, deleteTree" {
13 try os.makePath(a, "os_test_tmp/b/c");13 try os.makePath(a, "os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "c");
14 try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense");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(a, "os_test_tmp/b/file2.txt", "blah");15 try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "b" ++ os.path.sep_str ++ "file2.txt", "blah");
16 try os.deleteTree(a, "os_test_tmp");16 try os.deleteTree(a, "os_test_tmp");
17 if (os.Dir.open(a, "os_test_tmp")) |dir| {17 if (os.Dir.open(a, "os_test_tmp")) |dir| {
18 @panic("expected error");18 @panic("expected error");
19 } else |err| {19 } else |err| {
20 assert(err == error.PathNotFound);20 assert(err == error.FileNotFound);
21 }21 }
22}22}
2323
24test "access file" {24test "access file" {
25 try os.makePath(a, "os_test_tmp");25 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| {
27 @panic("expected error");27 @panic("expected error");
28 } else |err| {28 } else |err| {
29 assert(err == error.NotFound);29 assert(err == error.FileNotFound);
30 }30 }
3131
32 try io.writeFile(a, "os_test_tmp/file.txt", "");32 try io.writeFile("os_test_tmp" ++ os.path.sep_str ++ "file.txt", "");
33 try os.File.access(a, "os_test_tmp/file.txt");33 try os.File.access("os_test_tmp" ++ os.path.sep_str ++ "file.txt");
34 try os.deleteTree(a, "os_test_tmp");34 try os.deleteTree(a, "os_test_tmp");
35}35}
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
37test "spawn threads" {54test "spawn threads" {
38 var shared_ctx: i32 = 1;55 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));...@@ -67,8 +67,9 @@ pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD));
67pub const OVERLAPPED = extern struct {67pub const OVERLAPPED = extern struct {
68 Internal: ULONG_PTR,68 Internal: ULONG_PTR,
69 InternalHigh: ULONG_PTR,69 InternalHigh: ULONG_PTR,
70 Pointer: PVOID,70 Offset: DWORD,
71 hEvent: HANDLE,71 OffsetHigh: DWORD,
72 hEvent: ?HANDLE,
72};73};
73pub const LPOVERLAPPED = *OVERLAPPED;74pub const LPOVERLAPPED = *OVERLAPPED;
7475
...@@ -350,3 +351,15 @@ pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));...@@ -350,3 +351,15 @@ pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005));
350pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));351pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006));
351pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));352pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E));
352pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057));353pub 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 @@...@@ -1,14 +1,24 @@
1use @import("index.zig");1use @import("index.zig");
22
3pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL;
4
3pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;5pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
46
5pub extern "kernel32" stdcallcc fn CreateDirectoryA(7pub extern "kernel32" stdcallcc fn CreateDirectoryA(lpPathName: [*]const u8, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
6 lpPathName: LPCSTR,8pub extern "kernel32" stdcallcc fn CreateDirectoryW(lpPathName: [*]const u16, lpSecurityAttributes: ?*SECURITY_ATTRIBUTES) BOOL;
7 lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
8) BOOL;
99
10pub extern "kernel32" stdcallcc fn CreateFileA(10pub 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
12 dwDesiredAccess: DWORD,22 dwDesiredAccess: DWORD,
13 dwShareMode: DWORD,23 dwShareMode: DWORD,
14 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,24 lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
...@@ -47,7 +57,8 @@ pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, Ex...@@ -47,7 +57,8 @@ pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, Ex
4757
48pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;58pub 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
52pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;63pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
5364
...@@ -61,7 +72,11 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;...@@ -61,7 +72,11 @@ pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
6172
62pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;73pub 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
66pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;81pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
6782
...@@ -71,9 +86,11 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo...@@ -71,9 +86,11 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
7186
72pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;87pub 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
78pub extern "kernel32" stdcallcc fn GetLastError() DWORD;95pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
7996
...@@ -91,6 +108,15 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(...@@ -91,6 +108,15 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
91 dwFlags: DWORD,108 dwFlags: DWORD,
92) DWORD;109) 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
94pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;120pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
95pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;121pub 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...@@ -101,7 +127,6 @@ pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: S
101pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;127pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
102pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;128pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
103pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;129pub 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;
105pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;130pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
106pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;131pub 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...@@ -111,9 +136,17 @@ pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBy
111136
112pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;137pub 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
114pub extern "kernel32" stdcallcc fn MoveFileExA(141pub extern "kernel32" stdcallcc fn MoveFileExA(
115 lpExistingFileName: LPCSTR,142 lpExistingFileName: [*]const u8,
116 lpNewFileName: LPCSTR,143 lpNewFileName: [*]const u8,
144 dwFlags: DWORD,
145) BOOL;
146
147pub extern "kernel32" stdcallcc fn MoveFileExW(
148 lpExistingFileName: [*]const u16,
149 lpNewFileName: [*]const u16,
117 dwFlags: DWORD,150 dwFlags: DWORD,
118) BOOL;151) BOOL;
119152
...@@ -123,11 +156,22 @@ pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *...@@ -123,11 +156,22 @@ pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *
123156
124pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;157pub 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
126pub extern "kernel32" stdcallcc fn ReadFile(170pub extern "kernel32" stdcallcc fn ReadFile(
127 in_hFile: HANDLE,171 in_hFile: HANDLE,
128 out_lpBuffer: *c_void,172 out_lpBuffer: [*]u8,
129 in_nNumberOfBytesToRead: DWORD,173 in_nNumberOfBytesToRead: DWORD,
130 out_lpNumberOfBytesRead: *DWORD,174 out_lpNumberOfBytesRead: ?*DWORD,
131 in_out_lpOverlapped: ?*OVERLAPPED,175 in_out_lpOverlapped: ?*OVERLAPPED,
132) BOOL;176) BOOL;
133177
...@@ -150,13 +194,41 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis...@@ -150,13 +194,41 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis
150194
151pub extern "kernel32" stdcallcc fn WriteFile(195pub extern "kernel32" stdcallcc fn WriteFile(
152 in_hFile: HANDLE,196 in_hFile: HANDLE,
153 in_lpBuffer: *const c_void,197 in_lpBuffer: [*]const u8,
154 in_nNumberOfBytesToWrite: DWORD,198 in_nNumberOfBytesToWrite: DWORD,
155 out_lpNumberOfBytesWritten: ?*DWORD,199 out_lpNumberOfBytesWritten: ?*DWORD,
156 in_out_lpOverlapped: ?*OVERLAPPED,200 in_out_lpOverlapped: ?*OVERLAPPED,
157) BOOL;201) BOOL;
158202
203pub extern "kernel32" stdcallcc fn WriteFileEx(hFile: HANDLE, lpBuffer: [*]const u8, nNumberOfBytesToWrite: DWORD, lpOverlapped: LPOVERLAPPED, lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE) BOOL;
204
159//TODO: call unicode versions instead of relying on ANSI code page205//TODO: call unicode versions instead of relying on ANSI code page
160pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;206pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
161207
162pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;208pub 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;...@@ -7,9 +7,17 @@ const mem = std.mem;
7const BufMap = std.BufMap;7const BufMap = std.BufMap;
8const cstr = std.cstr;8const 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
10pub const WaitError = error{16pub const WaitError = error{
11 WaitAbandoned,17 WaitAbandoned,
12 WaitTimeOut,18 WaitTimeOut,
19
20 /// See https://github.com/ziglang/zig/issues/1396
13 Unexpected,21 Unexpected,
14};22};
1523
...@@ -36,20 +44,21 @@ pub fn windowsClose(handle: windows.HANDLE) void {...@@ -36,20 +44,21 @@ pub fn windowsClose(handle: windows.HANDLE) void {
36pub const WriteError = error{44pub const WriteError = error{
37 SystemResources,45 SystemResources,
38 OperationAborted,46 OperationAborted,
39 IoPending,
40 BrokenPipe,47 BrokenPipe,
48
49 /// See https://github.com/ziglang/zig/issues/1396
41 Unexpected,50 Unexpected,
42};51};
4352
44pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void {53pub 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) {
46 const err = windows.GetLastError();55 const err = windows.GetLastError();
47 return switch (err) {56 return switch (err) {
48 windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,57 windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,
49 windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,58 windows.ERROR.NOT_ENOUGH_MEMORY => WriteError.SystemResources,
50 windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,59 windows.ERROR.OPERATION_ABORTED => WriteError.OperationAborted,
51 windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,60 windows.ERROR.NOT_ENOUGH_QUOTA => WriteError.SystemResources,
52 windows.ERROR.IO_PENDING => WriteError.IoPending,61 windows.ERROR.IO_PENDING => unreachable,
53 windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,62 windows.ERROR.BROKEN_PIPE => WriteError.BrokenPipe,
54 else => os.unexpectedErrorWindows(err),63 else => os.unexpectedErrorWindows(err),
55 };64 };
...@@ -87,37 +96,51 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool {...@@ -87,37 +96,51 @@ pub fn windowsIsCygwinPty(handle: windows.HANDLE) bool {
87pub const OpenError = error{96pub const OpenError = error{
88 SharingViolation,97 SharingViolation,
89 PathAlreadyExists,98 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.
90 FileNotFound,104 FileNotFound,
105
91 AccessDenied,106 AccessDenied,
92 PipeBusy,107 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
93 Unexpected,118 Unexpected,
94 OutOfMemory,
95};119};
96120
97/// `file_path` needs to be copied in memory to add a null terminating byte, hence the allocator.
98pub fn windowsOpen(121pub fn windowsOpen(
99 allocator: *mem.Allocator,
100 file_path: []const u8,122 file_path: []const u8,
101 desired_access: windows.DWORD,123 desired_access: windows.DWORD,
102 share_mode: windows.DWORD,124 share_mode: windows.DWORD,
103 creation_disposition: windows.DWORD,125 creation_disposition: windows.DWORD,
104 flags_and_attrs: windows.DWORD,126 flags_and_attrs: windows.DWORD,
105) OpenError!windows.HANDLE {127) OpenError!windows.HANDLE {
106 const path_with_null = try cstr.addNullByte(allocator, file_path);128 const file_path_w = try sliceToPrefixedFileW(file_path);
107 defer allocator.free(path_with_null);
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
111 if (result == windows.INVALID_HANDLE_VALUE) {132 if (result == windows.INVALID_HANDLE_VALUE) {
112 const err = windows.GetLastError();133 const err = windows.GetLastError();
113 return switch (err) {134 switch (err) {
114 windows.ERROR.SHARING_VIOLATION => OpenError.SharingViolation,135 windows.ERROR.SHARING_VIOLATION => return OpenError.SharingViolation,
115 windows.ERROR.ALREADY_EXISTS, windows.ERROR.FILE_EXISTS => OpenError.PathAlreadyExists,136 windows.ERROR.ALREADY_EXISTS => return OpenError.PathAlreadyExists,
116 windows.ERROR.FILE_NOT_FOUND => OpenError.FileNotFound,137 windows.ERROR.FILE_EXISTS => return OpenError.PathAlreadyExists,
117 windows.ERROR.ACCESS_DENIED => OpenError.AccessDenied,138 windows.ERROR.FILE_NOT_FOUND => return OpenError.FileNotFound,
118 windows.ERROR.PIPE_BUSY => OpenError.PipeBusy,139 windows.ERROR.PATH_NOT_FOUND => return OpenError.FileNotFound,
119 else => os.unexpectedErrorWindows(err),140 windows.ERROR.ACCESS_DENIED => return OpenError.AccessDenied,
120 };141 windows.ERROR.PIPE_BUSY => return OpenError.PipeBusy,
142 else => return os.unexpectedErrorWindows(err),
143 }
121 }144 }
122145
123 return result;146 return result;
...@@ -193,9 +216,8 @@ pub fn windowsFindFirstFile(...@@ -193,9 +216,8 @@ pub fn windowsFindFirstFile(
193 if (handle == windows.INVALID_HANDLE_VALUE) {216 if (handle == windows.INVALID_HANDLE_VALUE) {
194 const err = windows.GetLastError();217 const err = windows.GetLastError();
195 switch (err) {218 switch (err) {
196 windows.ERROR.FILE_NOT_FOUND,219 windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
197 windows.ERROR.PATH_NOT_FOUND,220 windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
198 => return error.PathNotFound,
199 else => return os.unexpectedErrorWindows(err),221 else => return os.unexpectedErrorWindows(err),
200 }222 }
201 }223 }
...@@ -221,6 +243,7 @@ pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_compl...@@ -221,6 +243,7 @@ pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_compl
221 const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {243 const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
222 const err = windows.GetLastError();244 const err = windows.GetLastError();
223 switch (err) {245 switch (err) {
246 windows.ERROR.INVALID_PARAMETER => unreachable,
224 else => return os.unexpectedErrorWindows(err),247 else => return os.unexpectedErrorWindows(err),
225 }248 }
226 };249 };
...@@ -238,21 +261,55 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_...@@ -238,21 +261,55 @@ pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_
238 }261 }
239}262}
240263
241pub const WindowsWaitResult = error{264pub const WindowsWaitResult = enum {
242 Normal,265 Normal,
243 Aborted,266 Aborted,
267 Cancelled,
244};268};
245269
246pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult {270pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult {
247 if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) {271 if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) {
248 if (std.debug.runtime_safety) {272 const err = windows.GetLastError();
249 const err = windows.GetLastError();273 switch (err) {
250 if (err != windows.ERROR.ABANDONED_WAIT_0) {274 windows.ERROR.ABANDONED_WAIT_0 => return WindowsWaitResult.Aborted,
251 std.debug.warn("err: {}\n", err);275 windows.ERROR.OPERATION_ABORTED => return WindowsWaitResult.Cancelled,
252 }276 else => {
253 assert(err == windows.ERROR.ABANDONED_WAIT_0);277 if (std.debug.runtime_safety) {
278 std.debug.panic("unexpected error: {}\n", err);
279 }
280 },
254 }281 }
255 return WindowsWaitResult.Aborted;
256 }282 }
257 return WindowsWaitResult.Normal;283 return WindowsWaitResult.Normal;
258}284}
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 @@...@@ -1,38 +1,55 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3
1//////////////////////////4//////////////////////////
2//// IPC structures ////5//// IPC structures ////
3//////////////////////////6//////////////////////////
47
5pub const Message = struct {8pub const Message = struct {
6 sender: MailboxId,9sender: MailboxId,
7 receiver: MailboxId,10 receiver: MailboxId,
8 type: usize,11 code: usize,
9 payload: usize,12 args: [5]usize,
13 payload: ?[]const u8,
1014
11 pub fn from(mailbox_id: *const MailboxId) Message {15 pub fn from(mailbox_id: *const MailboxId) Message {
12 return Message{16 return Message {
13 .sender = MailboxId.Undefined,17 .sender = MailboxId.Undefined,
14 .receiver = *mailbox_id,18 .receiver = mailbox_id.*,
15 .type = 0,19 .code = undefined,
16 .payload = 0,20 .args = undefined,
21 .payload = null,
17 };22 };
18 }23 }
1924
20 pub fn to(mailbox_id: *const MailboxId, msg_type: usize) Message {25 pub fn to(mailbox_id: *const MailboxId, msg_code: usize, args: ...) Message {
21 return Message{26 var message = Message {
22 .sender = MailboxId.This,27 .sender = MailboxId.This,
23 .receiver = *mailbox_id,28 .receiver = mailbox_id.*,
24 .type = msg_type,29 .code = msg_code,
25 .payload = 0,30 .args = undefined,
31 .payload = null,
26 };32 };
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;
27 }41 }
2842
29 pub fn withData(mailbox_id: *const MailboxId, msg_type: usize, payload: usize) Message {43 pub fn as(self: *const Message, sender: *const MailboxId) Message {
30 return Message{44 var message = self.*;
31 .sender = MailboxId.This,45 message.sender = sender.*;
32 .receiver = *mailbox_id,46 return message;
33 .type = msg_type,47 }
34 .payload = payload,48
35 };49 pub fn withPayload(self: *const Message, payload: []const u8) Message {
50 var message = self.*;
51 message.payload = payload;
52 return message;
36 }53 }
37};54};
3855
...@@ -63,21 +80,26 @@ pub const STDOUT_FILENO = 1;...@@ -63,21 +80,26 @@ pub const STDOUT_FILENO = 1;
63pub const STDERR_FILENO = 2;80pub const STDERR_FILENO = 2;
6481
65// FIXME: let's borrow Linux's error numbers for now.82// FIXME: let's borrow Linux's error numbers for now.
66pub const getErrno = @import("linux/index.zig").getErrno;
67use @import("linux/errno.zig");83use @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
69// TODO: implement this correctly.90// TODO: implement this correctly.
70pub fn read(fd: i32, buf: *u8, count: usize) usize {91pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
71 switch (fd) {92 switch (fd) {
72 STDIN_FILENO => {93 STDIN_FILENO => {
73 var i: usize = 0;94 var i: usize = 0;
74 while (i < count) : (i += 1) {95 while (i < count) : (i += 1) {
75 send(Message.to(Server.Keyboard, 0));96 send(Message.to(Server.Keyboard, 0));
7697
98 // FIXME: we should be certain that we are receiving from Keyboard.
77 var message = Message.from(MailboxId.This);99 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]);
81 }103 }
82 },104 },
83 else => unreachable,105 else => unreachable,
...@@ -86,13 +108,11 @@ pub fn read(fd: i32, buf: *u8, count: usize) usize {...@@ -86,13 +108,11 @@ pub fn read(fd: i32, buf: *u8, count: usize) usize {
86}108}
87109
88// TODO: implement this correctly.110// 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 {
90 switch (fd) {112 switch (fd) {
91 STDOUT_FILENO, STDERR_FILENO => {113 STDOUT_FILENO, STDERR_FILENO => {
92 var i: usize = 0;114 send(Message.to(Server.Terminal, 1)
93 while (i < count) : (i += 1) {115 .withPayload(buf[0..count]));
94 send(Message.withData(Server.Terminal, 1, buf[i]));
95 }
96 },116 },
97 else => unreachable,117 else => unreachable,
98 }118 }
...@@ -104,17 +124,14 @@ pub fn write(fd: i32, buf: *const u8, count: usize) usize {...@@ -104,17 +124,14 @@ pub fn write(fd: i32, buf: *const u8, count: usize) usize {
104///////////////////////////124///////////////////////////
105125
106pub const Syscall = enum(usize) {126pub const Syscall = enum(usize) {
107 exit = 0,127 exit = 0,
108 createPort = 1,128 send = 1,
109 send = 2,129 receive = 2,
110 receive = 3,130 subscribeIRQ = 3,
111 subscribeIRQ = 4,131 inb = 4,
112 inb = 5,132 outb = 5,
113 map = 6,133 map = 6,
114 createThread = 7,134 createThread = 7,
115 createProcess = 8,
116 wait = 9,
117 portReady = 10,
118};135};
119136
120////////////////////137////////////////////
...@@ -126,13 +143,6 @@ pub fn exit(status: i32) noreturn {...@@ -126,13 +143,6 @@ pub fn exit(status: i32) noreturn {
126 unreachable;143 unreachable;
127}144}
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
136pub fn send(message: *const Message) void {146pub fn send(message: *const Message) void {
137 _ = syscall1(Syscall.send, @ptrToInt(message));147 _ = syscall1(Syscall.send, @ptrToInt(message));
138}148}
...@@ -146,29 +156,21 @@ pub fn subscribeIRQ(irq: u8, mailbox_id: *const MailboxId) void {...@@ -146,29 +156,21 @@ pub fn subscribeIRQ(irq: u8, mailbox_id: *const MailboxId) void {
146}156}
147157
148pub fn inb(port: u16) u8 {158pub 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);
150}164}
151165
152pub fn map(v_addr: usize, p_addr: usize, size: usize, writable: bool) bool {166pub 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;
154}168}
155169
156pub fn createThread(function: fn () void) u16 {170pub fn createThread(function: fn () void) u16 {
157 return u16(syscall1(Syscall.createThread, @ptrToInt(function)));171 return u16(syscall1(Syscall.createThread, @ptrToInt(function)));
158}172}
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
172/////////////////////////174/////////////////////////
173//// Syscall stubs ////175//// Syscall stubs ////
174/////////////////////////176/////////////////////////
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");...@@ -2,7 +2,7 @@ const std = @import("index.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const Allocator = std.mem.Allocator;3const Allocator = std.mem.Allocator;
44
5// Imagine that `fn at(self: &Self, index: usize) &T` is a customer asking for a box5// Imagine that `fn at(self: *Self, index: usize) &T` is a customer asking for a box
6// from a warehouse, based on a flat array, boxes ordered from 0 to N - 1.6// from a warehouse, based on a flat array, boxes ordered from 0 to N - 1.
7// But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes.7// But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes.
8// So when the customer requests a box index, we have to translate it to shelf index8// 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...@@ -93,6 +93,14 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
9393
94 pub const prealloc_count = prealloc_item_count;94 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
96 /// Deinitialize with `deinit`104 /// Deinitialize with `deinit`
97 pub fn init(allocator: *Allocator) Self {105 pub fn init(allocator: *Allocator) Self {
98 return Self{106 return Self{
...@@ -109,7 +117,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -109,7 +117,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
109 self.* = undefined;117 self.* = undefined;
110 }118 }
111119
112 pub fn at(self: *Self, i: usize) *T {120 pub fn at(self: var, i: usize) AtType(@typeOf(self)) {
113 assert(i < self.len);121 assert(i < self.len);
114 return self.uncheckedAt(i);122 return self.uncheckedAt(i);
115 }123 }
...@@ -133,7 +141,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -133,7 +141,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
133 if (self.len == 0) return null;141 if (self.len == 0) return null;
134142
135 const index = self.len - 1;143 const index = self.len - 1;
136 const result = self.uncheckedAt(index).*;144 const result = uncheckedAt(self, index).*;
137 self.len = index;145 self.len = index;
138 return result;146 return result;
139 }147 }
...@@ -141,7 +149,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -141,7 +149,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
141 pub fn addOne(self: *Self) !*T {149 pub fn addOne(self: *Self) !*T {
142 const new_length = self.len + 1;150 const new_length = self.len + 1;
143 try self.growCapacity(new_length);151 try self.growCapacity(new_length);
144 const result = self.uncheckedAt(self.len);152 const result = uncheckedAt(self, self.len);
145 self.len = new_length;153 self.len = new_length;
146 return result;154 return result;
147 }155 }
...@@ -193,7 +201,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type...@@ -193,7 +201,7 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
193 self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, new_cap_shelf_count);201 self.dynamic_segments = self.allocator.shrink([*]T, self.dynamic_segments, new_cap_shelf_count);
194 }202 }
195203
196 pub fn uncheckedAt(self: *Self, index: usize) *T {204 pub fn uncheckedAt(self: var, index: usize) AtType(@typeOf(self)) {
197 if (index < prealloc_item_count) {205 if (index < prealloc_item_count) {
198 return &self.prealloc_segment[index];206 return &self.prealloc_segment[index];
199 }207 }
std/special/bootstrap.zig-6
...@@ -13,17 +13,11 @@ comptime {...@@ -13,17 +13,11 @@ comptime {
13 @export("main", main, strong_linkage);13 @export("main", main, strong_linkage);
14 } else if (builtin.os == builtin.Os.windows) {14 } else if (builtin.os == builtin.Os.windows) {
15 @export("WinMainCRTStartup", WinMainCRTStartup, strong_linkage);15 @export("WinMainCRTStartup", WinMainCRTStartup, strong_linkage);
16 } else if (builtin.os == builtin.Os.zen) {
17 @export("_start", zen_start, strong_linkage);
18 } else {16 } else {
19 @export("_start", _start, strong_linkage);17 @export("_start", _start, strong_linkage);
20 }18 }
21}19}
2220
23extern fn zen_start() noreturn {
24 std.os.posix.exit(@inlineCall(callMain));
25}
26
27nakedcc fn _start() noreturn {21nakedcc fn _start() noreturn {
28 switch (builtin.arch) {22 switch (builtin.arch) {
29 builtin.Arch.x86_64 => {23 builtin.Arch.x86_64 => {
std/special/build_runner.zig+2-2
...@@ -72,10 +72,10 @@ pub fn main() !void {...@@ -72,10 +72,10 @@ pub fn main() !void {
72 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {72 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
73 const option_name = option_contents[0..name_end];73 const option_name = option_contents[0..name_end];
74 const option_value = option_contents[name_end + 1 ..];74 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))
76 return usageAndErr(&builder, false, try stderr_stream);76 return usageAndErr(&builder, false, try stderr_stream);
77 } else {77 } else {
78 if (builder.addUserInputFlag(option_contents))78 if (try builder.addUserInputFlag(option_contents))
79 return usageAndErr(&builder, false, try stderr_stream);79 return usageAndErr(&builder, false, try stderr_stream);
80 }80 }
81 } else if (mem.startsWith(u8, arg, "-")) {81 } else if (mem.startsWith(u8, arg, "-")) {
std/unicode.zig+90-32
...@@ -188,6 +188,7 @@ pub const Utf8View = struct {...@@ -188,6 +188,7 @@ pub const Utf8View = struct {
188 return Utf8View{ .bytes = s };188 return Utf8View{ .bytes = s };
189 }189 }
190190
191 /// TODO: https://github.com/ziglang/zig/issues/425
191 pub fn initComptime(comptime s: []const u8) Utf8View {192 pub fn initComptime(comptime s: []const u8) Utf8View {
192 if (comptime init(s)) |r| {193 if (comptime init(s)) |r| {
193 return r;194 return r;
...@@ -199,7 +200,7 @@ pub const Utf8View = struct {...@@ -199,7 +200,7 @@ pub const Utf8View = struct {
199 }200 }
200 }201 }
201202
202 pub fn iterator(s: *const Utf8View) Utf8Iterator {203 pub fn iterator(s: Utf8View) Utf8Iterator {
203 return Utf8Iterator{204 return Utf8Iterator{
204 .bytes = s.bytes,205 .bytes = s.bytes,
205 .i = 0,206 .i = 0,
...@@ -217,7 +218,6 @@ const Utf8Iterator = struct {...@@ -217,7 +218,6 @@ const Utf8Iterator = struct {
217 }218 }
218219
219 const cp_len = utf8ByteSequenceLength(it.bytes[it.i]) catch unreachable;220 const cp_len = utf8ByteSequenceLength(it.bytes[it.i]) catch unreachable;
220
221 it.i += cp_len;221 it.i += cp_len;
222 return it.bytes[it.i - cp_len .. it.i];222 return it.bytes[it.i - cp_len .. it.i];
223 }223 }
...@@ -235,6 +235,38 @@ const Utf8Iterator = struct {...@@ -235,6 +235,38 @@ const Utf8Iterator = struct {
235 }235 }
236};236};
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
238test "utf8 encode" {270test "utf8 encode" {
239 comptime testUtf8Encode() catch unreachable;271 comptime testUtf8Encode() catch unreachable;
240 try testUtf8Encode();272 try testUtf8Encode();
...@@ -445,42 +477,34 @@ fn testDecode(bytes: []const u8) !u32 {...@@ -445,42 +477,34 @@ fn testDecode(bytes: []const u8) !u32 {
445 return utf8Decode(bytes);477 return utf8Decode(bytes);
446}478}
447479
448// TODO: make this API on top of a non-allocating Utf16LeView480/// Caller must free returned memory.
449pub fn utf16leToUtf8(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {481pub fn utf16leToUtf8Alloc(allocator: *mem.Allocator, utf16le: []const u16) ![]u8 {
450 var result = std.ArrayList(u8).init(allocator);482 var result = std.ArrayList(u8).init(allocator);
451 // optimistically guess that it will all be ascii.483 // optimistically guess that it will all be ascii.
452 try result.ensureCapacity(utf16le.len);484 try result.ensureCapacity(utf16le.len);
453
454 const utf16le_as_bytes = @sliceToBytes(utf16le);
455 var i: usize = 0;
456 var out_index: usize = 0;485 var out_index: usize = 0;
457 while (i < utf16le_as_bytes.len) : (i += 2) {486 var it = Utf16LeIterator.init(utf16le);
458 // decode487 while (try it.nextCodepoint()) |codepoint| {
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
475 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;488 const utf8_len = utf8CodepointSequenceLength(codepoint) catch unreachable;
476 try result.resize(result.len + utf8_len);489 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);
478 out_index += utf8_len;491 out_index += utf8_len;
479 }492 }
480493
481 return result.toOwnedSlice();494 return result.toOwnedSlice();
482}495}
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
484test "utf16leToUtf8" {508test "utf16leToUtf8" {
485 var utf16le: [2]u16 = undefined;509 var utf16le: [2]u16 = undefined;
486 const utf16le_as_bytes = @sliceToBytes(utf16le[0..]);510 const utf16le_as_bytes = @sliceToBytes(utf16le[0..]);
...@@ -488,14 +512,14 @@ test "utf16leToUtf8" {...@@ -488,14 +512,14 @@ test "utf16leToUtf8" {
488 {512 {
489 mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little);513 mem.writeInt(utf16le_as_bytes[0..], u16('A'), builtin.Endian.Little);
490 mem.writeInt(utf16le_as_bytes[2..], u16('a'), builtin.Endian.Little);514 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);
492 assert(mem.eql(u8, utf8, "Aa"));516 assert(mem.eql(u8, utf8, "Aa"));
493 }517 }
494518
495 {519 {
496 mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little);520 mem.writeInt(utf16le_as_bytes[0..], u16(0x80), builtin.Endian.Little);
497 mem.writeInt(utf16le_as_bytes[2..], u16(0xffff), builtin.Endian.Little);521 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);
499 assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf"));523 assert(mem.eql(u8, utf8, "\xc2\x80" ++ "\xef\xbf\xbf"));
500 }524 }
501525
...@@ -503,7 +527,7 @@ test "utf16leToUtf8" {...@@ -503,7 +527,7 @@ test "utf16leToUtf8" {
503 // the values just outside the surrogate half range527 // the values just outside the surrogate half range
504 mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little);528 mem.writeInt(utf16le_as_bytes[0..], u16(0xd7ff), builtin.Endian.Little);
505 mem.writeInt(utf16le_as_bytes[2..], u16(0xe000), builtin.Endian.Little);529 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);
507 assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80"));531 assert(mem.eql(u8, utf8, "\xed\x9f\xbf" ++ "\xee\x80\x80"));
508 }532 }
509533
...@@ -511,7 +535,7 @@ test "utf16leToUtf8" {...@@ -511,7 +535,7 @@ test "utf16leToUtf8" {
511 // smallest surrogate pair535 // smallest surrogate pair
512 mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little);536 mem.writeInt(utf16le_as_bytes[0..], u16(0xd800), builtin.Endian.Little);
513 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);537 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);
515 assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80"));539 assert(mem.eql(u8, utf8, "\xf0\x90\x80\x80"));
516 }540 }
517541
...@@ -519,14 +543,48 @@ test "utf16leToUtf8" {...@@ -519,14 +543,48 @@ test "utf16leToUtf8" {
519 // largest surrogate pair543 // largest surrogate pair
520 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);544 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
521 mem.writeInt(utf16le_as_bytes[2..], u16(0xdfff), builtin.Endian.Little);545 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);
523 assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf"));547 assert(mem.eql(u8, utf8, "\xf4\x8f\xbf\xbf"));
524 }548 }
525549
526 {550 {
527 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);551 mem.writeInt(utf16le_as_bytes[0..], u16(0xdbff), builtin.Endian.Little);
528 mem.writeInt(utf16le_as_bytes[2..], u16(0xdc00), builtin.Endian.Little);552 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);
530 assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));554 assert(mem.eql(u8, utf8, "\xf4\x8f\xb0\x80"));
531 }555 }
532}556}
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 {...@@ -32,6 +32,12 @@ pub const Tree = struct {
32 return self.source[token.start..token.end];32 return self.source[token.start..token.end];
33 }33 }
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
35 pub const Location = struct {41 pub const Location = struct {
36 line: usize,42 line: usize,
37 column: usize,43 column: usize,
...@@ -338,7 +344,7 @@ pub const Node = struct {...@@ -338,7 +344,7 @@ pub const Node = struct {
338 unreachable;344 unreachable;
339 }345 }
340346
341 pub fn firstToken(base: *Node) TokenIndex {347 pub fn firstToken(base: *const Node) TokenIndex {
342 comptime var i = 0;348 comptime var i = 0;
343 inline while (i < @memberCount(Id)) : (i += 1) {349 inline while (i < @memberCount(Id)) : (i += 1) {
344 if (base.id == @field(Id, @memberName(Id, i))) {350 if (base.id == @field(Id, @memberName(Id, i))) {
...@@ -349,7 +355,7 @@ pub const Node = struct {...@@ -349,7 +355,7 @@ pub const Node = struct {
349 unreachable;355 unreachable;
350 }356 }
351357
352 pub fn lastToken(base: *Node) TokenIndex {358 pub fn lastToken(base: *const Node) TokenIndex {
353 comptime var i = 0;359 comptime var i = 0;
354 inline while (i < @memberCount(Id)) : (i += 1) {360 inline while (i < @memberCount(Id)) : (i += 1) {
355 if (base.id == @field(Id, @memberName(Id, i))) {361 if (base.id == @field(Id, @memberName(Id, i))) {
...@@ -473,11 +479,11 @@ pub const Node = struct {...@@ -473,11 +479,11 @@ pub const Node = struct {
473 return null;479 return null;
474 }480 }
475481
476 pub fn firstToken(self: *Root) TokenIndex {482 pub fn firstToken(self: *const Root) TokenIndex {
477 return if (self.decls.len == 0) self.eof_token else (self.decls.at(0).*).firstToken();483 return if (self.decls.len == 0) self.eof_token else (self.decls.at(0).*).firstToken();
478 }484 }
479485
480 pub fn lastToken(self: *Root) TokenIndex {486 pub fn lastToken(self: *const Root) TokenIndex {
481 return if (self.decls.len == 0) self.eof_token else (self.decls.at(self.decls.len - 1).*).lastToken();487 return if (self.decls.len == 0) self.eof_token else (self.decls.at(self.decls.len - 1).*).lastToken();
482 }488 }
483 };489 };
...@@ -518,7 +524,7 @@ pub const Node = struct {...@@ -518,7 +524,7 @@ pub const Node = struct {
518 return null;524 return null;
519 }525 }
520526
521 pub fn firstToken(self: *VarDecl) TokenIndex {527 pub fn firstToken(self: *const VarDecl) TokenIndex {
522 if (self.visib_token) |visib_token| return visib_token;528 if (self.visib_token) |visib_token| return visib_token;
523 if (self.comptime_token) |comptime_token| return comptime_token;529 if (self.comptime_token) |comptime_token| return comptime_token;
524 if (self.extern_export_token) |extern_export_token| return extern_export_token;530 if (self.extern_export_token) |extern_export_token| return extern_export_token;
...@@ -526,7 +532,7 @@ pub const Node = struct {...@@ -526,7 +532,7 @@ pub const Node = struct {
526 return self.mut_token;532 return self.mut_token;
527 }533 }
528534
529 pub fn lastToken(self: *VarDecl) TokenIndex {535 pub fn lastToken(self: *const VarDecl) TokenIndex {
530 return self.semicolon_token;536 return self.semicolon_token;
531 }537 }
532 };538 };
...@@ -548,12 +554,12 @@ pub const Node = struct {...@@ -548,12 +554,12 @@ pub const Node = struct {
548 return null;554 return null;
549 }555 }
550556
551 pub fn firstToken(self: *Use) TokenIndex {557 pub fn firstToken(self: *const Use) TokenIndex {
552 if (self.visib_token) |visib_token| return visib_token;558 if (self.visib_token) |visib_token| return visib_token;
553 return self.use_token;559 return self.use_token;
554 }560 }
555561
556 pub fn lastToken(self: *Use) TokenIndex {562 pub fn lastToken(self: *const Use) TokenIndex {
557 return self.semicolon_token;563 return self.semicolon_token;
558 }564 }
559 };565 };
...@@ -575,11 +581,11 @@ pub const Node = struct {...@@ -575,11 +581,11 @@ pub const Node = struct {
575 return null;581 return null;
576 }582 }
577583
578 pub fn firstToken(self: *ErrorSetDecl) TokenIndex {584 pub fn firstToken(self: *const ErrorSetDecl) TokenIndex {
579 return self.error_token;585 return self.error_token;
580 }586 }
581587
582 pub fn lastToken(self: *ErrorSetDecl) TokenIndex {588 pub fn lastToken(self: *const ErrorSetDecl) TokenIndex {
583 return self.rbrace_token;589 return self.rbrace_token;
584 }590 }
585 };591 };
...@@ -618,14 +624,14 @@ pub const Node = struct {...@@ -618,14 +624,14 @@ pub const Node = struct {
618 return null;624 return null;
619 }625 }
620626
621 pub fn firstToken(self: *ContainerDecl) TokenIndex {627 pub fn firstToken(self: *const ContainerDecl) TokenIndex {
622 if (self.layout_token) |layout_token| {628 if (self.layout_token) |layout_token| {
623 return layout_token;629 return layout_token;
624 }630 }
625 return self.kind_token;631 return self.kind_token;
626 }632 }
627633
628 pub fn lastToken(self: *ContainerDecl) TokenIndex {634 pub fn lastToken(self: *const ContainerDecl) TokenIndex {
629 return self.rbrace_token;635 return self.rbrace_token;
630 }636 }
631 };637 };
...@@ -646,12 +652,12 @@ pub const Node = struct {...@@ -646,12 +652,12 @@ pub const Node = struct {
646 return null;652 return null;
647 }653 }
648654
649 pub fn firstToken(self: *StructField) TokenIndex {655 pub fn firstToken(self: *const StructField) TokenIndex {
650 if (self.visib_token) |visib_token| return visib_token;656 if (self.visib_token) |visib_token| return visib_token;
651 return self.name_token;657 return self.name_token;
652 }658 }
653659
654 pub fn lastToken(self: *StructField) TokenIndex {660 pub fn lastToken(self: *const StructField) TokenIndex {
655 return self.type_expr.lastToken();661 return self.type_expr.lastToken();
656 }662 }
657 };663 };
...@@ -679,11 +685,11 @@ pub const Node = struct {...@@ -679,11 +685,11 @@ pub const Node = struct {
679 return null;685 return null;
680 }686 }
681687
682 pub fn firstToken(self: *UnionTag) TokenIndex {688 pub fn firstToken(self: *const UnionTag) TokenIndex {
683 return self.name_token;689 return self.name_token;
684 }690 }
685691
686 pub fn lastToken(self: *UnionTag) TokenIndex {692 pub fn lastToken(self: *const UnionTag) TokenIndex {
687 if (self.value_expr) |value_expr| {693 if (self.value_expr) |value_expr| {
688 return value_expr.lastToken();694 return value_expr.lastToken();
689 }695 }
...@@ -712,11 +718,11 @@ pub const Node = struct {...@@ -712,11 +718,11 @@ pub const Node = struct {
712 return null;718 return null;
713 }719 }
714720
715 pub fn firstToken(self: *EnumTag) TokenIndex {721 pub fn firstToken(self: *const EnumTag) TokenIndex {
716 return self.name_token;722 return self.name_token;
717 }723 }
718724
719 pub fn lastToken(self: *EnumTag) TokenIndex {725 pub fn lastToken(self: *const EnumTag) TokenIndex {
720 if (self.value) |value| {726 if (self.value) |value| {
721 return value.lastToken();727 return value.lastToken();
722 }728 }
...@@ -741,11 +747,11 @@ pub const Node = struct {...@@ -741,11 +747,11 @@ pub const Node = struct {
741 return null;747 return null;
742 }748 }
743749
744 pub fn firstToken(self: *ErrorTag) TokenIndex {750 pub fn firstToken(self: *const ErrorTag) TokenIndex {
745 return self.name_token;751 return self.name_token;
746 }752 }
747753
748 pub fn lastToken(self: *ErrorTag) TokenIndex {754 pub fn lastToken(self: *const ErrorTag) TokenIndex {
749 return self.name_token;755 return self.name_token;
750 }756 }
751 };757 };
...@@ -758,11 +764,11 @@ pub const Node = struct {...@@ -758,11 +764,11 @@ pub const Node = struct {
758 return null;764 return null;
759 }765 }
760766
761 pub fn firstToken(self: *Identifier) TokenIndex {767 pub fn firstToken(self: *const Identifier) TokenIndex {
762 return self.token;768 return self.token;
763 }769 }
764770
765 pub fn lastToken(self: *Identifier) TokenIndex {771 pub fn lastToken(self: *const Identifier) TokenIndex {
766 return self.token;772 return self.token;
767 }773 }
768 };774 };
...@@ -784,11 +790,11 @@ pub const Node = struct {...@@ -784,11 +790,11 @@ pub const Node = struct {
784 return null;790 return null;
785 }791 }
786792
787 pub fn firstToken(self: *AsyncAttribute) TokenIndex {793 pub fn firstToken(self: *const AsyncAttribute) TokenIndex {
788 return self.async_token;794 return self.async_token;
789 }795 }
790796
791 pub fn lastToken(self: *AsyncAttribute) TokenIndex {797 pub fn lastToken(self: *const AsyncAttribute) TokenIndex {
792 if (self.rangle_bracket) |rangle_bracket| {798 if (self.rangle_bracket) |rangle_bracket| {
793 return rangle_bracket;799 return rangle_bracket;
794 }800 }
...@@ -856,7 +862,7 @@ pub const Node = struct {...@@ -856,7 +862,7 @@ pub const Node = struct {
856 return null;862 return null;
857 }863 }
858864
859 pub fn firstToken(self: *FnProto) TokenIndex {865 pub fn firstToken(self: *const FnProto) TokenIndex {
860 if (self.visib_token) |visib_token| return visib_token;866 if (self.visib_token) |visib_token| return visib_token;
861 if (self.async_attr) |async_attr| return async_attr.firstToken();867 if (self.async_attr) |async_attr| return async_attr.firstToken();
862 if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;868 if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
...@@ -865,7 +871,7 @@ pub const Node = struct {...@@ -865,7 +871,7 @@ pub const Node = struct {
865 return self.fn_token;871 return self.fn_token;
866 }872 }
867873
868 pub fn lastToken(self: *FnProto) TokenIndex {874 pub fn lastToken(self: *const FnProto) TokenIndex {
869 if (self.body_node) |body_node| return body_node.lastToken();875 if (self.body_node) |body_node| return body_node.lastToken();
870 switch (self.return_type) {876 switch (self.return_type) {
871 // TODO allow this and next prong to share bodies since the types are the same877 // TODO allow this and next prong to share bodies since the types are the same
...@@ -896,11 +902,11 @@ pub const Node = struct {...@@ -896,11 +902,11 @@ pub const Node = struct {
896 return null;902 return null;
897 }903 }
898904
899 pub fn firstToken(self: *PromiseType) TokenIndex {905 pub fn firstToken(self: *const PromiseType) TokenIndex {
900 return self.promise_token;906 return self.promise_token;
901 }907 }
902908
903 pub fn lastToken(self: *PromiseType) TokenIndex {909 pub fn lastToken(self: *const PromiseType) TokenIndex {
904 if (self.result) |result| return result.return_type.lastToken();910 if (self.result) |result| return result.return_type.lastToken();
905 return self.promise_token;911 return self.promise_token;
906 }912 }
...@@ -923,14 +929,14 @@ pub const Node = struct {...@@ -923,14 +929,14 @@ pub const Node = struct {
923 return null;929 return null;
924 }930 }
925931
926 pub fn firstToken(self: *ParamDecl) TokenIndex {932 pub fn firstToken(self: *const ParamDecl) TokenIndex {
927 if (self.comptime_token) |comptime_token| return comptime_token;933 if (self.comptime_token) |comptime_token| return comptime_token;
928 if (self.noalias_token) |noalias_token| return noalias_token;934 if (self.noalias_token) |noalias_token| return noalias_token;
929 if (self.name_token) |name_token| return name_token;935 if (self.name_token) |name_token| return name_token;
930 return self.type_node.firstToken();936 return self.type_node.firstToken();
931 }937 }
932938
933 pub fn lastToken(self: *ParamDecl) TokenIndex {939 pub fn lastToken(self: *const ParamDecl) TokenIndex {
934 if (self.var_args_token) |var_args_token| return var_args_token;940 if (self.var_args_token) |var_args_token| return var_args_token;
935 return self.type_node.lastToken();941 return self.type_node.lastToken();
936 }942 }
...@@ -954,7 +960,7 @@ pub const Node = struct {...@@ -954,7 +960,7 @@ pub const Node = struct {
954 return null;960 return null;
955 }961 }
956962
957 pub fn firstToken(self: *Block) TokenIndex {963 pub fn firstToken(self: *const Block) TokenIndex {
958 if (self.label) |label| {964 if (self.label) |label| {
959 return label;965 return label;
960 }966 }
...@@ -962,7 +968,7 @@ pub const Node = struct {...@@ -962,7 +968,7 @@ pub const Node = struct {
962 return self.lbrace;968 return self.lbrace;
963 }969 }
964970
965 pub fn lastToken(self: *Block) TokenIndex {971 pub fn lastToken(self: *const Block) TokenIndex {
966 return self.rbrace;972 return self.rbrace;
967 }973 }
968 };974 };
...@@ -981,11 +987,11 @@ pub const Node = struct {...@@ -981,11 +987,11 @@ pub const Node = struct {
981 return null;987 return null;
982 }988 }
983989
984 pub fn firstToken(self: *Defer) TokenIndex {990 pub fn firstToken(self: *const Defer) TokenIndex {
985 return self.defer_token;991 return self.defer_token;
986 }992 }
987993
988 pub fn lastToken(self: *Defer) TokenIndex {994 pub fn lastToken(self: *const Defer) TokenIndex {
989 return self.expr.lastToken();995 return self.expr.lastToken();
990 }996 }
991 };997 };
...@@ -1005,11 +1011,11 @@ pub const Node = struct {...@@ -1005,11 +1011,11 @@ pub const Node = struct {
1005 return null;1011 return null;
1006 }1012 }
10071013
1008 pub fn firstToken(self: *Comptime) TokenIndex {1014 pub fn firstToken(self: *const Comptime) TokenIndex {
1009 return self.comptime_token;1015 return self.comptime_token;
1010 }1016 }
10111017
1012 pub fn lastToken(self: *Comptime) TokenIndex {1018 pub fn lastToken(self: *const Comptime) TokenIndex {
1013 return self.expr.lastToken();1019 return self.expr.lastToken();
1014 }1020 }
1015 };1021 };
...@@ -1029,11 +1035,11 @@ pub const Node = struct {...@@ -1029,11 +1035,11 @@ pub const Node = struct {
1029 return null;1035 return null;
1030 }1036 }
10311037
1032 pub fn firstToken(self: *Payload) TokenIndex {1038 pub fn firstToken(self: *const Payload) TokenIndex {
1033 return self.lpipe;1039 return self.lpipe;
1034 }1040 }
10351041
1036 pub fn lastToken(self: *Payload) TokenIndex {1042 pub fn lastToken(self: *const Payload) TokenIndex {
1037 return self.rpipe;1043 return self.rpipe;
1038 }1044 }
1039 };1045 };
...@@ -1054,11 +1060,11 @@ pub const Node = struct {...@@ -1054,11 +1060,11 @@ pub const Node = struct {
1054 return null;1060 return null;
1055 }1061 }
10561062
1057 pub fn firstToken(self: *PointerPayload) TokenIndex {1063 pub fn firstToken(self: *const PointerPayload) TokenIndex {
1058 return self.lpipe;1064 return self.lpipe;
1059 }1065 }
10601066
1061 pub fn lastToken(self: *PointerPayload) TokenIndex {1067 pub fn lastToken(self: *const PointerPayload) TokenIndex {
1062 return self.rpipe;1068 return self.rpipe;
1063 }1069 }
1064 };1070 };
...@@ -1085,11 +1091,11 @@ pub const Node = struct {...@@ -1085,11 +1091,11 @@ pub const Node = struct {
1085 return null;1091 return null;
1086 }1092 }
10871093
1088 pub fn firstToken(self: *PointerIndexPayload) TokenIndex {1094 pub fn firstToken(self: *const PointerIndexPayload) TokenIndex {
1089 return self.lpipe;1095 return self.lpipe;
1090 }1096 }
10911097
1092 pub fn lastToken(self: *PointerIndexPayload) TokenIndex {1098 pub fn lastToken(self: *const PointerIndexPayload) TokenIndex {
1093 return self.rpipe;1099 return self.rpipe;
1094 }1100 }
1095 };1101 };
...@@ -1114,11 +1120,11 @@ pub const Node = struct {...@@ -1114,11 +1120,11 @@ pub const Node = struct {
1114 return null;1120 return null;
1115 }1121 }
11161122
1117 pub fn firstToken(self: *Else) TokenIndex {1123 pub fn firstToken(self: *const Else) TokenIndex {
1118 return self.else_token;1124 return self.else_token;
1119 }1125 }
11201126
1121 pub fn lastToken(self: *Else) TokenIndex {1127 pub fn lastToken(self: *const Else) TokenIndex {
1122 return self.body.lastToken();1128 return self.body.lastToken();
1123 }1129 }
1124 };1130 };
...@@ -1146,11 +1152,11 @@ pub const Node = struct {...@@ -1146,11 +1152,11 @@ pub const Node = struct {
1146 return null;1152 return null;
1147 }1153 }
11481154
1149 pub fn firstToken(self: *Switch) TokenIndex {1155 pub fn firstToken(self: *const Switch) TokenIndex {
1150 return self.switch_token;1156 return self.switch_token;
1151 }1157 }
11521158
1153 pub fn lastToken(self: *Switch) TokenIndex {1159 pub fn lastToken(self: *const Switch) TokenIndex {
1154 return self.rbrace;1160 return self.rbrace;
1155 }1161 }
1156 };1162 };
...@@ -1181,11 +1187,11 @@ pub const Node = struct {...@@ -1181,11 +1187,11 @@ pub const Node = struct {
1181 return null;1187 return null;
1182 }1188 }
11831189
1184 pub fn firstToken(self: *SwitchCase) TokenIndex {1190 pub fn firstToken(self: *const SwitchCase) TokenIndex {
1185 return (self.items.at(0).*).firstToken();1191 return (self.items.at(0).*).firstToken();
1186 }1192 }
11871193
1188 pub fn lastToken(self: *SwitchCase) TokenIndex {1194 pub fn lastToken(self: *const SwitchCase) TokenIndex {
1189 return self.expr.lastToken();1195 return self.expr.lastToken();
1190 }1196 }
1191 };1197 };
...@@ -1198,11 +1204,11 @@ pub const Node = struct {...@@ -1198,11 +1204,11 @@ pub const Node = struct {
1198 return null;1204 return null;
1199 }1205 }
12001206
1201 pub fn firstToken(self: *SwitchElse) TokenIndex {1207 pub fn firstToken(self: *const SwitchElse) TokenIndex {
1202 return self.token;1208 return self.token;
1203 }1209 }
12041210
1205 pub fn lastToken(self: *SwitchElse) TokenIndex {1211 pub fn lastToken(self: *const SwitchElse) TokenIndex {
1206 return self.token;1212 return self.token;
1207 }1213 }
1208 };1214 };
...@@ -1245,7 +1251,7 @@ pub const Node = struct {...@@ -1245,7 +1251,7 @@ pub const Node = struct {
1245 return null;1251 return null;
1246 }1252 }
12471253
1248 pub fn firstToken(self: *While) TokenIndex {1254 pub fn firstToken(self: *const While) TokenIndex {
1249 if (self.label) |label| {1255 if (self.label) |label| {
1250 return label;1256 return label;
1251 }1257 }
...@@ -1257,7 +1263,7 @@ pub const Node = struct {...@@ -1257,7 +1263,7 @@ pub const Node = struct {
1257 return self.while_token;1263 return self.while_token;
1258 }1264 }
12591265
1260 pub fn lastToken(self: *While) TokenIndex {1266 pub fn lastToken(self: *const While) TokenIndex {
1261 if (self.@"else") |@"else"| {1267 if (self.@"else") |@"else"| {
1262 return @"else".body.lastToken();1268 return @"else".body.lastToken();
1263 }1269 }
...@@ -1298,7 +1304,7 @@ pub const Node = struct {...@@ -1298,7 +1304,7 @@ pub const Node = struct {
1298 return null;1304 return null;
1299 }1305 }
13001306
1301 pub fn firstToken(self: *For) TokenIndex {1307 pub fn firstToken(self: *const For) TokenIndex {
1302 if (self.label) |label| {1308 if (self.label) |label| {
1303 return label;1309 return label;
1304 }1310 }
...@@ -1310,7 +1316,7 @@ pub const Node = struct {...@@ -1310,7 +1316,7 @@ pub const Node = struct {
1310 return self.for_token;1316 return self.for_token;
1311 }1317 }
13121318
1313 pub fn lastToken(self: *For) TokenIndex {1319 pub fn lastToken(self: *const For) TokenIndex {
1314 if (self.@"else") |@"else"| {1320 if (self.@"else") |@"else"| {
1315 return @"else".body.lastToken();1321 return @"else".body.lastToken();
1316 }1322 }
...@@ -1349,11 +1355,11 @@ pub const Node = struct {...@@ -1349,11 +1355,11 @@ pub const Node = struct {
1349 return null;1355 return null;
1350 }1356 }
13511357
1352 pub fn firstToken(self: *If) TokenIndex {1358 pub fn firstToken(self: *const If) TokenIndex {
1353 return self.if_token;1359 return self.if_token;
1354 }1360 }
13551361
1356 pub fn lastToken(self: *If) TokenIndex {1362 pub fn lastToken(self: *const If) TokenIndex {
1357 if (self.@"else") |@"else"| {1363 if (self.@"else") |@"else"| {
1358 return @"else".body.lastToken();1364 return @"else".body.lastToken();
1359 }1365 }
...@@ -1480,11 +1486,11 @@ pub const Node = struct {...@@ -1480,11 +1486,11 @@ pub const Node = struct {
1480 return null;1486 return null;
1481 }1487 }
14821488
1483 pub fn firstToken(self: *InfixOp) TokenIndex {1489 pub fn firstToken(self: *const InfixOp) TokenIndex {
1484 return self.lhs.firstToken();1490 return self.lhs.firstToken();
1485 }1491 }
14861492
1487 pub fn lastToken(self: *InfixOp) TokenIndex {1493 pub fn lastToken(self: *const InfixOp) TokenIndex {
1488 return self.rhs.lastToken();1494 return self.rhs.lastToken();
1489 }1495 }
1490 };1496 };
...@@ -1570,11 +1576,11 @@ pub const Node = struct {...@@ -1570,11 +1576,11 @@ pub const Node = struct {
1570 return null;1576 return null;
1571 }1577 }
15721578
1573 pub fn firstToken(self: *PrefixOp) TokenIndex {1579 pub fn firstToken(self: *const PrefixOp) TokenIndex {
1574 return self.op_token;1580 return self.op_token;
1575 }1581 }
15761582
1577 pub fn lastToken(self: *PrefixOp) TokenIndex {1583 pub fn lastToken(self: *const PrefixOp) TokenIndex {
1578 return self.rhs.lastToken();1584 return self.rhs.lastToken();
1579 }1585 }
1580 };1586 };
...@@ -1594,11 +1600,11 @@ pub const Node = struct {...@@ -1594,11 +1600,11 @@ pub const Node = struct {
1594 return null;1600 return null;
1595 }1601 }
15961602
1597 pub fn firstToken(self: *FieldInitializer) TokenIndex {1603 pub fn firstToken(self: *const FieldInitializer) TokenIndex {
1598 return self.period_token;1604 return self.period_token;
1599 }1605 }
16001606
1601 pub fn lastToken(self: *FieldInitializer) TokenIndex {1607 pub fn lastToken(self: *const FieldInitializer) TokenIndex {
1602 return self.expr.lastToken();1608 return self.expr.lastToken();
1603 }1609 }
1604 };1610 };
...@@ -1673,7 +1679,7 @@ pub const Node = struct {...@@ -1673,7 +1679,7 @@ pub const Node = struct {
1673 return null;1679 return null;
1674 }1680 }
16751681
1676 pub fn firstToken(self: *SuffixOp) TokenIndex {1682 pub fn firstToken(self: *const SuffixOp) TokenIndex {
1677 switch (self.op) {1683 switch (self.op) {
1678 @TagType(Op).Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(),1684 @TagType(Op).Call => |*call_info| if (call_info.async_attr) |async_attr| return async_attr.firstToken(),
1679 else => {},1685 else => {},
...@@ -1681,7 +1687,7 @@ pub const Node = struct {...@@ -1681,7 +1687,7 @@ pub const Node = struct {
1681 return self.lhs.firstToken();1687 return self.lhs.firstToken();
1682 }1688 }
16831689
1684 pub fn lastToken(self: *SuffixOp) TokenIndex {1690 pub fn lastToken(self: *const SuffixOp) TokenIndex {
1685 return self.rtoken;1691 return self.rtoken;
1686 }1692 }
1687 };1693 };
...@@ -1701,11 +1707,11 @@ pub const Node = struct {...@@ -1701,11 +1707,11 @@ pub const Node = struct {
1701 return null;1707 return null;
1702 }1708 }
17031709
1704 pub fn firstToken(self: *GroupedExpression) TokenIndex {1710 pub fn firstToken(self: *const GroupedExpression) TokenIndex {
1705 return self.lparen;1711 return self.lparen;
1706 }1712 }
17071713
1708 pub fn lastToken(self: *GroupedExpression) TokenIndex {1714 pub fn lastToken(self: *const GroupedExpression) TokenIndex {
1709 return self.rparen;1715 return self.rparen;
1710 }1716 }
1711 };1717 };
...@@ -1749,11 +1755,11 @@ pub const Node = struct {...@@ -1749,11 +1755,11 @@ pub const Node = struct {
1749 return null;1755 return null;
1750 }1756 }
17511757
1752 pub fn firstToken(self: *ControlFlowExpression) TokenIndex {1758 pub fn firstToken(self: *const ControlFlowExpression) TokenIndex {
1753 return self.ltoken;1759 return self.ltoken;
1754 }1760 }
17551761
1756 pub fn lastToken(self: *ControlFlowExpression) TokenIndex {1762 pub fn lastToken(self: *const ControlFlowExpression) TokenIndex {
1757 if (self.rhs) |rhs| {1763 if (self.rhs) |rhs| {
1758 return rhs.lastToken();1764 return rhs.lastToken();
1759 }1765 }
...@@ -1792,11 +1798,11 @@ pub const Node = struct {...@@ -1792,11 +1798,11 @@ pub const Node = struct {
1792 return null;1798 return null;
1793 }1799 }
17941800
1795 pub fn firstToken(self: *Suspend) TokenIndex {1801 pub fn firstToken(self: *const Suspend) TokenIndex {
1796 return self.suspend_token;1802 return self.suspend_token;
1797 }1803 }
17981804
1799 pub fn lastToken(self: *Suspend) TokenIndex {1805 pub fn lastToken(self: *const Suspend) TokenIndex {
1800 if (self.body) |body| {1806 if (self.body) |body| {
1801 return body.lastToken();1807 return body.lastToken();
1802 }1808 }
...@@ -1813,11 +1819,11 @@ pub const Node = struct {...@@ -1813,11 +1819,11 @@ pub const Node = struct {
1813 return null;1819 return null;
1814 }1820 }
18151821
1816 pub fn firstToken(self: *IntegerLiteral) TokenIndex {1822 pub fn firstToken(self: *const IntegerLiteral) TokenIndex {
1817 return self.token;1823 return self.token;
1818 }1824 }
18191825
1820 pub fn lastToken(self: *IntegerLiteral) TokenIndex {1826 pub fn lastToken(self: *const IntegerLiteral) TokenIndex {
1821 return self.token;1827 return self.token;
1822 }1828 }
1823 };1829 };
...@@ -1830,11 +1836,11 @@ pub const Node = struct {...@@ -1830,11 +1836,11 @@ pub const Node = struct {
1830 return null;1836 return null;
1831 }1837 }
18321838
1833 pub fn firstToken(self: *FloatLiteral) TokenIndex {1839 pub fn firstToken(self: *const FloatLiteral) TokenIndex {
1834 return self.token;1840 return self.token;
1835 }1841 }
18361842
1837 pub fn lastToken(self: *FloatLiteral) TokenIndex {1843 pub fn lastToken(self: *const FloatLiteral) TokenIndex {
1838 return self.token;1844 return self.token;
1839 }1845 }
1840 };1846 };
...@@ -1856,11 +1862,11 @@ pub const Node = struct {...@@ -1856,11 +1862,11 @@ pub const Node = struct {
1856 return null;1862 return null;
1857 }1863 }
18581864
1859 pub fn firstToken(self: *BuiltinCall) TokenIndex {1865 pub fn firstToken(self: *const BuiltinCall) TokenIndex {
1860 return self.builtin_token;1866 return self.builtin_token;
1861 }1867 }
18621868
1863 pub fn lastToken(self: *BuiltinCall) TokenIndex {1869 pub fn lastToken(self: *const BuiltinCall) TokenIndex {
1864 return self.rparen_token;1870 return self.rparen_token;
1865 }1871 }
1866 };1872 };
...@@ -1873,11 +1879,11 @@ pub const Node = struct {...@@ -1873,11 +1879,11 @@ pub const Node = struct {
1873 return null;1879 return null;
1874 }1880 }
18751881
1876 pub fn firstToken(self: *StringLiteral) TokenIndex {1882 pub fn firstToken(self: *const StringLiteral) TokenIndex {
1877 return self.token;1883 return self.token;
1878 }1884 }
18791885
1880 pub fn lastToken(self: *StringLiteral) TokenIndex {1886 pub fn lastToken(self: *const StringLiteral) TokenIndex {
1881 return self.token;1887 return self.token;
1882 }1888 }
1883 };1889 };
...@@ -1892,11 +1898,11 @@ pub const Node = struct {...@@ -1892,11 +1898,11 @@ pub const Node = struct {
1892 return null;1898 return null;
1893 }1899 }
18941900
1895 pub fn firstToken(self: *MultilineStringLiteral) TokenIndex {1901 pub fn firstToken(self: *const MultilineStringLiteral) TokenIndex {
1896 return self.lines.at(0).*;1902 return self.lines.at(0).*;
1897 }1903 }
18981904
1899 pub fn lastToken(self: *MultilineStringLiteral) TokenIndex {1905 pub fn lastToken(self: *const MultilineStringLiteral) TokenIndex {
1900 return self.lines.at(self.lines.len - 1).*;1906 return self.lines.at(self.lines.len - 1).*;
1901 }1907 }
1902 };1908 };
...@@ -1909,11 +1915,11 @@ pub const Node = struct {...@@ -1909,11 +1915,11 @@ pub const Node = struct {
1909 return null;1915 return null;
1910 }1916 }
19111917
1912 pub fn firstToken(self: *CharLiteral) TokenIndex {1918 pub fn firstToken(self: *const CharLiteral) TokenIndex {
1913 return self.token;1919 return self.token;
1914 }1920 }
19151921
1916 pub fn lastToken(self: *CharLiteral) TokenIndex {1922 pub fn lastToken(self: *const CharLiteral) TokenIndex {
1917 return self.token;1923 return self.token;
1918 }1924 }
1919 };1925 };
...@@ -1926,11 +1932,11 @@ pub const Node = struct {...@@ -1926,11 +1932,11 @@ pub const Node = struct {
1926 return null;1932 return null;
1927 }1933 }
19281934
1929 pub fn firstToken(self: *BoolLiteral) TokenIndex {1935 pub fn firstToken(self: *const BoolLiteral) TokenIndex {
1930 return self.token;1936 return self.token;
1931 }1937 }
19321938
1933 pub fn lastToken(self: *BoolLiteral) TokenIndex {1939 pub fn lastToken(self: *const BoolLiteral) TokenIndex {
1934 return self.token;1940 return self.token;
1935 }1941 }
1936 };1942 };
...@@ -1943,11 +1949,11 @@ pub const Node = struct {...@@ -1943,11 +1949,11 @@ pub const Node = struct {
1943 return null;1949 return null;
1944 }1950 }
19451951
1946 pub fn firstToken(self: *NullLiteral) TokenIndex {1952 pub fn firstToken(self: *const NullLiteral) TokenIndex {
1947 return self.token;1953 return self.token;
1948 }1954 }
19491955
1950 pub fn lastToken(self: *NullLiteral) TokenIndex {1956 pub fn lastToken(self: *const NullLiteral) TokenIndex {
1951 return self.token;1957 return self.token;
1952 }1958 }
1953 };1959 };
...@@ -1960,11 +1966,11 @@ pub const Node = struct {...@@ -1960,11 +1966,11 @@ pub const Node = struct {
1960 return null;1966 return null;
1961 }1967 }
19621968
1963 pub fn firstToken(self: *UndefinedLiteral) TokenIndex {1969 pub fn firstToken(self: *const UndefinedLiteral) TokenIndex {
1964 return self.token;1970 return self.token;
1965 }1971 }
19661972
1967 pub fn lastToken(self: *UndefinedLiteral) TokenIndex {1973 pub fn lastToken(self: *const UndefinedLiteral) TokenIndex {
1968 return self.token;1974 return self.token;
1969 }1975 }
1970 };1976 };
...@@ -1977,11 +1983,11 @@ pub const Node = struct {...@@ -1977,11 +1983,11 @@ pub const Node = struct {
1977 return null;1983 return null;
1978 }1984 }
19791985
1980 pub fn firstToken(self: *ThisLiteral) TokenIndex {1986 pub fn firstToken(self: *const ThisLiteral) TokenIndex {
1981 return self.token;1987 return self.token;
1982 }1988 }
19831989
1984 pub fn lastToken(self: *ThisLiteral) TokenIndex {1990 pub fn lastToken(self: *const ThisLiteral) TokenIndex {
1985 return self.token;1991 return self.token;
1986 }1992 }
1987 };1993 };
...@@ -2022,11 +2028,11 @@ pub const Node = struct {...@@ -2022,11 +2028,11 @@ pub const Node = struct {
2022 return null;2028 return null;
2023 }2029 }
20242030
2025 pub fn firstToken(self: *AsmOutput) TokenIndex {2031 pub fn firstToken(self: *const AsmOutput) TokenIndex {
2026 return self.lbracket;2032 return self.lbracket;
2027 }2033 }
20282034
2029 pub fn lastToken(self: *AsmOutput) TokenIndex {2035 pub fn lastToken(self: *const AsmOutput) TokenIndex {
2030 return self.rparen;2036 return self.rparen;
2031 }2037 }
2032 };2038 };
...@@ -2054,11 +2060,11 @@ pub const Node = struct {...@@ -2054,11 +2060,11 @@ pub const Node = struct {
2054 return null;2060 return null;
2055 }2061 }
20562062
2057 pub fn firstToken(self: *AsmInput) TokenIndex {2063 pub fn firstToken(self: *const AsmInput) TokenIndex {
2058 return self.lbracket;2064 return self.lbracket;
2059 }2065 }
20602066
2061 pub fn lastToken(self: *AsmInput) TokenIndex {2067 pub fn lastToken(self: *const AsmInput) TokenIndex {
2062 return self.rparen;2068 return self.rparen;
2063 }2069 }
2064 };2070 };
...@@ -2089,11 +2095,11 @@ pub const Node = struct {...@@ -2089,11 +2095,11 @@ pub const Node = struct {
2089 return null;2095 return null;
2090 }2096 }
20912097
2092 pub fn firstToken(self: *Asm) TokenIndex {2098 pub fn firstToken(self: *const Asm) TokenIndex {
2093 return self.asm_token;2099 return self.asm_token;
2094 }2100 }
20952101
2096 pub fn lastToken(self: *Asm) TokenIndex {2102 pub fn lastToken(self: *const Asm) TokenIndex {
2097 return self.rparen;2103 return self.rparen;
2098 }2104 }
2099 };2105 };
...@@ -2106,11 +2112,11 @@ pub const Node = struct {...@@ -2106,11 +2112,11 @@ pub const Node = struct {
2106 return null;2112 return null;
2107 }2113 }
21082114
2109 pub fn firstToken(self: *Unreachable) TokenIndex {2115 pub fn firstToken(self: *const Unreachable) TokenIndex {
2110 return self.token;2116 return self.token;
2111 }2117 }
21122118
2113 pub fn lastToken(self: *Unreachable) TokenIndex {2119 pub fn lastToken(self: *const Unreachable) TokenIndex {
2114 return self.token;2120 return self.token;
2115 }2121 }
2116 };2122 };
...@@ -2123,11 +2129,11 @@ pub const Node = struct {...@@ -2123,11 +2129,11 @@ pub const Node = struct {
2123 return null;2129 return null;
2124 }2130 }
21252131
2126 pub fn firstToken(self: *ErrorType) TokenIndex {2132 pub fn firstToken(self: *const ErrorType) TokenIndex {
2127 return self.token;2133 return self.token;
2128 }2134 }
21292135
2130 pub fn lastToken(self: *ErrorType) TokenIndex {2136 pub fn lastToken(self: *const ErrorType) TokenIndex {
2131 return self.token;2137 return self.token;
2132 }2138 }
2133 };2139 };
...@@ -2140,11 +2146,11 @@ pub const Node = struct {...@@ -2140,11 +2146,11 @@ pub const Node = struct {
2140 return null;2146 return null;
2141 }2147 }
21422148
2143 pub fn firstToken(self: *VarType) TokenIndex {2149 pub fn firstToken(self: *const VarType) TokenIndex {
2144 return self.token;2150 return self.token;
2145 }2151 }
21462152
2147 pub fn lastToken(self: *VarType) TokenIndex {2153 pub fn lastToken(self: *const VarType) TokenIndex {
2148 return self.token;2154 return self.token;
2149 }2155 }
2150 };2156 };
...@@ -2159,11 +2165,11 @@ pub const Node = struct {...@@ -2159,11 +2165,11 @@ pub const Node = struct {
2159 return null;2165 return null;
2160 }2166 }
21612167
2162 pub fn firstToken(self: *DocComment) TokenIndex {2168 pub fn firstToken(self: *const DocComment) TokenIndex {
2163 return self.lines.at(0).*;2169 return self.lines.at(0).*;
2164 }2170 }
21652171
2166 pub fn lastToken(self: *DocComment) TokenIndex {2172 pub fn lastToken(self: *const DocComment) TokenIndex {
2167 return self.lines.at(self.lines.len - 1).*;2173 return self.lines.at(self.lines.len - 1).*;
2168 }2174 }
2169 };2175 };
...@@ -2184,11 +2190,11 @@ pub const Node = struct {...@@ -2184,11 +2190,11 @@ pub const Node = struct {
2184 return null;2190 return null;
2185 }2191 }
21862192
2187 pub fn firstToken(self: *TestDecl) TokenIndex {2193 pub fn firstToken(self: *const TestDecl) TokenIndex {
2188 return self.test_token;2194 return self.test_token;
2189 }2195 }
21902196
2191 pub fn lastToken(self: *TestDecl) TokenIndex {2197 pub fn lastToken(self: *const TestDecl) TokenIndex {
2192 return self.body_node.lastToken();2198 return self.body_node.lastToken();
2193 }2199 }
2194 };2200 };
test/behavior.zig+1
...@@ -10,6 +10,7 @@ comptime {...@@ -10,6 +10,7 @@ comptime {
10 _ = @import("cases/bool.zig");10 _ = @import("cases/bool.zig");
11 _ = @import("cases/bugs/1111.zig");11 _ = @import("cases/bugs/1111.zig");
12 _ = @import("cases/bugs/1230.zig");12 _ = @import("cases/bugs/1230.zig");
13 _ = @import("cases/bugs/1277.zig");
13 _ = @import("cases/bugs/394.zig");14 _ = @import("cases/bugs/394.zig");
14 _ = @import("cases/bugs/655.zig");15 _ = @import("cases/bugs/655.zig");
15 _ = @import("cases/bugs/656.zig");16 _ = @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 {...@@ -485,3 +485,14 @@ fn MakeType(comptime T: type) type {
485 }485 }
486 };486 };
487}487}
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 @@...@@ -1,5 +1,5 @@
1const A = error{1const A = error{
2 PathNotFound,2 FileNotFound,
3 NotDir,3 NotDir,
4};4};
5const B = error{OutOfMemory};5const B = error{OutOfMemory};
...@@ -15,7 +15,7 @@ test "merge error sets" {...@@ -15,7 +15,7 @@ test "merge error sets" {
15 @panic("unexpected");15 @panic("unexpected");
16 } else |err| switch (err) {16 } else |err| switch (err) {
17 error.OutOfMemory => @panic("unexpected"),17 error.OutOfMemory => @panic("unexpected"),
18 error.PathNotFound => @panic("unexpected"),18 error.FileNotFound => @panic("unexpected"),
19 error.NotDir => {},19 error.NotDir => {},
20 }20 }
21}21}
test/translate_c.zig+45
...@@ -1,6 +1,51 @@...@@ -1,6 +1,51 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.TranslateCContext) void {3pub 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
4 cases.add("double define struct",49 cases.add("double define struct",
5 \\typedef struct Bar Bar;50 \\typedef struct Bar Bar;
6 \\typedef struct Foo Foo;51 \\typedef struct Foo Foo;