authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-14 18:27:59-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-14 18:27:59-04:00
log32dd98b19fe3cc384df32704dac0ff3e377dbe0c
tree2eddf3618d80313bdd24c25bd589bd474f3d82fc
parentef7f69d14a017c6c2065e4a376bb8e1f05ace04b
parentf0697c28f80d64c544302aea576e41ebc443b41c

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


93 files changed, 5383 insertions(+), 2150 deletions(-)

CMakeLists.txt+3
...@@ -464,6 +464,7 @@ set(ZIG_STD_FILES...@@ -464,6 +464,7 @@ set(ZIG_STD_FILES
464 "math/atan.zig"464 "math/atan.zig"
465 "math/atan2.zig"465 "math/atan2.zig"
466 "math/atanh.zig"466 "math/atanh.zig"
467 "math/big/int.zig"
467 "math/cbrt.zig"468 "math/cbrt.zig"
468 "math/ceil.zig"469 "math/ceil.zig"
469 "math/complex/abs.zig"470 "math/complex/abs.zig"
...@@ -555,6 +556,7 @@ set(ZIG_STD_FILES...@@ -555,6 +556,7 @@ set(ZIG_STD_FILES
555 "special/compiler_rt/aulldiv.zig"556 "special/compiler_rt/aulldiv.zig"
556 "special/compiler_rt/aullrem.zig"557 "special/compiler_rt/aullrem.zig"
557 "special/compiler_rt/comparetf2.zig"558 "special/compiler_rt/comparetf2.zig"
559 "special/compiler_rt/divti3.zig"
558 "special/compiler_rt/fixuint.zig"560 "special/compiler_rt/fixuint.zig"
559 "special/compiler_rt/fixunsdfdi.zig"561 "special/compiler_rt/fixunsdfdi.zig"
560 "special/compiler_rt/fixunsdfsi.zig"562 "special/compiler_rt/fixunsdfsi.zig"
...@@ -565,6 +567,7 @@ set(ZIG_STD_FILES...@@ -565,6 +567,7 @@ set(ZIG_STD_FILES
565 "special/compiler_rt/fixunstfdi.zig"567 "special/compiler_rt/fixunstfdi.zig"
566 "special/compiler_rt/fixunstfsi.zig"568 "special/compiler_rt/fixunstfsi.zig"
567 "special/compiler_rt/fixunstfti.zig"569 "special/compiler_rt/fixunstfti.zig"
570 "special/compiler_rt/muloti4.zig"
568 "special/compiler_rt/index.zig"571 "special/compiler_rt/index.zig"
569 "special/compiler_rt/udivmod.zig"572 "special/compiler_rt/udivmod.zig"
570 "special/compiler_rt/udivmoddi4.zig"573 "special/compiler_rt/udivmoddi4.zig"
README.md+12-12
...@@ -55,18 +55,18 @@ that counts as "freestanding" for the purposes of this table....@@ -55,18 +55,18 @@ that counts as "freestanding" for the purposes of this table.
55|i386 | OK | planned | OK | planned | planned |55|i386 | OK | planned | OK | planned | planned |
56|x86_64 | OK | OK | OK | OK | planned |56|x86_64 | OK | OK | OK | OK | planned |
57|arm | OK | planned | planned | N/A | planned |57|arm | OK | planned | planned | N/A | planned |
58|aarch64 | OK | planned | planned | planned | planned |58|aarch64 | OK | planned | N/A | planned | planned |
59|bpf | OK | planned | planned | N/A | planned |59|bpf | OK | planned | N/A | N/A | planned |
60|hexagon | OK | planned | planned | N/A | planned |60|hexagon | OK | planned | N/A | N/A | planned |
61|mips | OK | planned | planned | N/A | planned |61|mips | OK | planned | N/A | N/A | planned |
62|powerpc | OK | planned | planned | N/A | planned |62|powerpc | OK | planned | N/A | N/A | planned |
63|r600 | OK | planned | planned | N/A | planned |63|r600 | OK | planned | N/A | N/A | planned |
64|amdgcn | OK | planned | planned | N/A | planned |64|amdgcn | OK | planned | N/A | N/A | planned |
65|sparc | OK | planned | planned | N/A | planned |65|sparc | OK | planned | N/A | N/A | planned |
66|s390x | OK | planned | planned | N/A | planned |66|s390x | OK | planned | N/A | N/A | planned |
67|thumb | OK | planned | planned | N/A | planned |67|thumb | OK | planned | N/A | N/A | planned |
68|spir | OK | planned | planned | N/A | planned |68|spir | OK | planned | N/A | N/A | planned |
69|lanai | OK | planned | planned | N/A | planned |69|lanai | OK | planned | N/A | N/A | planned |
7070
71## Community71## Community
7272
build.zig+5-4
...@@ -63,6 +63,7 @@ pub fn build(b: *Builder) !void {...@@ -63,6 +63,7 @@ pub fn build(b: *Builder) !void {
63 exe.addObjectFile(lib);63 exe.addObjectFile(lib);
64 }64 }
65 } else {65 } else {
66 addCppLib(b, exe, cmake_binary_dir, "embedded_lld_wasm");
66 addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf");67 addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf");
67 addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff");68 addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff");
68 addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib");69 addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib");
...@@ -74,7 +75,7 @@ pub fn build(b: *Builder) !void {...@@ -74,7 +75,7 @@ pub fn build(b: *Builder) !void {
74 cxx_compiler,75 cxx_compiler,
75 "-print-file-name=libstdc++.a",76 "-print-file-name=libstdc++.a",
76 });77 });
77 const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next();78 const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?;
78 if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) {79 if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) {
79 warn(80 warn(
80 \\Unable to determine path to libstdc++.a81 \\Unable to determine path to libstdc++.a
...@@ -101,11 +102,11 @@ pub fn build(b: *Builder) !void {...@@ -101,11 +102,11 @@ pub fn build(b: *Builder) !void {
101102
102 b.default_step.dependOn(&exe.step);103 b.default_step.dependOn(&exe.step);
103104
104 const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") ?? false;105 const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false;
105 if (!skip_self_hosted) {106 if (!skip_self_hosted) {
106 test_step.dependOn(&exe.step);107 test_step.dependOn(&exe.step);
107 }108 }
108 const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") ?? false;109 const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") orelse false;
109 exe.setVerboseLink(verbose_link_exe);110 exe.setVerboseLink(verbose_link_exe);
110111
111 b.installArtifact(exe);112 b.installArtifact(exe);
...@@ -113,7 +114,7 @@ pub fn build(b: *Builder) !void {...@@ -113,7 +114,7 @@ pub fn build(b: *Builder) !void {
113 installCHeaders(b, c_header_files);114 installCHeaders(b, c_header_files);
114115
115 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");116 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
116 const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") ?? false;117 const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") orelse false;
117118
118 test_step.dependOn(docs_step);119 test_step.dependOn(docs_step);
119120
doc/codegen.md+4-4
...@@ -6,7 +6,7 @@ Every type has a "handle". If a type is a simple primitive type such as i32 or...@@ -6,7 +6,7 @@ Every type has a "handle". If a type is a simple primitive type such as i32 or
6f64, the handle is "by value", meaning that we pass around the value itself when6f64, the handle is "by value", meaning that we pass around the value itself when
7we refer to a value of that type.7we refer to a value of that type.
88
9If a type is a container, error union, maybe type, slice, or array, then its9If a type is a container, error union, optional type, slice, or array, then its
10handle is a pointer, and everywhere we refer to a value of this type we refer to10handle is a pointer, and everywhere we refer to a value of this type we refer to
11a pointer.11a pointer.
1212
...@@ -19,7 +19,7 @@ Error union types are represented as:...@@ -19,7 +19,7 @@ Error union types are represented as:
19 payload: T,19 payload: T,
20 }20 }
2121
22Maybe types are represented as:22Optional types are represented as:
2323
24 struct {24 struct {
25 payload: T,25 payload: T,
...@@ -28,6 +28,6 @@ Maybe types are represented as:...@@ -28,6 +28,6 @@ Maybe types are represented as:
2828
29## Data Optimizations29## Data Optimizations
3030
31Maybe pointer types are special: the 0x0 pointer value is used to represent a31Optional pointer types are special: the 0x0 pointer value is used to represent a
32null pointer. Thus, instead of the struct above, maybe pointer types are32null pointer. Thus, instead of the struct above, optional pointer types are
33represented as a `usize` in codegen and the handle is by value.33represented as a `usize` in codegen and the handle is by value.
doc/docgen.zig+28-11
...@@ -25,13 +25,13 @@ pub fn main() !void {...@@ -25,13 +25,13 @@ pub fn main() !void {
2525
26 if (!args_it.skip()) @panic("expected self arg");26 if (!args_it.skip()) @panic("expected self arg");
2727
28 const zig_exe = try (args_it.next(allocator) ?? @panic("expected zig exe arg"));28 const zig_exe = try (args_it.next(allocator) orelse @panic("expected zig exe arg"));
29 defer allocator.free(zig_exe);29 defer allocator.free(zig_exe);
3030
31 const in_file_name = try (args_it.next(allocator) ?? @panic("expected input arg"));31 const in_file_name = try (args_it.next(allocator) orelse @panic("expected input arg"));
32 defer allocator.free(in_file_name);32 defer allocator.free(in_file_name);
3333
34 const out_file_name = try (args_it.next(allocator) ?? @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(allocator, in_file_name);
...@@ -51,14 +51,8 @@ pub fn main() !void {...@@ -51,14 +51,8 @@ pub fn main() !void {
51 var toc = try genToc(allocator, &tokenizer);51 var toc = try genToc(allocator, &tokenizer);
5252
53 try os.makePath(allocator, tmp_dir_name);53 try os.makePath(allocator, tmp_dir_name);
54 defer {54 defer os.deleteTree(allocator, tmp_dir_name) catch {};
55 // TODO issue #70955
56 // disabled to pass CI tests, but obviously we want to implement this
57 // and then remove this workaround
58 if (builtin.os != builtin.Os.windows) {
59 os.deleteTree(allocator, tmp_dir_name) catch {};
60 }
61 }
62 try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);56 try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);
63 try buffered_out_stream.flush();57 try buffered_out_stream.flush();
64}58}
...@@ -300,6 +294,7 @@ const Link = struct {...@@ -300,6 +294,7 @@ const Link = struct {
300const Node = union(enum) {294const Node = union(enum) {
301 Content: []const u8,295 Content: []const u8,
302 Nav,296 Nav,
297 Builtin,
303 HeaderOpen: HeaderOpen,298 HeaderOpen: HeaderOpen,
304 SeeAlso: []const SeeAlsoItem,299 SeeAlso: []const SeeAlsoItem,
305 Code: Code,300 Code: Code,
...@@ -356,6 +351,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {...@@ -356,6 +351,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
356 _ = try eatToken(tokenizer, Token.Id.BracketClose);351 _ = try eatToken(tokenizer, Token.Id.BracketClose);
357352
358 try nodes.append(Node.Nav);353 try nodes.append(Node.Nav);
354 } else if (mem.eql(u8, tag_name, "builtin")) {
355 _ = try eatToken(tokenizer, Token.Id.BracketClose);
356 try nodes.append(Node.Builtin);
359 } else if (mem.eql(u8, tag_name, "header_open")) {357 } else if (mem.eql(u8, tag_name, "header_open")) {
360 _ = try eatToken(tokenizer, Token.Id.Separator);358 _ = try eatToken(tokenizer, Token.Id.Separator);
361 const content_token = try eatToken(tokenizer, Token.Id.TagContent);359 const content_token = try eatToken(tokenizer, Token.Id.TagContent);
...@@ -690,6 +688,9 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {...@@ -690,6 +688,9 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {
690688
691fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void {689fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void {
692 var code_progress_index: usize = 0;690 var code_progress_index: usize = 0;
691
692 const builtin_code = try escapeHtml(allocator, try getBuiltinCode(allocator, zig_exe));
693
693 for (toc.nodes) |node| {694 for (toc.nodes) |node| {
694 switch (node) {695 switch (node) {
695 Node.Content => |data| {696 Node.Content => |data| {
...@@ -704,6 +705,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -704,6 +705,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
704 Node.Nav => {705 Node.Nav => {
705 try out.write(toc.toc);706 try out.write(toc.toc);
706 },707 },
708 Node.Builtin => {
709 try out.print("<pre><code class=\"zig\">{}</code></pre>", builtin_code);
710 },
707 Node.HeaderOpen => |info| {711 Node.HeaderOpen => |info| {
708 try out.print("<h{} id=\"{}\">{}</h{}>\n", info.n, info.url, info.name, info.n);712 try out.print("<h{} id=\"{}\">{}</h{}>\n", info.n, info.url, info.name, info.n);
709 },713 },
...@@ -954,6 +958,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -954,6 +958,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
954 var build_args = std.ArrayList([]const u8).init(allocator);958 var build_args = std.ArrayList([]const u8).init(allocator);
955 defer build_args.deinit();959 defer build_args.deinit();
956960
961 const name_plus_h_ext = try std.fmt.allocPrint(allocator, "{}.h", code.name);
962 const output_h_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_h_ext);
963
957 try build_args.appendSlice([][]const u8{964 try build_args.appendSlice([][]const u8{
958 zig_exe,965 zig_exe,
959 "build-obj",966 "build-obj",
...@@ -962,6 +969,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -962,6 +969,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
962 "on",969 "on",
963 "--output",970 "--output",
964 tmp_obj_file_name,971 tmp_obj_file_name,
972 "--output-h",
973 output_h_file_name,
965 });974 });
966975
967 if (!code.is_inline) {976 if (!code.is_inline) {
...@@ -1060,3 +1069,11 @@ fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.Ex...@@ -1060,3 +1069,11 @@ fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.Ex
1060 }1069 }
1061 return result;1070 return result;
1062}1071}
1072
1073fn getBuiltinCode(allocator: *mem.Allocator, zig_exe: []const u8) ![]const u8 {
1074 const result = try exec(allocator, []const []const u8{
1075 zig_exe,
1076 "builtin",
1077 });
1078 return result.stdout;
1079}
doc/langref.html.in+609-511
...@@ -156,18 +156,18 @@ pub fn main() void {...@@ -156,18 +156,18 @@ pub fn main() void {
156 true or false,156 true or false,
157 !true);157 !true);
158 158
159 // nullable159 // optional
160 var nullable_value: ?[]const u8 = null;160 var optional_value: ?[]const u8 = null;
161 assert(nullable_value == null);161 assert(optional_value == null);
162162
163 warn("\nnullable 1\ntype: {}\nvalue: {}\n",163 warn("\noptional 1\ntype: {}\nvalue: {}\n",
164 @typeName(@typeOf(nullable_value)), nullable_value);164 @typeName(@typeOf(optional_value)), optional_value);
165165
166 nullable_value = "hi";166 optional_value = "hi";
167 assert(nullable_value != null);167 assert(optional_value != null);
168168
169 warn("\nnullable 2\ntype: {}\nvalue: {}\n",169 warn("\noptional 2\ntype: {}\nvalue: {}\n",
170 @typeName(@typeOf(nullable_value)), nullable_value);170 @typeName(@typeOf(optional_value)), optional_value);
171171
172 // error union172 // error union
173 var number_or_error: error!i32 = error.ArgNotFound;173 var number_or_error: error!i32 = error.ArgNotFound;
...@@ -428,7 +428,7 @@ pub fn main() void {...@@ -428,7 +428,7 @@ pub fn main() void {
428 </tr>428 </tr>
429 <tr>429 <tr>
430 <td><code>null</code></td>430 <td><code>null</code></td>
431 <td>used to set a nullable type to <code>null</code></td>431 <td>used to set an optional type to <code>null</code></td>
432 </tr>432 </tr>
433 <tr>433 <tr>
434 <td><code>undefined</code></td>434 <td><code>undefined</code></td>
...@@ -440,7 +440,7 @@ pub fn main() void {...@@ -440,7 +440,7 @@ pub fn main() void {
440 </tr>440 </tr>
441 </table>441 </table>
442 </div>442 </div>
443 {#see_also|Nullables|this#}443 {#see_also|Optionals|this#}
444 {#header_close#}444 {#header_close#}
445 {#header_open|String Literals#}445 {#header_open|String Literals#}
446 {#code_begin|test#}446 {#code_begin|test#}
...@@ -590,6 +590,7 @@ test "initialization" {...@@ -590,6 +590,7 @@ test "initialization" {
590 x = 1;590 x = 1;
591}591}
592 {#code_end#}592 {#code_end#}
593 {#header_open|undefined#}
593 <p>Use <code>undefined</code> to leave variables uninitialized:</p>594 <p>Use <code>undefined</code> to leave variables uninitialized:</p>
594 {#code_begin|test#}595 {#code_begin|test#}
595const assert = @import("std").debug.assert;596const assert = @import("std").debug.assert;
...@@ -602,6 +603,7 @@ test "init with undefined" {...@@ -602,6 +603,7 @@ test "init with undefined" {
602 {#code_end#}603 {#code_end#}
603 {#header_close#}604 {#header_close#}
604 {#header_close#}605 {#header_close#}
606 {#header_close#}
605 {#header_open|Integers#}607 {#header_open|Integers#}
606 {#header_open|Integer Literals#}608 {#header_open|Integer Literals#}
607 {#code_begin|syntax#}609 {#code_begin|syntax#}
...@@ -985,10 +987,10 @@ a ^= b</code></pre></td>...@@ -985,10 +987,10 @@ a ^= b</code></pre></td>
985 </td>987 </td>
986 </tr>988 </tr>
987 <tr>989 <tr>
988 <td><pre><code class="zig">a ?? b</code></pre></td>990 <td><pre><code class="zig">a orelse b</code></pre></td>
989 <td>991 <td>
990 <ul>992 <ul>
991 <li>{#link|Nullables#}</li>993 <li>{#link|Optionals#}</li>
992 </ul>994 </ul>
993 </td>995 </td>
994 <td>If <code>a</code> is <code>null</code>,996 <td>If <code>a</code> is <code>null</code>,
...@@ -998,24 +1000,24 @@ a ^= b</code></pre></td>...@@ -998,24 +1000,24 @@ a ^= b</code></pre></td>
998 </td>1000 </td>
999 <td>1001 <td>
1000 <pre><code class="zig">const value: ?u32 = null;1002 <pre><code class="zig">const value: ?u32 = null;
1001const unwrapped = value ?? 1234;1003const unwrapped = value orelse 1234;
1002unwrapped == 1234</code></pre>1004unwrapped == 1234</code></pre>
1003 </td>1005 </td>
1004 </tr>1006 </tr>
1005 <tr>1007 <tr>
1006 <td><pre><code class="zig">??a</code></pre></td>1008 <td><pre><code class="zig">a.?</code></pre></td>
1007 <td>1009 <td>
1008 <ul>1010 <ul>
1009 <li>{#link|Nullables#}</li>1011 <li>{#link|Optionals#}</li>
1010 </ul>1012 </ul>
1011 </td>1013 </td>
1012 <td>1014 <td>
1013 Equivalent to:1015 Equivalent to:
1014 <pre><code class="zig">a ?? unreachable</code></pre>1016 <pre><code class="zig">a orelse unreachable</code></pre>
1015 </td>1017 </td>
1016 <td>1018 <td>
1017 <pre><code class="zig">const value: ?u32 = 5678;1019 <pre><code class="zig">const value: ?u32 = 5678;
1018??value == 5678</code></pre>1020value.? == 5678</code></pre>
1019 </td>1021 </td>
1020 </tr>1022 </tr>
1021 <tr>1023 <tr>
...@@ -1103,7 +1105,7 @@ unwrapped == 1234</code></pre>...@@ -1103,7 +1105,7 @@ unwrapped == 1234</code></pre>
1103 <td><pre><code class="zig">a == null<code></pre></td>1105 <td><pre><code class="zig">a == null<code></pre></td>
1104 <td>1106 <td>
1105 <ul>1107 <ul>
1106 <li>{#link|Nullables#}</li>1108 <li>{#link|Optionals#}</li>
1107 </ul>1109 </ul>
1108 </td>1110 </td>
1109 <td>1111 <td>
...@@ -1261,15 +1263,31 @@ const ptr = &amp;x;...@@ -1261,15 +1263,31 @@ const ptr = &amp;x;
1261x.* == 1234</code></pre>1263x.* == 1234</code></pre>
1262 </td>1264 </td>
1263 </tr>1265 </tr>
1266 <tr>
1267 <td><pre><code class="zig">a || b<code></pre></td>
1268 <td>
1269 <ul>
1270 <li>{#link|Error Set Type#}</li>
1271 </ul>
1272 </td>
1273 <td>
1274 {#link|Merging Error Sets#}
1275 </td>
1276 <td>
1277 <pre><code class="zig">const A = error{One};
1278const B = error{Two};
1279(A || B) == error{One, Two}</code></pre>
1280 </td>
1281 </tr>
1264 </table>1282 </table>
1265 </div>1283 </div>
1266 {#header_close#}1284 {#header_close#}
1267 {#header_open|Precedence#}1285 {#header_open|Precedence#}
1268 <pre><code>x() x[] x.y1286 <pre><code>x() x[] x.y
1269a!b1287a!b
1270!x -x -%x ~x &amp;x ?x ??x1288!x -x -%x ~x &amp;x ?x
1271x{} x.*1289x{} x.* x.?
1272! * / % ** *%1290! * / % ** *% ||
1273+ - ++ +% -%1291+ - ++ +% -%
1274&lt;&lt; &gt;&gt;1292&lt;&lt; &gt;&gt;
1275&amp;1293&amp;
...@@ -1278,7 +1296,7 @@ x{} x.*...@@ -1278,7 +1296,7 @@ x{} x.*
1278== != &lt; &gt; &lt;= &gt;=1296== != &lt; &gt; &lt;= &gt;=
1279and1297and
1280or1298or
1281?? catch1299orelse catch
1282= *= /= %= += -= &lt;&lt;= &gt;&gt;= &amp;= ^= |=</code></pre>1300= *= /= %= += -= &lt;&lt;= &gt;&gt;= &amp;= ^= |=</code></pre>
1283 {#header_close#}1301 {#header_close#}
1284 {#header_close#}1302 {#header_close#}
...@@ -1483,17 +1501,17 @@ test "volatile" {...@@ -1483,17 +1501,17 @@ test "volatile" {
1483 assert(@typeOf(mmio_ptr) == *volatile u8);1501 assert(@typeOf(mmio_ptr) == *volatile u8);
1484}1502}
14851503
1486test "nullable pointers" {1504test "optional pointers" {
1487 // Pointers cannot be null. If you want a null pointer, use the nullable1505 // Pointers cannot be null. If you want a null pointer, use the optional
1488 // prefix `?` to make the pointer type nullable.1506 // prefix `?` to make the pointer type optional.
1489 var ptr: ?*i32 = null;1507 var ptr: ?*i32 = null;
14901508
1491 var x: i32 = 1;1509 var x: i32 = 1;
1492 ptr = &x;1510 ptr = &x;
14931511
1494 assert((??ptr).* == 1);1512 assert(ptr.?.* == 1);
14951513
1496 // Nullable pointers are the same size as normal pointers, because pointer1514 // Optional pointers are the same size as normal pointers, because pointer
1497 // value 0 is used as the null value.1515 // value 0 is used as the null value.
1498 assert(@sizeOf(?*i32) == @sizeOf(*i32));1516 assert(@sizeOf(?*i32) == @sizeOf(*i32));
1499}1517}
...@@ -1832,7 +1850,7 @@ test "linked list" {...@@ -1832,7 +1850,7 @@ test "linked list" {
1832 .last = &node,1850 .last = &node,
1833 .len = 1,1851 .len = 1,
1834 };1852 };
1835 assert((??list2.first).data == 1234);1853 assert(list2.first.?.data == 1234);
1836}1854}
1837 {#code_end#}1855 {#code_end#}
1838 {#see_also|comptime|@fieldParentPtr#}1856 {#see_also|comptime|@fieldParentPtr#}
...@@ -2270,7 +2288,7 @@ fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {...@@ -2270,7 +2288,7 @@ fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
2270}2288}
22712289
2272test "while null capture" {2290test "while null capture" {
2273 // Just like if expressions, while loops can take a nullable as the2291 // Just like if expressions, while loops can take an optional as the
2274 // condition and capture the payload. When null is encountered the loop2292 // condition and capture the payload. When null is encountered the loop
2275 // exits.2293 // exits.
2276 var sum1: u32 = 0;2294 var sum1: u32 = 0;
...@@ -2280,7 +2298,7 @@ test "while null capture" {...@@ -2280,7 +2298,7 @@ test "while null capture" {
2280 }2298 }
2281 assert(sum1 == 3);2299 assert(sum1 == 3);
22822300
2283 // The else branch is allowed on nullable iteration. In this case, it will2301 // The else branch is allowed on optional iteration. In this case, it will
2284 // be executed on the first null value encountered.2302 // be executed on the first null value encountered.
2285 var sum2: u32 = 0;2303 var sum2: u32 = 0;
2286 numbers_left = 3;2304 numbers_left = 3;
...@@ -2340,7 +2358,7 @@ fn typeNameLength(comptime T: type) usize {...@@ -2340,7 +2358,7 @@ fn typeNameLength(comptime T: type) usize {
2340 return @typeName(T).len;2358 return @typeName(T).len;
2341}2359}
2342 {#code_end#}2360 {#code_end#}
2343 {#see_also|if|Nullables|Errors|comptime|unreachable#}2361 {#see_also|if|Optionals|Errors|comptime|unreachable#}
2344 {#header_close#}2362 {#header_close#}
2345 {#header_open|for#}2363 {#header_open|for#}
2346 {#code_begin|test|for#}2364 {#code_begin|test|for#}
...@@ -2400,7 +2418,7 @@ test "for else" {...@@ -2400,7 +2418,7 @@ test "for else" {
2400 if (value == null) {2418 if (value == null) {
2401 break 9;2419 break 9;
2402 } else {2420 } else {
2403 sum += ??value;2421 sum += value.?;
2404 }2422 }
2405 } else blk: {2423 } else blk: {
2406 assert(sum == 7);2424 assert(sum == 7);
...@@ -2461,7 +2479,7 @@ test "if boolean" {...@@ -2461,7 +2479,7 @@ test "if boolean" {
2461 assert(result == 47);2479 assert(result == 47);
2462}2480}
24632481
2464test "if nullable" {2482test "if optional" {
2465 // If expressions test for null.2483 // If expressions test for null.
24662484
2467 const a: ?u32 = 0;2485 const a: ?u32 = 0;
...@@ -2544,7 +2562,7 @@ test "if error union" {...@@ -2544,7 +2562,7 @@ test "if error union" {
2544 }2562 }
2545}2563}
2546 {#code_end#}2564 {#code_end#}
2547 {#see_also|Nullables|Errors#}2565 {#see_also|Optionals|Errors#}
2548 {#header_close#}2566 {#header_close#}
2549 {#header_open|defer#}2567 {#header_open|defer#}
2550 {#code_begin|test|defer#}2568 {#code_begin|test|defer#}
...@@ -2983,6 +3001,7 @@ test "parse u64" {...@@ -2983,6 +3001,7 @@ test "parse u64" {
2983 <li>You know with complete certainty it will not return an error, so want to unconditionally unwrap it.</li>3001 <li>You know with complete certainty it will not return an error, so want to unconditionally unwrap it.</li>
2984 <li>You want to take a different action for each possible error.</li>3002 <li>You want to take a different action for each possible error.</li>
2985 </ul>3003 </ul>
3004 {#header_open|catch#}
2986 <p>If you want to provide a default value, you can use the <code>catch</code> binary operator:</p>3005 <p>If you want to provide a default value, you can use the <code>catch</code> binary operator:</p>
2987 {#code_begin|syntax#}3006 {#code_begin|syntax#}
2988fn doAThing(str: []u8) void {3007fn doAThing(str: []u8) void {
...@@ -2995,6 +3014,8 @@ fn doAThing(str: []u8) void {...@@ -2995,6 +3014,8 @@ fn doAThing(str: []u8) void {
2995 a default value of 13. The type of the right hand side of the binary <code>catch</code> operator must3014 a default value of 13. The type of the right hand side of the binary <code>catch</code> operator must
2996 match the unwrapped error union type, or be of type <code>noreturn</code>.3015 match the unwrapped error union type, or be of type <code>noreturn</code>.
2997 </p>3016 </p>
3017 {#header_close#}
3018 {#header_open|try#}
2998 <p>Let's say you wanted to return the error if you got one, otherwise continue with the3019 <p>Let's say you wanted to return the error if you got one, otherwise continue with the
2999 function logic:</p>3020 function logic:</p>
3000 {#code_begin|syntax#}3021 {#code_begin|syntax#}
...@@ -3017,6 +3038,7 @@ fn doAThing(str: []u8) !void {...@@ -3017,6 +3038,7 @@ fn doAThing(str: []u8) !void {
3017 from the current function with the same error. Otherwise, the expression results in3038 from the current function with the same error. Otherwise, the expression results in
3018 the unwrapped value.3039 the unwrapped value.
3019 </p>3040 </p>
3041 {#header_close#}
3020 <p>3042 <p>
3021 Maybe you know with complete certainty that an expression will never be an error.3043 Maybe you know with complete certainty that an expression will never be an error.
3022 In this case you can do this:3044 In this case you can do this:
...@@ -3031,7 +3053,7 @@ fn doAThing(str: []u8) !void {...@@ -3031,7 +3053,7 @@ fn doAThing(str: []u8) !void {
3031 </p>3053 </p>
3032 <p>3054 <p>
3033 Finally, you may want to take a different action for every situation. For that, we combine3055 Finally, you may want to take a different action for every situation. For that, we combine
3034 the <code>if</code> and <code>switch</code> expression:3056 the {#link|if#} and {#link|switch#} expression:
3035 </p>3057 </p>
3036 {#code_begin|syntax#}3058 {#code_begin|syntax#}
3037fn doAThing(str: []u8) void {3059fn doAThing(str: []u8) void {
...@@ -3046,9 +3068,10 @@ fn doAThing(str: []u8) void {...@@ -3046,9 +3068,10 @@ fn doAThing(str: []u8) void {
3046 }3068 }
3047}3069}
3048 {#code_end#}3070 {#code_end#}
3071 {#header_open|errdefer#}
3049 <p>3072 <p>
3050 The other component to error handling is defer statements.3073 The other component to error handling is defer statements.
3051 In addition to an unconditional <code>defer</code>, Zig has <code>errdefer</code>,3074 In addition to an unconditional {#link|defer#}, Zig has <code>errdefer</code>,
3052 which evaluates the deferred expression on block exit path if and only if3075 which evaluates the deferred expression on block exit path if and only if
3053 the function returned with an error from the block.3076 the function returned with an error from the block.
3054 </p>3077 </p>
...@@ -3062,7 +3085,7 @@ fn createFoo(param: i32) !Foo {...@@ -3062,7 +3085,7 @@ fn createFoo(param: i32) !Foo {
3062 // but we want to return it if the function succeeds.3085 // but we want to return it if the function succeeds.
3063 errdefer deallocateFoo(foo);3086 errdefer deallocateFoo(foo);
30643087
3065 const tmp_buf = allocateTmpBuffer() ?? return error.OutOfMemory;3088 const tmp_buf = allocateTmpBuffer() orelse return error.OutOfMemory;
3066 // tmp_buf is truly a temporary resource, and we for sure want to clean it up3089 // tmp_buf is truly a temporary resource, and we for sure want to clean it up
3067 // before this block leaves scope3090 // before this block leaves scope
3068 defer deallocateTmpBuffer(tmp_buf);3091 defer deallocateTmpBuffer(tmp_buf);
...@@ -3079,6 +3102,7 @@ fn createFoo(param: i32) !Foo {...@@ -3079,6 +3102,7 @@ fn createFoo(param: i32) !Foo {
3079 the verbosity and cognitive overhead of trying to make sure every exit path3102 the verbosity and cognitive overhead of trying to make sure every exit path
3080 is covered. The deallocation code is always directly following the allocation code.3103 is covered. The deallocation code is always directly following the allocation code.
3081 </p>3104 </p>
3105 {#header_close#}
3082 <p>3106 <p>
3083 A couple of other tidbits about error handling:3107 A couple of other tidbits about error handling:
3084 </p>3108 </p>
...@@ -3117,7 +3141,50 @@ test "error union" {...@@ -3117,7 +3141,50 @@ test "error union" {
3117 comptime assert(@typeOf(foo).ErrorSet == error);3141 comptime assert(@typeOf(foo).ErrorSet == error);
3118}3142}
3119 {#code_end#}3143 {#code_end#}
3120 <p>TODO the <code>||</code> operator for error sets</p>3144 {#header_open|Merging Error Sets#}
3145 <p>
3146 Use the <code>||</code> operator to merge two error sets together. The resulting
3147 error set contains the errors of both error sets. Doc comments from the left-hand
3148 side override doc comments from the right-hand side. In this example, the doc
3149 comments for <code>C.PathNotFound</code> is <code>A doc comment</code>.
3150 </p>
3151 <p>
3152 This is especially useful for functions which return different error sets depending
3153 on {#link|comptime#} branches. For example, the Zig standard library uses
3154 <code>LinuxFileOpenError || WindowsFileOpenError</code> for the error set of opening
3155 files.
3156 </p>
3157 {#code_begin|test#}
3158const A = error{
3159 NotDir,
3160
3161 /// A doc comment
3162 PathNotFound,
3163};
3164const B = error{
3165 OutOfMemory,
3166
3167 /// B doc comment
3168 PathNotFound,
3169};
3170
3171const C = A || B;
3172
3173fn foo() C!void {
3174 return error.NotDir;
3175}
3176
3177test "merge error sets" {
3178 if (foo()) {
3179 @panic("unexpected");
3180 } else |err| switch (err) {
3181 error.OutOfMemory => @panic("unexpected"),
3182 error.PathNotFound => @panic("unexpected"),
3183 error.NotDir => {},
3184 }
3185}
3186 {#code_end#}
3187 {#header_close#}
3121 {#header_open|Inferred Error Sets#}3188 {#header_open|Inferred Error Sets#}
3122 <p>3189 <p>
3123 Because many functions in Zig return a possible error, Zig supports inferring the error set.3190 Because many functions in Zig return a possible error, Zig supports inferring the error set.
...@@ -3164,27 +3231,194 @@ test "inferred error set" {...@@ -3164,27 +3231,194 @@ test "inferred error set" {
3164 {#header_close#}3231 {#header_close#}
3165 {#header_close#}3232 {#header_close#}
3166 {#header_open|Error Return Traces#}3233 {#header_open|Error Return Traces#}
3167 <p>TODO</p>3234 <p>
3235 Error Return Traces show all the points in the code that an error was returned to the calling function. This makes it practical to use {#link|try#} everywhere and then still be able to know what happened if an error ends up bubbling all the way out of your application.
3236 </p>
3237 {#code_begin|exe_err#}
3238pub fn main() !void {
3239 try foo(12);
3240}
3241
3242fn foo(x: i32) !void {
3243 if (x >= 5) {
3244 try bar();
3245 } else {
3246 try bang2();
3247 }
3248}
3249
3250fn bar() !void {
3251 if (baz()) {
3252 try quux();
3253 } else |err| switch (err) {
3254 error.FileNotFound => try hello(),
3255 else => try another(),
3256 }
3257}
3258
3259fn baz() !void {
3260 try bang1();
3261}
3262
3263fn quux() !void {
3264 try bang2();
3265}
3266
3267fn hello() !void {
3268 try bang2();
3269}
3270
3271fn another() !void {
3272 try bang1();
3273}
3274
3275fn bang1() !void {
3276 return error.FileNotFound;
3277}
3278
3279fn bang2() !void {
3280 return error.PermissionDenied;
3281}
3282 {#code_end#}
3283 <p>
3284 Look closely at this example. This is no stack trace.
3285 </p>
3286 <p>
3287 You can see that the final error bubbled up was <code>PermissionDenied</code>,
3288 but the original error that started this whole thing was <code>FileNotFound</code>. In the <code>bar</code> function, the code handles the original error code,
3289 and then returns another one, from the switch statement. Error Return Traces make this clear, whereas a stack trace would look like this:
3290 </p>
3291 {#code_begin|exe_err#}
3292pub fn main() void {
3293 foo(12);
3294}
3295
3296fn foo(x: i32) void {
3297 if (x >= 5) {
3298 bar();
3299 } else {
3300 bang2();
3301 }
3302}
3303
3304fn bar() void {
3305 if (baz()) {
3306 quux();
3307 } else {
3308 hello();
3309 }
3310}
3311
3312fn baz() bool {
3313 return bang1();
3314}
3315
3316fn quux() void {
3317 bang2();
3318}
3319
3320fn hello() void {
3321 bang2();
3322}
3323
3324fn bang1() bool {
3325 return false;
3326}
3327
3328fn bang2() void {
3329 @panic("PermissionDenied");
3330}
3331 {#code_end#}
3332 <p>
3333 Here, the stack trace does not explain how the control
3334 flow in <code>bar</code> got to the <code>hello()</code> call.
3335 One would have to open a debugger or further instrument the application
3336 in order to find out. The error return trace, on the other hand,
3337 shows exactly how the error bubbled up.
3338 </p>
3339 <p>
3340 This debugging feature makes it easier to iterate quickly on code that
3341 robustly handles all error conditions. This means that Zig developers
3342 will naturally find themselves writing correct, robust code in order
3343 to increase their development pace.
3344 </p>
3345 <p>
3346 Error Return Traces are enabled by default in {#link|Debug#} and {#link|ReleaseSafe#} builds and disabled by default in {#link|ReleaseFast#} and {#link|ReleaseSmall#} builds.
3347 </p>
3348 <p>
3349 There are a few ways to activate this error return tracing feature:
3350 </p>
3351 <ul>
3352 <li>Return an error from main</li>
3353 <li>An error makes its way to <code>catch unreachable</code> and you have not overridden the default panic handler</li>
3354 <li>Use {#link|errorReturnTrace#} to access the current return trace. You can use <code>std.debug.dumpStackTrace</code> to print it. This function returns comptime-known {#link|null#} when building without error return tracing support.</li>
3355 </ul>
3356 {#header_open|Implementation Details#}
3357 <p>
3358 To analyze performance cost, there are two cases:
3359 </p>
3360 <ul>
3361 <li>when no errors are returned</li>
3362 <li>when returning errors</li>
3363 </ul>
3364 <p>
3365 For the case when no errors are returned, the cost is a single memory write operation, only in the first non-failable function in the call graph that calls a failable function, i.e. when a function returning <code>void</code> calls a function returning <code>error</code>.
3366 This is to initialize this struct in the stack memory:
3367 </p>
3368 {#code_begin|syntax#}
3369pub const StackTrace = struct {
3370 index: usize,
3371 instruction_addresses: [N]usize,
3372};
3373 {#code_end#}
3374 <p>
3375 Here, N is the maximum function call depth as determined by call graph analysis. Recursion is ignored and counts for 2.
3376 </p>
3377 <p>
3378 A pointer to <code>StackTrace</code> is passed as a secret parameter to every function that can return an error, but it's always the first parameter, so it can likely sit in a register and stay there.
3379 </p>
3380 <p>
3381 That's it for the path when no errors occur. It's practically free in terms of performance.
3382 </p>
3383 <p>
3384 When generating the code for a function that returns an error, just before the <code>return</code> statement (only for the <code>return</code> statements that return errors), Zig generates a call to this function:
3385 </p>
3386 {#code_begin|syntax#}
3387// marked as "no-inline" in LLVM IR
3388fn __zig_return_error(stack_trace: *StackTrace) void {
3389 stack_trace.instruction_addresses[stack_trace.index] = @returnAddress();
3390 stack_trace.index = (stack_trace.index + 1) % N;
3391}
3392 {#code_end#}
3393 <p>
3394 The cost is 2 math operations plus some memory reads and writes. The memory accessed is constrained and should remain cached for the duration of the error return bubbling.
3395 </p>
3396 <p>
3397 As for code size cost, 1 function call before a return statement is no big deal. Even so,
3398 I have <a href="https://github.com/ziglang/zig/issues/690">a plan</a> to make the call to
3399 <code>__zig_return_error</code> a tail call, which brings the code size cost down to actually zero. What is a return statement in code without error return tracing can become a jump instruction in code with error return tracing.
3400 </p>
3168 {#header_close#}3401 {#header_close#}
3169 {#header_close#}3402 {#header_close#}
3170 {#header_open|Nullables#}3403 {#header_close#}
3404 {#header_open|Optionals#}
3171 <p>3405 <p>
3172 One area that Zig provides safety without compromising efficiency or3406 One area that Zig provides safety without compromising efficiency or
3173 readability is with the nullable type.3407 readability is with the optional type.
3174 </p>3408 </p>
3175 <p>3409 <p>
3176 The question mark symbolizes the nullable type. You can convert a type to a nullable3410 The question mark symbolizes the optional type. You can convert a type to an optional
3177 type by putting a question mark in front of it, like this:3411 type by putting a question mark in front of it, like this:
3178 </p>3412 </p>
3179 {#code_begin|syntax#}3413 {#code_begin|syntax#}
3180// normal integer3414// normal integer
3181const normal_int: i32 = 1234;3415const normal_int: i32 = 1234;
31823416
3183// nullable integer3417// optional integer
3184const nullable_int: ?i32 = 5678;3418const optional_int: ?i32 = 5678;
3185 {#code_end#}3419 {#code_end#}
3186 <p>3420 <p>
3187 Now the variable <code>nullable_int</code> could be an <code>i32</code>, or <code>null</code>.3421 Now the variable <code>optional_int</code> could be an <code>i32</code>, or <code>null</code>.
3188 </p>3422 </p>
3189 <p>3423 <p>
3190 Instead of integers, let's talk about pointers. Null references are the source of many runtime3424 Instead of integers, let's talk about pointers. Null references are the source of many runtime
...@@ -3193,8 +3427,8 @@ const nullable_int: ?i32 = 5678;...@@ -3193,8 +3427,8 @@ const nullable_int: ?i32 = 5678;
3193 </p>3427 </p>
3194 <p>Zig does not have them.</p>3428 <p>Zig does not have them.</p>
3195 <p>3429 <p>
3196 Instead, you can use a nullable pointer. This secretly compiles down to a normal pointer,3430 Instead, you can use an optional pointer. This secretly compiles down to a normal pointer,
3197 since we know we can use 0 as the null value for the nullable type. But the compiler3431 since we know we can use 0 as the null value for the optional type. But the compiler
3198 can check your work and make sure you don't assign null to something that can't be null.3432 can check your work and make sure you don't assign null to something that can't be null.
3199 </p>3433 </p>
3200 <p>3434 <p>
...@@ -3219,14 +3453,14 @@ struct Foo *do_a_thing(void) {...@@ -3219,14 +3453,14 @@ struct Foo *do_a_thing(void) {
3219extern fn malloc(size: size_t) ?*u8;3453extern fn malloc(size: size_t) ?*u8;
32203454
3221fn doAThing() ?*Foo {3455fn doAThing() ?*Foo {
3222 const ptr = malloc(1234) ?? return null;3456 const ptr = malloc(1234) orelse return null;
3223 // ...3457 // ...
3224}3458}
3225 {#code_end#}3459 {#code_end#}
3226 <p>3460 <p>
3227 Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr"3461 Here, Zig is at least as convenient, if not more, than C. And, the type of "ptr"
3228 is <code>*u8</code> <em>not</em> <code>?*u8</code>. The <code>??</code> operator3462 is <code>*u8</code> <em>not</em> <code>?*u8</code>. The <code>orelse</code> keyword
3229 unwrapped the nullable type and therefore <code>ptr</code> is guaranteed to be non-null everywhere3463 unwrapped the optional type and therefore <code>ptr</code> is guaranteed to be non-null everywhere
3230 it is used in the function.3464 it is used in the function.
3231 </p>3465 </p>
3232 <p>3466 <p>
...@@ -3245,10 +3479,10 @@ fn doAThing() ?*Foo {...@@ -3245,10 +3479,10 @@ fn doAThing() ?*Foo {
3245 In Zig you can accomplish the same thing:3479 In Zig you can accomplish the same thing:
3246 </p>3480 </p>
3247 {#code_begin|syntax#}3481 {#code_begin|syntax#}
3248fn doAThing(nullable_foo: ?*Foo) void {3482fn doAThing(optional_foo: ?*Foo) void {
3249 // do some stuff3483 // do some stuff
32503484
3251 if (nullable_foo) |foo| {3485 if (optional_foo) |foo| {
3252 doSomethingWithFoo(foo);3486 doSomethingWithFoo(foo);
3253 }3487 }
32543488
...@@ -3257,7 +3491,7 @@ fn doAThing(nullable_foo: ?*Foo) void {...@@ -3257,7 +3491,7 @@ fn doAThing(nullable_foo: ?*Foo) void {
3257 {#code_end#}3491 {#code_end#}
3258 <p>3492 <p>
3259 Once again, the notable thing here is that inside the if block,3493 Once again, the notable thing here is that inside the if block,
3260 <code>foo</code> is no longer a nullable pointer, it is a pointer, which3494 <code>foo</code> is no longer an optional pointer, it is a pointer, which
3261 cannot be null.3495 cannot be null.
3262 </p>3496 </p>
3263 <p>3497 <p>
...@@ -3267,22 +3501,31 @@ fn doAThing(nullable_foo: ?*Foo) void {...@@ -3267,22 +3501,31 @@ fn doAThing(nullable_foo: ?*Foo) void {
3267 The optimizer can sometimes make better decisions knowing that pointer arguments3501 The optimizer can sometimes make better decisions knowing that pointer arguments
3268 cannot be null.3502 cannot be null.
3269 </p>3503 </p>
3270 {#header_open|Nullable Type#}3504 {#header_open|Optional Type#}
3271 <p>A nullable is created by putting <code>?</code> in front of a type. You can use compile-time3505 <p>An optional is created by putting <code>?</code> in front of a type. You can use compile-time
3272 reflection to access the child type of a nullable:</p>3506 reflection to access the child type of an optional:</p>
3273 {#code_begin|test#}3507 {#code_begin|test#}
3274const assert = @import("std").debug.assert;3508const assert = @import("std").debug.assert;
32753509
3276test "nullable type" {3510test "optional type" {
3277 // Declare a nullable and implicitly cast from null:3511 // Declare an optional and implicitly cast from null:
3278 var foo: ?i32 = null;3512 var foo: ?i32 = null;
32793513
3280 // Implicitly cast from child type of a nullable3514 // Implicitly cast from child type of an optional
3281 foo = 1234;3515 foo = 1234;
32823516
3283 // Use compile-time reflection to access the child type of the nullable:3517 // Use compile-time reflection to access the child type of the optional:
3284 comptime assert(@typeOf(foo).Child == i32);3518 comptime assert(@typeOf(foo).Child == i32);
3285}3519}
3520 {#code_end#}
3521 {#header_close#}
3522 {#header_open|null#}
3523 <p>
3524 Just like {#link|undefined#}, <code>null</code> has its own type, and the only way to use it is to
3525 cast it to a different type:
3526 </p>
3527 {#code_begin|syntax#}
3528const optional_value: ?i32 = null;
3286 {#code_end#}3529 {#code_end#}
3287 {#header_close#}3530 {#header_close#}
3288 {#header_close#}3531 {#header_close#}
...@@ -3845,9 +4088,6 @@ pub fn printValue(self: *OutStream, value: var) !void {...@@ -3845,9 +4088,6 @@ pub fn printValue(self: *OutStream, value: var) !void {
3845 return self.printInt(T, value);4088 return self.printInt(T, value);
3846 } else if (@isFloat(T)) {4089 } else if (@isFloat(T)) {
3847 return self.printFloat(T, value);4090 return self.printFloat(T, value);
3848 } else if (@canImplicitCast([]const u8, value)) {
3849 const casted_value = ([]const u8)(value);
3850 return self.write(casted_value);
3851 } else {4091 } else {
3852 @compileError("Unable to print type '" ++ @typeName(T) ++ "'");4092 @compileError("Unable to print type '" ++ @typeName(T) ++ "'");
3853 }4093 }
...@@ -3911,6 +4151,277 @@ pub fn main() void {...@@ -3911,6 +4151,277 @@ pub fn main() void {
3911 <p>TODO: @fence()</p>4151 <p>TODO: @fence()</p>
3912 <p>TODO: @atomic rmw</p>4152 <p>TODO: @atomic rmw</p>
3913 <p>TODO: builtin atomic memory ordering enum</p>4153 <p>TODO: builtin atomic memory ordering enum</p>
4154 {#header_close#}
4155 {#header_open|Coroutines#}
4156 <p>
4157 A coroutine is a generalization of a function.
4158 </p>
4159 <p>
4160 When you call a function, it creates a stack frame,
4161 and then the function runs until it reaches a return
4162 statement, and then the stack frame is destroyed.
4163 At the callsite, the next line of code does not run
4164 until the function returns.
4165 </p>
4166 <p>
4167 A coroutine is like a function, but it can be suspended
4168 and resumed any number of times, and then it must be
4169 explicitly destroyed. When a coroutine suspends, it
4170 returns to the resumer.
4171 </p>
4172 {#header_open|Minimal Coroutine Example#}
4173 <p>
4174 Declare a coroutine with the <code>async</code> keyword.
4175 The expression in angle brackets must evaluate to a struct
4176 which has these fields:
4177 </p>
4178 <ul>
4179 <li><code>allocFn: fn (self: *Allocator, byte_count: usize, alignment: u29) Error![]u8</code> - where <code>Error</code> can be any error set.</li>
4180 <li><code>freeFn: fn (self: *Allocator, old_mem: []u8) void</code></li>
4181 </ul>
4182 <p>
4183 You may notice that this corresponds to the <code>std.mem.Allocator</code> interface.
4184 This makes it convenient to integrate with existing allocators. Note, however,
4185 that the language feature does not depend on the standard library, and any struct which
4186 has these fields is allowed.
4187 </p>
4188 <p>
4189 Omitting the angle bracket expression when defining an async function makes
4190 the function generic. Zig will infer the allocator type when the async function is called.
4191 </p>
4192 <p>
4193 Call a coroutine with the <code>async</code> keyword. Here, the expression in angle brackets
4194 is a pointer to the allocator struct that the coroutine expects.
4195 </p>
4196 <p>
4197 The result of an async function call is a <code>promise->T</code> type, where <code>T</code>
4198 is the return type of the async function. Once a promise has been created, it must be
4199 consumed, either with <code>cancel</code> or <code>await</code>:
4200 </p>
4201 <p>
4202 Async functions start executing when created, so in the following example, the entire
4203 async function completes before it is canceled:
4204 </p>
4205 {#code_begin|test#}
4206const std = @import("std");
4207const assert = std.debug.assert;
4208
4209var x: i32 = 1;
4210
4211test "create a coroutine and cancel it" {
4212 const p = try async<std.debug.global_allocator> simpleAsyncFn();
4213 comptime assert(@typeOf(p) == promise->void);
4214 cancel p;
4215 assert(x == 2);
4216}
4217async<*std.mem.Allocator> fn simpleAsyncFn() void {
4218 x += 1;
4219}
4220 {#code_end#}
4221 {#header_close#}
4222 {#header_open|Suspend and Resume#}
4223 <p>
4224 At any point, an async function may suspend itself. This causes control flow to
4225 return to the caller or resumer. The following code demonstrates where control flow
4226 goes:
4227 </p>
4228 {#code_begin|test#}
4229const std = @import("std");
4230const assert = std.debug.assert;
4231
4232test "coroutine suspend, resume, cancel" {
4233 seq('a');
4234 const p = try async<std.debug.global_allocator> testAsyncSeq();
4235 seq('c');
4236 resume p;
4237 seq('f');
4238 cancel p;
4239 seq('g');
4240
4241 assert(std.mem.eql(u8, points, "abcdefg"));
4242}
4243async fn testAsyncSeq() void {
4244 defer seq('e');
4245
4246 seq('b');
4247 suspend;
4248 seq('d');
4249}
4250var points = []u8{0} ** "abcdefg".len;
4251var index: usize = 0;
4252
4253fn seq(c: u8) void {
4254 points[index] = c;
4255 index += 1;
4256}
4257 {#code_end#}
4258 <p>
4259 When an async function suspends itself, it must be sure that it will be
4260 resumed or canceled somehow, for example by registering its promise handle
4261 in an event loop. Use a suspend capture block to gain access to the
4262 promise:
4263 </p>
4264 {#code_begin|test#}
4265const std = @import("std");
4266const assert = std.debug.assert;
4267
4268test "coroutine suspend with block" {
4269 const p = try async<std.debug.global_allocator> testSuspendBlock();
4270 std.debug.assert(!result);
4271 resume a_promise;
4272 std.debug.assert(result);
4273 cancel p;
4274}
4275
4276var a_promise: promise = undefined;
4277var result = false;
4278async fn testSuspendBlock() void {
4279 suspend |p| {
4280 comptime assert(@typeOf(p) == promise->void);
4281 a_promise = p;
4282 }
4283 result = true;
4284}
4285 {#code_end#}
4286 <p>
4287 Every suspend point in an async function represents a point at which the coroutine
4288 could be destroyed. If that happens, <code>defer</code> expressions that are in
4289 scope are run, as well as <code>errdefer</code> expressions.
4290 </p>
4291 <p>
4292 {#link|Await#} counts as a suspend point.
4293 </p>
4294 {#header_open|Breaking from Suspend Blocks#}
4295 <p>
4296 Suspend blocks support labeled break, just like {#link|while#} and {#link|for#}.
4297 </p>
4298 <p>
4299 Upon entering a <code>suspend</code> block, the coroutine is already considered
4300 suspended, and can be resumed. For example, if you started another kernel thread,
4301 and had that thread call <code>resume</code> on the promise handle provided by the
4302 <code>suspend</code> block, the new thread would begin executing after the suspend
4303 block, while the old thread continued executing the suspend block.
4304 </p>
4305 <p>
4306 However, if you use labeled <code>break</code> on the suspend block, the coroutine
4307 never returns to its resumer and continues executing.
4308 </p>
4309 {#code_begin|test#}
4310const std = @import("std");
4311const assert = std.debug.assert;
4312
4313test "break from suspend" {
4314 var buf: [500]u8 = undefined;
4315 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
4316 var my_result: i32 = 1;
4317 const p = try async<a> testBreakFromSuspend(&my_result);
4318 cancel p;
4319 std.debug.assert(my_result == 2);
4320}
4321async fn testBreakFromSuspend(my_result: *i32) void {
4322 s: suspend |p| {
4323 break :s;
4324 }
4325 my_result.* += 1;
4326 suspend;
4327 my_result.* += 1;
4328}
4329 {#code_end#}
4330 {#header_close#}
4331 {#header_close#}
4332 {#header_open|Await#}
4333 <p>
4334 The <code>await</code> keyword is used to coordinate with an async function's
4335 <code>return</code> statement.
4336 </p>
4337 <p>
4338 <code>await</code> is valid only in an <code>async</code> function, and it takes
4339 as an operand a promise handle.
4340 If the async function associated with the promise handle has already returned,
4341 then <code>await</code> destroys the target async function, and gives the return value.
4342 Otherwise, <code>await</code> suspends the current async function, registering its
4343 promise handle with the target coroutine. It becomes the target coroutine's responsibility
4344 to have ensured that it will be resumed or destroyed. When the target coroutine reaches
4345 its return statement, it gives the return value to the awaiter, destroys itself, and then
4346 resumes the awaiter.
4347 </p>
4348 <p>
4349 A promise handle must be consumed exactly once after it is created, either by <code>cancel</code> or <code>await</code>.
4350 </p>
4351 <p>
4352 <code>await</code> counts as a suspend point, and therefore at every <code>await</code>,
4353 a coroutine can be potentially destroyed, which would run <code>defer</code> and <code>errdefer</code> expressions.
4354 </p>
4355 {#code_begin|test#}
4356const std = @import("std");
4357const assert = std.debug.assert;
4358
4359var a_promise: promise = undefined;
4360var final_result: i32 = 0;
4361
4362test "coroutine await" {
4363 seq('a');
4364 const p = async<std.debug.global_allocator> amain() catch unreachable;
4365 seq('f');
4366 resume a_promise;
4367 seq('i');
4368 assert(final_result == 1234);
4369 assert(std.mem.eql(u8, seq_points, "abcdefghi"));
4370}
4371async fn amain() void {
4372 seq('b');
4373 const p = async another() catch unreachable;
4374 seq('e');
4375 final_result = await p;
4376 seq('h');
4377}
4378async fn another() i32 {
4379 seq('c');
4380 suspend |p| {
4381 seq('d');
4382 a_promise = p;
4383 }
4384 seq('g');
4385 return 1234;
4386}
4387
4388var seq_points = []u8{0} ** "abcdefghi".len;
4389var seq_index: usize = 0;
4390
4391fn seq(c: u8) void {
4392 seq_points[seq_index] = c;
4393 seq_index += 1;
4394}
4395 {#code_end#}
4396 <p>
4397 In general, <code>suspend</code> is lower level than <code>await</code>. Most application
4398 code will use only <code>async</code> and <code>await</code>, but event loop
4399 implementations will make use of <code>suspend</code> internally.
4400 </p>
4401 {#header_close#}
4402 {#header_open|Open Issues#}
4403 <p>
4404 There are a few issues with coroutines that are considered unresolved. Best be aware of them,
4405 as the situation is likely to change before 1.0.0:
4406 </p>
4407 <ul>
4408 <li>Async functions have optimizations disabled - even in release modes - due to an
4409 <a href="https://github.com/ziglang/zig/issues/802">LLVM bug</a>.
4410 </li>
4411 <li>
4412 There are some situations where we can know statically that there will not be
4413 memory allocation failure, but Zig still forces us to handle it.
4414 TODO file an issue for this and link it here.
4415 </li>
4416 <li>
4417 Zig does not take advantage of LLVM's allocation elision optimization for
4418 coroutines. It crashed LLVM when I tried to do it the first time. This is
4419 related to the other 2 bullet points here. See
4420 <a href="https://github.com/ziglang/zig/issues/802">#802</a>.
4421 </li>
4422 </ul>
4423 {#header_close#}
4424
3914 {#header_close#}4425 {#header_close#}
3915 {#header_open|Builtin Functions#}4426 {#header_open|Builtin Functions#}
3916 <p>4427 <p>
...@@ -4102,12 +4613,6 @@ comptime {...@@ -4102,12 +4613,6 @@ comptime {
4102 </p>4613 </p>
4103 {#see_also|Import from C Header File|@cImport|@cDefine|@cInclude#}4614 {#see_also|Import from C Header File|@cImport|@cDefine|@cInclude#}
4104 {#header_close#}4615 {#header_close#}
4105 {#header_open|@canImplicitCast#}
4106 <pre><code class="zig">@canImplicitCast(comptime T: type, value) bool</code></pre>
4107 <p>
4108 Returns whether a value can be implicitly casted to a given type.
4109 </p>
4110 {#header_close#}
4111 {#header_open|@clz#}4616 {#header_open|@clz#}
4112 <pre><code class="zig">@clz(x: T) U</code></pre>4617 <pre><code class="zig">@clz(x: T) U</code></pre>
4113 <p>4618 <p>
...@@ -4897,7 +5402,7 @@ pub const TypeId = enum {...@@ -4897,7 +5402,7 @@ pub const TypeId = enum {
4897 ComptimeInt,5402 ComptimeInt,
4898 Undefined,5403 Undefined,
4899 Null,5404 Null,
4900 Nullable,5405 Optional,
4901 ErrorUnion,5406 ErrorUnion,
4902 Error,5407 Error,
4903 Enum,5408 Enum,
...@@ -4931,7 +5436,7 @@ pub const TypeInfo = union(TypeId) {...@@ -4931,7 +5436,7 @@ pub const TypeInfo = union(TypeId) {
4931 ComptimeInt: void,5436 ComptimeInt: void,
4932 Undefined: void,5437 Undefined: void,
4933 Null: void,5438 Null: void,
4934 Nullable: Nullable,5439 Optional: Optional,
4935 ErrorUnion: ErrorUnion,5440 ErrorUnion: ErrorUnion,
4936 ErrorSet: ErrorSet,5441 ErrorSet: ErrorSet,
4937 Enum: Enum,5442 Enum: Enum,
...@@ -4984,7 +5489,7 @@ pub const TypeInfo = union(TypeId) {...@@ -4984,7 +5489,7 @@ pub const TypeInfo = union(TypeId) {
4984 defs: []Definition,5489 defs: []Definition,
4985 };5490 };
49865491
4987 pub const Nullable = struct {5492 pub const Optional = struct {
4988 child: type,5493 child: type,
4989 };5494 };
49905495
...@@ -5105,12 +5610,13 @@ pub const TypeInfo = union(TypeId) {...@@ -5105,12 +5610,13 @@ pub const TypeInfo = union(TypeId) {
5105 {#header_close#}5610 {#header_close#}
5106 {#header_open|Build Mode#}5611 {#header_open|Build Mode#}
5107 <p>5612 <p>
5108 Zig has three build modes:5613 Zig has four build modes:
5109 </p>5614 </p>
5110 <ul>5615 <ul>
5111 <li>{#link|Debug#} (default)</li>5616 <li>{#link|Debug#} (default)</li>
5112 <li>{#link|ReleaseFast#}</li>5617 <li>{#link|ReleaseFast#}</li>
5113 <li>{#link|ReleaseSafe#}</li>5618 <li>{#link|ReleaseSafe#}</li>
5619 <li>{#link|ReleaseSmall#}</li>
5114 </ul>5620 </ul>
5115 <p>5621 <p>
5116 To add standard build options to a <code>build.zig</code> file:5622 To add standard build options to a <code>build.zig</code> file:
...@@ -5127,14 +5633,16 @@ pub fn build(b: &Builder) void {...@@ -5127,14 +5633,16 @@ pub fn build(b: &Builder) void {
5127 <p>5633 <p>
5128 This causes these options to be available:5634 This causes these options to be available:
5129 </p>5635 </p>
5130 <pre><code class="shell"> -Drelease-safe=(bool) optimizations on and safety on5636 <pre><code class="shell"> -Drelease-safe=[bool] optimizations on and safety on
5131 -Drelease-fast=(bool) optimizations on and safety off</code></pre>5637 -Drelease-fast=[bool] optimizations on and safety off
5638 -Drelease-small=[bool] size optimizations on and safety off</code></pre>
5132 {#header_open|Debug#}5639 {#header_open|Debug#}
5133 <pre><code class="shell">$ zig build-exe example.zig</code></pre>5640 <pre><code class="shell">$ zig build-exe example.zig</code></pre>
5134 <ul>5641 <ul>
5135 <li>Fast compilation speed</li>5642 <li>Fast compilation speed</li>
5136 <li>Safety checks enabled</li>5643 <li>Safety checks enabled</li>
5137 <li>Slow runtime performance</li>5644 <li>Slow runtime performance</li>
5645 <li>Large binary size</li>
5138 </ul>5646 </ul>
5139 {#header_close#}5647 {#header_close#}
5140 {#header_open|ReleaseFast#}5648 {#header_open|ReleaseFast#}
...@@ -5143,6 +5651,7 @@ pub fn build(b: &Builder) void {...@@ -5143,6 +5651,7 @@ pub fn build(b: &Builder) void {
5143 <li>Fast runtime performance</li>5651 <li>Fast runtime performance</li>
5144 <li>Safety checks disabled</li>5652 <li>Safety checks disabled</li>
5145 <li>Slow compilation speed</li>5653 <li>Slow compilation speed</li>
5654 <li>Large binary size</li>
5146 </ul>5655 </ul>
5147 {#header_close#}5656 {#header_close#}
5148 {#header_open|ReleaseSafe#}5657 {#header_open|ReleaseSafe#}
...@@ -5151,9 +5660,19 @@ pub fn build(b: &Builder) void {...@@ -5151,9 +5660,19 @@ pub fn build(b: &Builder) void {
5151 <li>Medium runtime performance</li>5660 <li>Medium runtime performance</li>
5152 <li>Safety checks enabled</li>5661 <li>Safety checks enabled</li>
5153 <li>Slow compilation speed</li>5662 <li>Slow compilation speed</li>
5663 <li>Large binary size</li>
5664 </ul>
5665 {#header_close#}
5666 {#header_open|ReleaseSmall#}
5667 <pre><code class="shell">$ zig build-exe example.zig --release-small</code></pre>
5668 <ul>
5669 <li>Medium runtime performance</li>
5670 <li>Safety checks disabled</li>
5671 <li>Slow compilation speed</li>
5672 <li>Small binary size</li>
5154 </ul>5673 </ul>
5155 {#see_also|Compile Variables|Zig Build System|Undefined Behavior#}
5156 {#header_close#}5674 {#header_close#}
5675 {#see_also|Compile Variables|Zig Build System|Undefined Behavior#}
5157 {#header_close#}5676 {#header_close#}
5158 {#header_open|Undefined Behavior#}5677 {#header_open|Undefined Behavior#}
5159 <p>5678 <p>
...@@ -5161,7 +5680,7 @@ pub fn build(b: &Builder) void {...@@ -5161,7 +5680,7 @@ pub fn build(b: &Builder) void {
5161 detected at compile-time, Zig emits an error. Most undefined behavior that5680 detected at compile-time, Zig emits an error. Most undefined behavior that
5162 cannot be detected at compile-time can be detected at runtime. In these cases,5681 cannot be detected at compile-time can be detected at runtime. In these cases,
5163 Zig has safety checks. Safety checks can be disabled on a per-block basis5682 Zig has safety checks. Safety checks can be disabled on a per-block basis
5164 with <code>@setRuntimeSafety</code>. The {#link|ReleaseFast#}5683 with {#link|setRuntimeSafety#}. The {#link|ReleaseFast#}
5165 build mode disables all safety checks in order to facilitate optimizations.5684 build mode disables all safety checks in order to facilitate optimizations.
5166 </p>5685 </p>
5167 <p>5686 <p>
...@@ -5375,8 +5894,8 @@ comptime {...@@ -5375,8 +5894,8 @@ comptime {
5375 <p>At compile-time:</p>5894 <p>At compile-time:</p>
5376 {#code_begin|test_err|unable to unwrap null#}5895 {#code_begin|test_err|unable to unwrap null#}
5377comptime {5896comptime {
5378 const nullable_number: ?i32 = null;5897 const optional_number: ?i32 = null;
5379 const number = ??nullable_number;5898 const number = optional_number.?;
5380}5899}
5381 {#code_end#}5900 {#code_end#}
5382 <p>At runtime crashes with the message <code>attempt to unwrap null</code> and a stack trace.</p>5901 <p>At runtime crashes with the message <code>attempt to unwrap null</code> and a stack trace.</p>
...@@ -5385,9 +5904,9 @@ comptime {...@@ -5385,9 +5904,9 @@ comptime {
5385 {#code_begin|exe|test#}5904 {#code_begin|exe|test#}
5386const warn = @import("std").debug.warn;5905const warn = @import("std").debug.warn;
5387pub fn main() void {5906pub fn main() void {
5388 const nullable_number: ?i32 = null;5907 const optional_number: ?i32 = null;
53895908
5390 if (nullable_number) |number| {5909 if (optional_number) |number| {
5391 warn("got number: {}\n", number);5910 warn("got number: {}\n", number);
5392 } else {5911 } else {
5393 warn("it's null\n");5912 warn("it's null\n");
...@@ -5474,425 +5993,7 @@ const separator = if (builtin.os == builtin.Os.windows) '\\' else '/';...@@ -5474,425 +5993,7 @@ const separator = if (builtin.os == builtin.Os.windows) '\\' else '/';
5474 <p>5993 <p>
5475 Example of what is imported with <code>@import("builtin")</code>:5994 Example of what is imported with <code>@import("builtin")</code>:
5476 </p>5995 </p>
5477 {#code_begin|syntax#}5996 {#builtin#}
5478pub const StackTrace = struct {
5479 index: usize,
5480 instruction_addresses: []usize,
5481};
5482
5483pub const Os = enum {
5484 freestanding,
5485 ananas,
5486 cloudabi,
5487 dragonfly,
5488 freebsd,
5489 fuchsia,
5490 ios,
5491 kfreebsd,
5492 linux,
5493 lv2,
5494 macosx,
5495 netbsd,
5496 openbsd,
5497 solaris,
5498 windows,
5499 haiku,
5500 minix,
5501 rtems,
5502 nacl,
5503 cnk,
5504 aix,
5505 cuda,
5506 nvcl,
5507 amdhsa,
5508 ps4,
5509 elfiamcu,
5510 tvos,
5511 watchos,
5512 mesa3d,
5513 contiki,
5514 amdpal,
5515 zen,
5516};
5517
5518pub const Arch = enum {
5519 armv8_3a,
5520 armv8_2a,
5521 armv8_1a,
5522 armv8,
5523 armv8r,
5524 armv8m_baseline,
5525 armv8m_mainline,
5526 armv7,
5527 armv7em,
5528 armv7m,
5529 armv7s,
5530 armv7k,
5531 armv7ve,
5532 armv6,
5533 armv6m,
5534 armv6k,
5535 armv6t2,
5536 armv5,
5537 armv5te,
5538 armv4t,
5539 armebv8_3a,
5540 armebv8_2a,
5541 armebv8_1a,
5542 armebv8,
5543 armebv8r,
5544 armebv8m_baseline,
5545 armebv8m_mainline,
5546 armebv7,
5547 armebv7em,
5548 armebv7m,
5549 armebv7s,
5550 armebv7k,
5551 armebv7ve,
5552 armebv6,
5553 armebv6m,
5554 armebv6k,
5555 armebv6t2,
5556 armebv5,
5557 armebv5te,
5558 armebv4t,
5559 aarch64,
5560 aarch64_be,
5561 arc,
5562 avr,
5563 bpfel,
5564 bpfeb,
5565 hexagon,
5566 mips,
5567 mipsel,
5568 mips64,
5569 mips64el,
5570 msp430,
5571 nios2,
5572 powerpc,
5573 powerpc64,
5574 powerpc64le,
5575 r600,
5576 amdgcn,
5577 riscv32,
5578 riscv64,
5579 sparc,
5580 sparcv9,
5581 sparcel,
5582 s390x,
5583 tce,
5584 tcele,
5585 thumb,
5586 thumbeb,
5587 i386,
5588 x86_64,
5589 xcore,
5590 nvptx,
5591 nvptx64,
5592 le32,
5593 le64,
5594 amdil,
5595 amdil64,
5596 hsail,
5597 hsail64,
5598 spir,
5599 spir64,
5600 kalimbav3,
5601 kalimbav4,
5602 kalimbav5,
5603 shave,
5604 lanai,
5605 wasm32,
5606 wasm64,
5607 renderscript32,
5608 renderscript64,
5609};
5610
5611pub const Environ = enum {
5612 unknown,
5613 gnu,
5614 gnuabin32,
5615 gnuabi64,
5616 gnueabi,
5617 gnueabihf,
5618 gnux32,
5619 code16,
5620 eabi,
5621 eabihf,
5622 android,
5623 musl,
5624 musleabi,
5625 musleabihf,
5626 msvc,
5627 itanium,
5628 cygnus,
5629 amdopencl,
5630 coreclr,
5631 opencl,
5632 simulator,
5633};
5634
5635pub const ObjectFormat = enum {
5636 unknown,
5637 coff,
5638 elf,
5639 macho,
5640 wasm,
5641};
5642
5643pub const GlobalLinkage = enum {
5644 Internal,
5645 Strong,
5646 Weak,
5647 LinkOnce,
5648};
5649
5650pub const AtomicOrder = enum {
5651 Unordered,
5652 Monotonic,
5653 Acquire,
5654 Release,
5655 AcqRel,
5656 SeqCst,
5657};
5658
5659pub const AtomicRmwOp = enum {
5660 Xchg,
5661 Add,
5662 Sub,
5663 And,
5664 Nand,
5665 Or,
5666 Xor,
5667 Max,
5668 Min,
5669};
5670
5671pub const Mode = enum {
5672 Debug,
5673 ReleaseSafe,
5674 ReleaseFast,
5675 ReleaseSmall,
5676};
5677
5678pub const TypeId = enum {
5679 Type,
5680 Void,
5681 Bool,
5682 NoReturn,
5683 Int,
5684 Float,
5685 Pointer,
5686 Array,
5687 Struct,
5688 ComptimeFloat,
5689 ComptimeInt,
5690 Undefined,
5691 Null,
5692 Nullable,
5693 ErrorUnion,
5694 ErrorSet,
5695 Enum,
5696 Union,
5697 Fn,
5698 Namespace,
5699 Block,
5700 BoundFn,
5701 ArgTuple,
5702 Opaque,
5703 Promise,
5704};
5705
5706pub const TypeInfo = union(TypeId) {
5707 Type: void,
5708 Void: void,
5709 Bool: void,
5710 NoReturn: void,
5711 Int: Int,
5712 Float: Float,
5713 Pointer: Pointer,
5714 Array: Array,
5715 Struct: Struct,
5716 ComptimeFloat: void,
5717 ComptimeInt: void,
5718 Undefined: void,
5719 Null: void,
5720 Nullable: Nullable,
5721 ErrorUnion: ErrorUnion,
5722 ErrorSet: ErrorSet,
5723 Enum: Enum,
5724 Union: Union,
5725 Fn: Fn,
5726 Namespace: void,
5727 Block: void,
5728 BoundFn: Fn,
5729 ArgTuple: void,
5730 Opaque: void,
5731 Promise: Promise,
5732
5733
5734 pub const Int = struct {
5735 is_signed: bool,
5736 bits: u8,
5737 };
5738
5739 pub const Float = struct {
5740 bits: u8,
5741 };
5742
5743 pub const Pointer = struct {
5744 is_const: bool,
5745 is_volatile: bool,
5746 alignment: u32,
5747 child: type,
5748 };
5749
5750 pub const Array = struct {
5751 len: usize,
5752 child: type,
5753 };
5754
5755 pub const ContainerLayout = enum {
5756 Auto,
5757 Extern,
5758 Packed,
5759 };
5760
5761 pub const StructField = struct {
5762 name: []const u8,
5763 offset: ?usize,
5764 field_type: type,
5765 };
5766
5767 pub const Struct = struct {
5768 layout: ContainerLayout,
5769 fields: []StructField,
5770 defs: []Definition,
5771 };
5772
5773 pub const Nullable = struct {
5774 child: type,
5775 };
5776
5777 pub const ErrorUnion = struct {
5778 error_set: type,
5779 payload: type,
5780 };
5781
5782 pub const Error = struct {
5783 name: []const u8,
5784 value: usize,
5785 };
5786
5787 pub const ErrorSet = struct {
5788 errors: []Error,
5789 };
5790
5791 pub const EnumField = struct {
5792 name: []const u8,
5793 value: usize,
5794 };
5795
5796 pub const Enum = struct {
5797 layout: ContainerLayout,
5798 tag_type: type,
5799 fields: []EnumField,
5800 defs: []Definition,
5801 };
5802
5803 pub const UnionField = struct {
5804 name: []const u8,
5805 enum_field: ?EnumField,
5806 field_type: type,
5807 };
5808
5809 pub const Union = struct {
5810 layout: ContainerLayout,
5811 tag_type: type,
5812 fields: []UnionField,
5813 defs: []Definition,
5814 };
5815
5816 pub const CallingConvention = enum {
5817 Unspecified,
5818 C,
5819 Cold,
5820 Naked,
5821 Stdcall,
5822 Async,
5823 };
5824
5825 pub const FnArg = struct {
5826 is_generic: bool,
5827 is_noalias: bool,
5828 arg_type: type,
5829 };
5830
5831 pub const Fn = struct {
5832 calling_convention: CallingConvention,
5833 is_generic: bool,
5834 is_var_args: bool,
5835 return_type: type,
5836 async_allocator_type: type,
5837 args: []FnArg,
5838 };
5839
5840 pub const Promise = struct {
5841 child: type,
5842 };
5843
5844 pub const Definition = struct {
5845 name: []const u8,
5846 is_pub: bool,
5847 data: Data,
5848
5849 pub const Data = union(enum) {
5850 Type: type,
5851 Var: type,
5852 Fn: FnDef,
5853
5854 pub const FnDef = struct {
5855 fn_type: type,
5856 inline_type: Inline,
5857 calling_convention: CallingConvention,
5858 is_var_args: bool,
5859 is_extern: bool,
5860 is_export: bool,
5861 lib_name: ?[]const u8,
5862 return_type: type,
5863 arg_names: [][] const u8,
5864
5865 pub const Inline = enum {
5866 Auto,
5867 Always,
5868 Never,
5869 };
5870 };
5871 };
5872 };
5873};
5874
5875pub const FloatMode = enum {
5876 Optimized,
5877 Strict,
5878};
5879
5880pub const Endian = enum {
5881 Big,
5882 Little,
5883};
5884
5885pub const endian = Endian.Little;
5886pub const is_test = true;
5887pub const os = Os.linux;
5888pub const arch = Arch.x86_64;
5889pub const environ = Environ.gnu;
5890pub const object_format = ObjectFormat.elf;
5891pub const mode = Mode.Debug;
5892pub const link_libc = false;
5893pub const have_error_return_tracing = true;
5894pub const __zig_test_fn_slice = {}; // overwritten later
5895 {#code_end#}
5896 {#see_also|Build Mode#}5997 {#see_also|Build Mode#}
5897 {#header_close#}5998 {#header_close#}
5898 {#header_open|Root Source File#}5999 {#header_open|Root Source File#}
...@@ -6053,8 +6154,7 @@ pub fn build(b: *Builder) void {...@@ -6053,8 +6154,7 @@ pub fn build(b: *Builder) void {
6053 b.default_step.dependOn(&exe.step);6154 b.default_step.dependOn(&exe.step);
6054}6155}
6055 {#code_end#}6156 {#code_end#}
6056 {#header_close#}6157 <p class="file">terminal</p>
6057 {#header_open|Terminal#}
6058 <pre><code class="shell">$ zig build6158 <pre><code class="shell">$ zig build
6059$ ./test6159$ ./test
6060all your base are belong to us</code></pre>6160all your base are belong to us</code></pre>
...@@ -6367,9 +6467,9 @@ AsmInputItem = "[" Symbol "]" String "(" Expression ")"...@@ -6367,9 +6467,9 @@ AsmInputItem = "[" Symbol "]" String "(" Expression ")"
63676467
6368AsmClobbers= ":" list(String, ",")6468AsmClobbers= ":" list(String, ",")
63696469
6370UnwrapExpression = BoolOrExpression (UnwrapNullable | UnwrapError) | BoolOrExpression6470UnwrapExpression = BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression
63716471
6372UnwrapNullable = "??" Expression6472UnwrapOptional = "orelse" Expression
63736473
6374UnwrapError = "catch" option("|" Symbol "|") Expression6474UnwrapError = "catch" option("|" Symbol "|") Expression
63756475
...@@ -6443,12 +6543,10 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"...@@ -6443,12 +6543,10 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
64436543
6444PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression6544PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression
64456545
6446SuffixOpExpression = ("async" option("&lt;" SuffixOpExpression "&gt;") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | PtrDerefExpression)6546SuffixOpExpression = ("async" option("&lt;" SuffixOpExpression "&gt;") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ".*" | ".?")
64476547
6448FieldAccessExpression = "." Symbol6548FieldAccessExpression = "." Symbol
64496549
6450PtrDerefExpression = ".*"
6451
6452FnCallExpression = "(" list(Expression, ",") ")"6550FnCallExpression = "(" list(Expression, ",") ")"
64536551
6454ArrayAccessExpression = "[" Expression "]"6552ArrayAccessExpression = "[" Expression "]"
...@@ -6461,7 +6559,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",")...@@ -6461,7 +6559,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",")
64616559
6462StructLiteralField = "." Symbol "=" Expression6560StructLiteralField = "." Symbol "=" Expression
64636561
6464PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await"6562PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "-%" | "try" | "await"
64656563
6466PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType6564PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType
64676565
...@@ -6554,8 +6652,8 @@ hljs.registerLanguage("zig", function(t) {...@@ -6554,8 +6652,8 @@ hljs.registerLanguage("zig", function(t) {
6554 },6652 },
6555 a = t.IR + "\\s*\\(",6653 a = t.IR + "\\s*\\(",
6556 c = {6654 c = {
6557 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",6655 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",
6558 built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall",6656 built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall",
6559 literal: "true false null undefined"6657 literal: "true false null undefined"
6560 },6658 },
6561 n = [e, t.CLCM, t.CBCM, s, r];6659 n = [e, t.CLCM, t.CBCM, s, r];
example/cat/main.zig+1-1
...@@ -7,7 +7,7 @@ const allocator = std.debug.global_allocator;...@@ -7,7 +7,7 @@ const allocator = std.debug.global_allocator;
77
8pub fn main() !void {8pub fn main() !void {
9 var args_it = os.args();9 var args_it = os.args();
10 const exe = try unwrapArg(??args_it.next(allocator));10 const exe = try unwrapArg(args_it.next(allocator).?);
11 var catted_anything = false;11 var catted_anything = false;
12 var stdout_file = try io.getStdOut();12 var stdout_file = try io.getStdOut();
1313
src-self-hosted/arg.zig+5-5
...@@ -99,7 +99,7 @@ pub const Args = struct {...@@ -99,7 +99,7 @@ pub const Args = struct {
99 error.ArgumentNotInAllowedSet => {99 error.ArgumentNotInAllowedSet => {
100 std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg);100 std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg);
101 std.debug.warn("allowed options are ");101 std.debug.warn("allowed options are ");
102 for (??flag.allowed_set) |possible| {102 for (flag.allowed_set.?) |possible| {
103 std.debug.warn("'{}' ", possible);103 std.debug.warn("'{}' ", possible);
104 }104 }
105 std.debug.warn("\n");105 std.debug.warn("\n");
...@@ -276,14 +276,14 @@ test "parse arguments" {...@@ -276,14 +276,14 @@ test "parse arguments" {
276 debug.assert(!args.present("help2"));276 debug.assert(!args.present("help2"));
277 debug.assert(!args.present("init"));277 debug.assert(!args.present("init"));
278278
279 debug.assert(mem.eql(u8, ??args.single("build-file"), "build.zig"));279 debug.assert(mem.eql(u8, args.single("build-file").?, "build.zig"));
280 debug.assert(mem.eql(u8, ??args.single("color"), "on"));280 debug.assert(mem.eql(u8, args.single("color").?, "on"));
281281
282 const objects = ??args.many("object");282 const objects = args.many("object").?;
283 debug.assert(mem.eql(u8, objects[0], "obj1"));283 debug.assert(mem.eql(u8, objects[0], "obj1"));
284 debug.assert(mem.eql(u8, objects[1], "obj2"));284 debug.assert(mem.eql(u8, objects[1], "obj2"));
285285
286 debug.assert(mem.eql(u8, ??args.single("library"), "lib2"));286 debug.assert(mem.eql(u8, args.single("library").?, "lib2"));
287287
288 const pos = args.positionals.toSliceConst();288 const pos = args.positionals.toSliceConst();
289 debug.assert(mem.eql(u8, pos[0], "build"));289 debug.assert(mem.eql(u8, pos[0], "build"));
src-self-hosted/introspect.zig+1-1
...@@ -27,7 +27,7 @@ pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {...@@ -27,7 +27,7 @@ pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {
2727
28 var cur_path: []const u8 = self_exe_path;28 var cur_path: []const u8 = self_exe_path;
29 while (true) {29 while (true) {
30 const test_dir = os.path.dirname(cur_path);30 const test_dir = os.path.dirname(cur_path) orelse ".";
3131
32 if (mem.eql(u8, test_dir, cur_path)) {32 if (mem.eql(u8, test_dir, cur_path)) {
33 break;33 break;
src-self-hosted/llvm.zig+1-1
...@@ -8,6 +8,6 @@ pub const ContextRef = removeNullability(c.LLVMContextRef);...@@ -8,6 +8,6 @@ pub const ContextRef = removeNullability(c.LLVMContextRef);
8pub const BuilderRef = removeNullability(c.LLVMBuilderRef);8pub const BuilderRef = removeNullability(c.LLVMBuilderRef);
99
10fn removeNullability(comptime T: type) type {10fn removeNullability(comptime T: type) type {
11 comptime assert(@typeId(T) == builtin.TypeId.Nullable);11 comptime assert(@typeId(T) == builtin.TypeId.Optional);
12 return T.Child;12 return T.Child;
13}13}
src-self-hosted/main.zig+13-13
...@@ -212,7 +212,7 @@ fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void {...@@ -212,7 +212,7 @@ fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void {
212 const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");212 const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
213 defer allocator.free(build_runner_path);213 defer allocator.free(build_runner_path);
214214
215 const build_file = flags.single("build-file") ?? "build.zig";215 const build_file = flags.single("build-file") orelse "build.zig";
216 const build_file_abs = try os.path.resolve(allocator, ".", build_file);216 const build_file_abs = try os.path.resolve(allocator, ".", build_file);
217 defer allocator.free(build_file_abs);217 defer allocator.free(build_file_abs);
218218
...@@ -249,7 +249,7 @@ fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void {...@@ -249,7 +249,7 @@ fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void {
249 defer build_args.deinit();249 defer build_args.deinit();
250250
251 const build_file_basename = os.path.basename(build_file_abs);251 const build_file_basename = os.path.basename(build_file_abs);
252 const build_file_dirname = os.path.dirname(build_file_abs);252 const build_file_dirname = os.path.dirname(build_file_abs) orelse ".";
253253
254 var full_cache_dir: []u8 = undefined;254 var full_cache_dir: []u8 = undefined;
255 if (flags.single("cache-dir")) |cache_dir| {255 if (flags.single("cache-dir")) |cache_dir| {
...@@ -490,7 +490,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo...@@ -490,7 +490,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
490 try stderr.print("encountered --pkg-end with no matching --pkg-begin\n");490 try stderr.print("encountered --pkg-end with no matching --pkg-begin\n");
491 os.exit(1);491 os.exit(1);
492 }492 }
493 cur_pkg = ??cur_pkg.parent;493 cur_pkg = cur_pkg.parent.?;
494 }494 }
495 }495 }
496496
...@@ -514,28 +514,28 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo...@@ -514,28 +514,28 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
514 },514 },
515 }515 }
516516
517 const basename = os.path.basename(??in_file);517 const basename = os.path.basename(in_file.?);
518 var it = mem.split(basename, ".");518 var it = mem.split(basename, ".");
519 const root_name = it.next() ?? {519 const root_name = it.next() orelse {
520 try stderr.write("file name cannot be empty\n");520 try stderr.write("file name cannot be empty\n");
521 os.exit(1);521 os.exit(1);
522 };522 };
523523
524 const asm_a = flags.many("assembly");524 const asm_a = flags.many("assembly");
525 const obj_a = flags.many("object");525 const obj_a = flags.many("object");
526 if (in_file == null and (obj_a == null or (??obj_a).len == 0) and (asm_a == null or (??asm_a).len == 0)) {526 if (in_file == null and (obj_a == null or obj_a.?.len == 0) and (asm_a == null or asm_a.?.len == 0)) {
527 try stderr.write("Expected source file argument or at least one --object or --assembly argument\n");527 try stderr.write("Expected source file argument or at least one --object or --assembly argument\n");
528 os.exit(1);528 os.exit(1);
529 }529 }
530530
531 if (out_type == Module.Kind.Obj and (obj_a != null and (??obj_a).len != 0)) {531 if (out_type == Module.Kind.Obj and (obj_a != null and obj_a.?.len != 0)) {
532 try stderr.write("When building an object file, --object arguments are invalid\n");532 try stderr.write("When building an object file, --object arguments are invalid\n");
533 os.exit(1);533 os.exit(1);
534 }534 }
535535
536 const zig_root_source_file = in_file;536 const zig_root_source_file = in_file;
537537
538 const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") ?? "zig-cache"[0..]) catch {538 const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") orelse "zig-cache"[0..]) catch {
539 os.exit(1);539 os.exit(1);
540 };540 };
541 defer allocator.free(full_cache_dir);541 defer allocator.free(full_cache_dir);
...@@ -555,9 +555,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo...@@ -555,9 +555,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
555 );555 );
556 defer module.destroy();556 defer module.destroy();
557557
558 module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") ?? "0", 10);558 module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
559 module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") ?? "0", 10);559 module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
560 module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") ?? "0", 10);560 module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
561561
562 module.is_test = false;562 module.is_test = false;
563563
...@@ -652,7 +652,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo...@@ -652,7 +652,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
652 }652 }
653653
654 try module.build();654 try module.build();
655 try module.link(flags.single("out-file") ?? null);655 try module.link(flags.single("out-file") orelse null);
656656
657 if (flags.present("print-timing-info")) {657 if (flags.present("print-timing-info")) {
658 // codegen_print_timing_info(g, stderr);658 // codegen_print_timing_info(g, stderr);
...@@ -734,7 +734,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {...@@ -734,7 +734,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
734 defer file.close();734 defer file.close();
735735
736 const source_code = io.readFileAlloc(allocator, file_path) catch |err| {736 const source_code = io.readFileAlloc(allocator, file_path) catch |err| {
737 try stderr.print("unable to open '{}': {}", file_path, err);737 try stderr.print("unable to open '{}': {}\n", file_path, err);
738 fmt_errors = true;738 fmt_errors = true;
739 continue;739 continue;
740 };740 };
src-self-hosted/module.zig+4-4
...@@ -130,13 +130,13 @@ pub const Module = struct {...@@ -130,13 +130,13 @@ pub const Module = struct {
130 var name_buffer = try Buffer.init(allocator, name);130 var name_buffer = try Buffer.init(allocator, name);
131 errdefer name_buffer.deinit();131 errdefer name_buffer.deinit();
132132
133 const context = c.LLVMContextCreate() ?? return error.OutOfMemory;133 const context = c.LLVMContextCreate() orelse return error.OutOfMemory;
134 errdefer c.LLVMContextDispose(context);134 errdefer c.LLVMContextDispose(context);
135135
136 const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) ?? return error.OutOfMemory;136 const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory;
137 errdefer c.LLVMDisposeModule(module);137 errdefer c.LLVMDisposeModule(module);
138138
139 const builder = c.LLVMCreateBuilderInContext(context) ?? return error.OutOfMemory;139 const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory;
140 errdefer c.LLVMDisposeBuilder(builder);140 errdefer c.LLVMDisposeBuilder(builder);
141141
142 const module_ptr = try allocator.create(Module);142 const module_ptr = try allocator.create(Module);
...@@ -223,7 +223,7 @@ pub const Module = struct {...@@ -223,7 +223,7 @@ pub const Module = struct {
223 c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);223 c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
224 }224 }
225225
226 const root_src_path = self.root_src_path ?? @panic("TODO handle null root src path");226 const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
227 const root_src_real_path = os.path.real(self.allocator, root_src_path) catch |err| {227 const root_src_real_path = os.path.real(self.allocator, root_src_path) catch |err| {
228 try printError("unable to get real path '{}': {}", root_src_path, err);228 try printError("unable to get real path '{}': {}", root_src_path, err);
229 return err;229 return err;
src/all_types.hpp+39-33
...@@ -144,6 +144,9 @@ enum ConstPtrSpecial {...@@ -144,6 +144,9 @@ enum ConstPtrSpecial {
144 // understand the value of pointee at compile time. However, we will still144 // understand the value of pointee at compile time. However, we will still
145 // emit a binary with a compile time known address.145 // emit a binary with a compile time known address.
146 // In this case index is the numeric address value.146 // In this case index is the numeric address value.
147 // We also use this for null pointer. We need the data layout for ConstCastOnly == true
148 // types to be the same, so all optionals of pointer types use x_ptr
149 // instead of x_optional
147 ConstPtrSpecialHardCodedAddr,150 ConstPtrSpecialHardCodedAddr,
148 // This means that the pointer represents memory of assigning to _.151 // This means that the pointer represents memory of assigning to _.
149 // That is, storing discards the data, and loading is invalid.152 // That is, storing discards the data, and loading is invalid.
...@@ -219,10 +222,10 @@ enum RuntimeHintErrorUnion {...@@ -219,10 +222,10 @@ enum RuntimeHintErrorUnion {
219 RuntimeHintErrorUnionNonError,222 RuntimeHintErrorUnionNonError,
220};223};
221224
222enum RuntimeHintMaybe {225enum RuntimeHintOptional {
223 RuntimeHintMaybeUnknown,226 RuntimeHintOptionalUnknown,
224 RuntimeHintMaybeNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known.227 RuntimeHintOptionalNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known.
225 RuntimeHintMaybeNonNull,228 RuntimeHintOptionalNonNull,
226};229};
227230
228enum RuntimeHintPtr {231enum RuntimeHintPtr {
...@@ -251,7 +254,7 @@ struct ConstExprValue {...@@ -251,7 +254,7 @@ struct ConstExprValue {
251 bool x_bool;254 bool x_bool;
252 ConstBoundFnValue x_bound_fn;255 ConstBoundFnValue x_bound_fn;
253 TypeTableEntry *x_type;256 TypeTableEntry *x_type;
254 ConstExprValue *x_maybe;257 ConstExprValue *x_optional;
255 ConstErrValue x_err_union;258 ConstErrValue x_err_union;
256 ErrorTableEntry *x_err_set;259 ErrorTableEntry *x_err_set;
257 BigInt x_enum_tag;260 BigInt x_enum_tag;
...@@ -265,7 +268,7 @@ struct ConstExprValue {...@@ -265,7 +268,7 @@ struct ConstExprValue {
265268
266 // populated if special == ConstValSpecialRuntime269 // populated if special == ConstValSpecialRuntime
267 RuntimeHintErrorUnion rh_error_union;270 RuntimeHintErrorUnion rh_error_union;
268 RuntimeHintMaybe rh_maybe;271 RuntimeHintOptional rh_maybe;
269 RuntimeHintPtr rh_ptr;272 RuntimeHintPtr rh_ptr;
270 } data;273 } data;
271};274};
...@@ -384,6 +387,7 @@ enum NodeType {...@@ -384,6 +387,7 @@ enum NodeType {
384 NodeTypeSliceExpr,387 NodeTypeSliceExpr,
385 NodeTypeFieldAccessExpr,388 NodeTypeFieldAccessExpr,
386 NodeTypePtrDeref,389 NodeTypePtrDeref,
390 NodeTypeUnwrapOptional,
387 NodeTypeUse,391 NodeTypeUse,
388 NodeTypeBoolLiteral,392 NodeTypeBoolLiteral,
389 NodeTypeNullLiteral,393 NodeTypeNullLiteral,
...@@ -553,7 +557,7 @@ enum BinOpType {...@@ -553,7 +557,7 @@ enum BinOpType {
553 BinOpTypeMultWrap,557 BinOpTypeMultWrap,
554 BinOpTypeDiv,558 BinOpTypeDiv,
555 BinOpTypeMod,559 BinOpTypeMod,
556 BinOpTypeUnwrapMaybe,560 BinOpTypeUnwrapOptional,
557 BinOpTypeArrayCat,561 BinOpTypeArrayCat,
558 BinOpTypeArrayMult,562 BinOpTypeArrayMult,
559 BinOpTypeErrorUnion,563 BinOpTypeErrorUnion,
...@@ -572,6 +576,10 @@ struct AstNodeCatchExpr {...@@ -572,6 +576,10 @@ struct AstNodeCatchExpr {
572 AstNode *op2;576 AstNode *op2;
573};577};
574578
579struct AstNodeUnwrapOptional {
580 AstNode *expr;
581};
582
575enum CastOp {583enum CastOp {
576 CastOpNoCast, // signifies the function call expression is not a cast584 CastOpNoCast, // signifies the function call expression is not a cast
577 CastOpNoop, // fn call expr is a cast, but does nothing585 CastOpNoop, // fn call expr is a cast, but does nothing
...@@ -583,6 +591,7 @@ enum CastOp {...@@ -583,6 +591,7 @@ enum CastOp {
583 CastOpNumLitToConcrete,591 CastOpNumLitToConcrete,
584 CastOpErrSet,592 CastOpErrSet,
585 CastOpBitCast,593 CastOpBitCast,
594 CastOpPtrOfArrayToSlice,
586};595};
587596
588struct AstNodeFnCallExpr {597struct AstNodeFnCallExpr {
...@@ -619,8 +628,7 @@ enum PrefixOp {...@@ -619,8 +628,7 @@ enum PrefixOp {
619 PrefixOpBinNot,628 PrefixOpBinNot,
620 PrefixOpNegation,629 PrefixOpNegation,
621 PrefixOpNegationWrap,630 PrefixOpNegationWrap,
622 PrefixOpMaybe,631 PrefixOpOptional,
623 PrefixOpUnwrapMaybe,
624 PrefixOpAddrOf,632 PrefixOpAddrOf,
625};633};
626634
...@@ -905,6 +913,7 @@ struct AstNode {...@@ -905,6 +913,7 @@ struct AstNode {
905 AstNodeTestDecl test_decl;913 AstNodeTestDecl test_decl;
906 AstNodeBinOpExpr bin_op_expr;914 AstNodeBinOpExpr bin_op_expr;
907 AstNodeCatchExpr unwrap_err_expr;915 AstNodeCatchExpr unwrap_err_expr;
916 AstNodeUnwrapOptional unwrap_optional;
908 AstNodePrefixOpExpr prefix_op_expr;917 AstNodePrefixOpExpr prefix_op_expr;
909 AstNodePointerType pointer_type;918 AstNodePointerType pointer_type;
910 AstNodeFnCallExpr fn_call_expr;919 AstNodeFnCallExpr fn_call_expr;
...@@ -1037,6 +1046,10 @@ struct TypeTableEntryStruct {...@@ -1037,6 +1046,10 @@ struct TypeTableEntryStruct {
1037 // whether we've finished resolving it1046 // whether we've finished resolving it
1038 bool complete;1047 bool complete;
10391048
1049 // whether any of the fields require comptime
1050 // the value is not valid until zero_bits_known == true
1051 bool requires_comptime;
1052
1040 bool zero_bits_loop_flag;1053 bool zero_bits_loop_flag;
1041 bool zero_bits_known;1054 bool zero_bits_known;
1042 uint32_t abi_alignment; // also figured out with zero_bits pass1055 uint32_t abi_alignment; // also figured out with zero_bits pass
...@@ -1044,7 +1057,7 @@ struct TypeTableEntryStruct {...@@ -1044,7 +1057,7 @@ struct TypeTableEntryStruct {
1044 HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name;1057 HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name;
1045};1058};
10461059
1047struct TypeTableEntryMaybe {1060struct TypeTableEntryOptional {
1048 TypeTableEntry *child_type;1061 TypeTableEntry *child_type;
1049};1062};
10501063
...@@ -1078,8 +1091,7 @@ struct TypeTableEntryEnum {...@@ -1078,8 +1091,7 @@ struct TypeTableEntryEnum {
1078 bool zero_bits_loop_flag;1091 bool zero_bits_loop_flag;
1079 bool zero_bits_known;1092 bool zero_bits_known;
10801093
1081 bool generate_name_table;1094 LLVMValueRef name_function;
1082 LLVMValueRef name_table;
10831095
1084 HashMap<Buf *, TypeEnumField *, buf_hash, buf_eql_buf> fields_by_name;1096 HashMap<Buf *, TypeEnumField *, buf_hash, buf_eql_buf> fields_by_name;
1085};1097};
...@@ -1105,6 +1117,10 @@ struct TypeTableEntryUnion {...@@ -1105,6 +1117,10 @@ struct TypeTableEntryUnion {
1105 // whether we've finished resolving it1117 // whether we've finished resolving it
1106 bool complete;1118 bool complete;
11071119
1120 // whether any of the fields require comptime
1121 // the value is not valid until zero_bits_known == true
1122 bool requires_comptime;
1123
1108 bool zero_bits_loop_flag;1124 bool zero_bits_loop_flag;
1109 bool zero_bits_known;1125 bool zero_bits_known;
1110 uint32_t abi_alignment; // also figured out with zero_bits pass1126 uint32_t abi_alignment; // also figured out with zero_bits pass
...@@ -1163,7 +1179,7 @@ enum TypeTableEntryId {...@@ -1163,7 +1179,7 @@ enum TypeTableEntryId {
1163 TypeTableEntryIdComptimeInt,1179 TypeTableEntryIdComptimeInt,
1164 TypeTableEntryIdUndefined,1180 TypeTableEntryIdUndefined,
1165 TypeTableEntryIdNull,1181 TypeTableEntryIdNull,
1166 TypeTableEntryIdMaybe,1182 TypeTableEntryIdOptional,
1167 TypeTableEntryIdErrorUnion,1183 TypeTableEntryIdErrorUnion,
1168 TypeTableEntryIdErrorSet,1184 TypeTableEntryIdErrorSet,
1169 TypeTableEntryIdEnum,1185 TypeTableEntryIdEnum,
...@@ -1194,7 +1210,7 @@ struct TypeTableEntry {...@@ -1194,7 +1210,7 @@ struct TypeTableEntry {
1194 TypeTableEntryFloat floating;1210 TypeTableEntryFloat floating;
1195 TypeTableEntryArray array;1211 TypeTableEntryArray array;
1196 TypeTableEntryStruct structure;1212 TypeTableEntryStruct structure;
1197 TypeTableEntryMaybe maybe;1213 TypeTableEntryOptional maybe;
1198 TypeTableEntryErrorUnion error_union;1214 TypeTableEntryErrorUnion error_union;
1199 TypeTableEntryErrorSet error_set;1215 TypeTableEntryErrorSet error_set;
1200 TypeTableEntryEnum enumeration;1216 TypeTableEntryEnum enumeration;
...@@ -1346,7 +1362,6 @@ enum BuiltinFnId {...@@ -1346,7 +1362,6 @@ enum BuiltinFnId {
1346 BuiltinFnIdSetRuntimeSafety,1362 BuiltinFnIdSetRuntimeSafety,
1347 BuiltinFnIdSetFloatMode,1363 BuiltinFnIdSetFloatMode,
1348 BuiltinFnIdTypeName,1364 BuiltinFnIdTypeName,
1349 BuiltinFnIdCanImplicitCast,
1350 BuiltinFnIdPanic,1365 BuiltinFnIdPanic,
1351 BuiltinFnIdPtrCast,1366 BuiltinFnIdPtrCast,
1352 BuiltinFnIdBitCast,1367 BuiltinFnIdBitCast,
...@@ -1391,10 +1406,11 @@ enum PanicMsgId {...@@ -1391,10 +1406,11 @@ enum PanicMsgId {
1391 PanicMsgIdRemainderDivisionByZero,1406 PanicMsgIdRemainderDivisionByZero,
1392 PanicMsgIdExactDivisionRemainder,1407 PanicMsgIdExactDivisionRemainder,
1393 PanicMsgIdSliceWidenRemainder,1408 PanicMsgIdSliceWidenRemainder,
1394 PanicMsgIdUnwrapMaybeFail,1409 PanicMsgIdUnwrapOptionalFail,
1395 PanicMsgIdInvalidErrorCode,1410 PanicMsgIdInvalidErrorCode,
1396 PanicMsgIdIncorrectAlignment,1411 PanicMsgIdIncorrectAlignment,
1397 PanicMsgIdBadUnionField,1412 PanicMsgIdBadUnionField,
1413 PanicMsgIdBadEnumValue,
13981414
1399 PanicMsgIdCount,1415 PanicMsgIdCount,
1400};1416};
...@@ -1712,8 +1728,6 @@ struct CodeGen {...@@ -1712,8 +1728,6 @@ struct CodeGen {
1712 ZigList<Buf *> link_objects;1728 ZigList<Buf *> link_objects;
1713 ZigList<Buf *> assembly_files;1729 ZigList<Buf *> assembly_files;
17141730
1715 ZigList<TypeTableEntry *> name_table_enums;
1716
1717 Buf *test_filter;1731 Buf *test_filter;
1718 Buf *test_name_prefix;1732 Buf *test_name_prefix;
17191733
...@@ -2003,8 +2017,8 @@ enum IrInstructionId {...@@ -2003,8 +2017,8 @@ enum IrInstructionId {
2003 IrInstructionIdAsm,2017 IrInstructionIdAsm,
2004 IrInstructionIdSizeOf,2018 IrInstructionIdSizeOf,
2005 IrInstructionIdTestNonNull,2019 IrInstructionIdTestNonNull,
2006 IrInstructionIdUnwrapMaybe,2020 IrInstructionIdUnwrapOptional,
2007 IrInstructionIdMaybeWrap,2021 IrInstructionIdOptionalWrap,
2008 IrInstructionIdUnionTag,2022 IrInstructionIdUnionTag,
2009 IrInstructionIdClz,2023 IrInstructionIdClz,
2010 IrInstructionIdCtz,2024 IrInstructionIdCtz,
...@@ -2055,7 +2069,6 @@ enum IrInstructionId {...@@ -2055,7 +2069,6 @@ enum IrInstructionId {
2055 IrInstructionIdCheckSwitchProngs,2069 IrInstructionIdCheckSwitchProngs,
2056 IrInstructionIdCheckStatementIsVoid,2070 IrInstructionIdCheckStatementIsVoid,
2057 IrInstructionIdTypeName,2071 IrInstructionIdTypeName,
2058 IrInstructionIdCanImplicitCast,
2059 IrInstructionIdDeclRef,2072 IrInstructionIdDeclRef,
2060 IrInstructionIdPanic,2073 IrInstructionIdPanic,
2061 IrInstructionIdTagName,2074 IrInstructionIdTagName,
...@@ -2172,7 +2185,7 @@ enum IrUnOp {...@@ -2172,7 +2185,7 @@ enum IrUnOp {
2172 IrUnOpNegation,2185 IrUnOpNegation,
2173 IrUnOpNegationWrap,2186 IrUnOpNegationWrap,
2174 IrUnOpDereference,2187 IrUnOpDereference,
2175 IrUnOpMaybe,2188 IrUnOpOptional,
2176};2189};
21772190
2178struct IrInstructionUnOp {2191struct IrInstructionUnOp {
...@@ -2475,7 +2488,7 @@ struct IrInstructionTestNonNull {...@@ -2475,7 +2488,7 @@ struct IrInstructionTestNonNull {
2475 IrInstruction *value;2488 IrInstruction *value;
2476};2489};
24772490
2478struct IrInstructionUnwrapMaybe {2491struct IrInstructionUnwrapOptional {
2479 IrInstruction base;2492 IrInstruction base;
24802493
2481 IrInstruction *value;2494 IrInstruction *value;
...@@ -2733,7 +2746,7 @@ struct IrInstructionUnwrapErrPayload {...@@ -2733,7 +2746,7 @@ struct IrInstructionUnwrapErrPayload {
2733 bool safety_check_on;2746 bool safety_check_on;
2734};2747};
27352748
2736struct IrInstructionMaybeWrap {2749struct IrInstructionOptionalWrap {
2737 IrInstruction base;2750 IrInstruction base;
27382751
2739 IrInstruction *value;2752 IrInstruction *value;
...@@ -2848,13 +2861,6 @@ struct IrInstructionTypeName {...@@ -2848,13 +2861,6 @@ struct IrInstructionTypeName {
2848 IrInstruction *type_value;2861 IrInstruction *type_value;
2849};2862};
28502863
2851struct IrInstructionCanImplicitCast {
2852 IrInstruction base;
2853
2854 IrInstruction *type_value;
2855 IrInstruction *target_value;
2856};
2857
2858struct IrInstructionDeclRef {2864struct IrInstructionDeclRef {
2859 IrInstruction base;2865 IrInstruction base;
28602866
...@@ -2949,10 +2955,10 @@ struct IrInstructionExport {...@@ -2949,10 +2955,10 @@ struct IrInstructionExport {
2949struct IrInstructionErrorReturnTrace {2955struct IrInstructionErrorReturnTrace {
2950 IrInstruction base;2956 IrInstruction base;
29512957
2952 enum Nullable {2958 enum Optional {
2953 Null,2959 Null,
2954 NonNull,2960 NonNull,
2955 } nullable;2961 } optional;
2956};2962};
29572963
2958struct IrInstructionErrorUnion {2964struct IrInstructionErrorUnion {
src/analyze.cpp+215-161
...@@ -236,7 +236,7 @@ bool type_is_complete(TypeTableEntry *type_entry) {...@@ -236,7 +236,7 @@ bool type_is_complete(TypeTableEntry *type_entry) {
236 case TypeTableEntryIdComptimeInt:236 case TypeTableEntryIdComptimeInt:
237 case TypeTableEntryIdUndefined:237 case TypeTableEntryIdUndefined:
238 case TypeTableEntryIdNull:238 case TypeTableEntryIdNull:
239 case TypeTableEntryIdMaybe:239 case TypeTableEntryIdOptional:
240 case TypeTableEntryIdErrorUnion:240 case TypeTableEntryIdErrorUnion:
241 case TypeTableEntryIdErrorSet:241 case TypeTableEntryIdErrorSet:
242 case TypeTableEntryIdFn:242 case TypeTableEntryIdFn:
...@@ -272,7 +272,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) {...@@ -272,7 +272,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) {
272 case TypeTableEntryIdComptimeInt:272 case TypeTableEntryIdComptimeInt:
273 case TypeTableEntryIdUndefined:273 case TypeTableEntryIdUndefined:
274 case TypeTableEntryIdNull:274 case TypeTableEntryIdNull:
275 case TypeTableEntryIdMaybe:275 case TypeTableEntryIdOptional:
276 case TypeTableEntryIdErrorUnion:276 case TypeTableEntryIdErrorUnion:
277 case TypeTableEntryIdErrorSet:277 case TypeTableEntryIdErrorSet:
278 case TypeTableEntryIdFn:278 case TypeTableEntryIdFn:
...@@ -384,6 +384,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type...@@ -384,6 +384,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type
384 bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count)384 bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count)
385{385{
386 assert(!type_is_invalid(child_type));386 assert(!type_is_invalid(child_type));
387 assert(ptr_len == PtrLenSingle || child_type->id != TypeTableEntryIdOpaque);
387388
388 TypeId type_id = {};389 TypeId type_id = {};
389 TypeTableEntry **parent_pointer = nullptr;390 TypeTableEntry **parent_pointer = nullptr;
...@@ -519,9 +520,8 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {...@@ -519,9 +520,8 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
519 } else {520 } else {
520 ensure_complete_type(g, child_type);521 ensure_complete_type(g, child_type);
521522
522 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMaybe);523 TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdOptional);
523 assert(child_type->type_ref || child_type->zero_bits);524 assert(child_type->type_ref || child_type->zero_bits);
524 assert(child_type->di_type);
525 entry->is_copyable = type_is_copyable(g, child_type);525 entry->is_copyable = type_is_copyable(g, child_type);
526526
527 buf_resize(&entry->name, 0);527 buf_resize(&entry->name, 0);
...@@ -531,12 +531,14 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {...@@ -531,12 +531,14 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
531 entry->type_ref = LLVMInt1Type();531 entry->type_ref = LLVMInt1Type();
532 entry->di_type = g->builtin_types.entry_bool->di_type;532 entry->di_type = g->builtin_types.entry_bool->di_type;
533 } else if (type_is_codegen_pointer(child_type)) {533 } else if (type_is_codegen_pointer(child_type)) {
534 assert(child_type->di_type);
534 // this is an optimization but also is necessary for calling C535 // this is an optimization but also is necessary for calling C
535 // functions where all pointers are maybe pointers536 // functions where all pointers are maybe pointers
536 // function types are technically pointers537 // function types are technically pointers
537 entry->type_ref = child_type->type_ref;538 entry->type_ref = child_type->type_ref;
538 entry->di_type = child_type->di_type;539 entry->di_type = child_type->di_type;
539 } else {540 } else {
541 assert(child_type->di_type);
540 // create a struct with a boolean whether this is the null value542 // create a struct with a boolean whether this is the null value
541 LLVMTypeRef elem_types[] = {543 LLVMTypeRef elem_types[] = {
542 child_type->type_ref,544 child_type->type_ref,
...@@ -1360,7 +1362,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) {...@@ -1360,7 +1362,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) {
1360 return type_entry->data.structure.layout == ContainerLayoutPacked;1362 return type_entry->data.structure.layout == ContainerLayoutPacked;
1361 case TypeTableEntryIdUnion:1363 case TypeTableEntryIdUnion:
1362 return type_entry->data.unionation.layout == ContainerLayoutPacked;1364 return type_entry->data.unionation.layout == ContainerLayoutPacked;
1363 case TypeTableEntryIdMaybe:1365 case TypeTableEntryIdOptional:
1364 {1366 {
1365 TypeTableEntry *child_type = type_entry->data.maybe.child_type;1367 TypeTableEntry *child_type = type_entry->data.maybe.child_type;
1366 return type_is_codegen_pointer(child_type);1368 return type_is_codegen_pointer(child_type);
...@@ -1414,7 +1416,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {...@@ -1414,7 +1416,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
1414 return type_allowed_in_extern(g, type_entry->data.pointer.child_type);1416 return type_allowed_in_extern(g, type_entry->data.pointer.child_type);
1415 case TypeTableEntryIdStruct:1417 case TypeTableEntryIdStruct:
1416 return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked;1418 return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked;
1417 case TypeTableEntryIdMaybe:1419 case TypeTableEntryIdOptional:
1418 {1420 {
1419 TypeTableEntry *child_type = type_entry->data.maybe.child_type;1421 TypeTableEntry *child_type = type_entry->data.maybe.child_type;
1420 return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn;1422 return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn;
...@@ -1537,7 +1539,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c...@@ -1537,7 +1539,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1537 case TypeTableEntryIdPointer:1539 case TypeTableEntryIdPointer:
1538 case TypeTableEntryIdArray:1540 case TypeTableEntryIdArray:
1539 case TypeTableEntryIdStruct:1541 case TypeTableEntryIdStruct:
1540 case TypeTableEntryIdMaybe:1542 case TypeTableEntryIdOptional:
1541 case TypeTableEntryIdErrorUnion:1543 case TypeTableEntryIdErrorUnion:
1542 case TypeTableEntryIdErrorSet:1544 case TypeTableEntryIdErrorSet:
1543 case TypeTableEntryIdEnum:1545 case TypeTableEntryIdEnum:
...@@ -1631,7 +1633,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c...@@ -1631,7 +1633,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1631 case TypeTableEntryIdPointer:1633 case TypeTableEntryIdPointer:
1632 case TypeTableEntryIdArray:1634 case TypeTableEntryIdArray:
1633 case TypeTableEntryIdStruct:1635 case TypeTableEntryIdStruct:
1634 case TypeTableEntryIdMaybe:1636 case TypeTableEntryIdOptional:
1635 case TypeTableEntryIdErrorUnion:1637 case TypeTableEntryIdErrorUnion:
1636 case TypeTableEntryIdErrorSet:1638 case TypeTableEntryIdErrorSet:
1637 case TypeTableEntryIdEnum:1639 case TypeTableEntryIdEnum:
...@@ -2532,6 +2534,10 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {...@@ -2532,6 +2534,10 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
2532 continue;2534 continue;
2533 }2535 }
25342536
2537 if (type_requires_comptime(field_type)) {
2538 struct_type->data.structure.requires_comptime = true;
2539 }
2540
2535 if (!type_has_bits(field_type))2541 if (!type_has_bits(field_type))
2536 continue;2542 continue;
25372543
...@@ -2723,6 +2729,11 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {...@@ -2723,6 +2729,11 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
2723 }2729 }
2724 union_field->type_entry = field_type;2730 union_field->type_entry = field_type;
27252731
2732 if (type_requires_comptime(field_type)) {
2733 union_type->data.unionation.requires_comptime = true;
2734 }
2735
2736
2726 if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) {2737 if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) {
2727 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,2738 ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,
2728 buf_sprintf("non-enum union field assignment"));2739 buf_sprintf("non-enum union field assignment"));
...@@ -2975,8 +2986,8 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {...@@ -2975,8 +2986,8 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
2975 return wrong_panic_prototype(g, proto_node, fn_type);2986 return wrong_panic_prototype(g, proto_node, fn_type);
2976 }2987 }
29772988
2978 TypeTableEntry *nullable_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g));2989 TypeTableEntry *optional_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g));
2979 if (fn_type_id->param_info[1].type != nullable_ptr_to_stack_trace_type) {2990 if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) {
2980 return wrong_panic_prototype(g, proto_node, fn_type);2991 return wrong_panic_prototype(g, proto_node, fn_type);
2981 }2992 }
29822993
...@@ -3298,6 +3309,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {...@@ -3298,6 +3309,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
3298 case NodeTypeAsmExpr:3309 case NodeTypeAsmExpr:
3299 case NodeTypeFieldAccessExpr:3310 case NodeTypeFieldAccessExpr:
3300 case NodeTypePtrDeref:3311 case NodeTypePtrDeref:
3312 case NodeTypeUnwrapOptional:
3301 case NodeTypeStructField:3313 case NodeTypeStructField:
3302 case NodeTypeContainerInitExpr:3314 case NodeTypeContainerInitExpr:
3303 case NodeTypeStructValueField:3315 case NodeTypeStructValueField:
...@@ -3358,7 +3370,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt...@@ -3358,7 +3370,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt
3358 case TypeTableEntryIdPointer:3370 case TypeTableEntryIdPointer:
3359 case TypeTableEntryIdArray:3371 case TypeTableEntryIdArray:
3360 case TypeTableEntryIdStruct:3372 case TypeTableEntryIdStruct:
3361 case TypeTableEntryIdMaybe:3373 case TypeTableEntryIdOptional:
3362 case TypeTableEntryIdErrorUnion:3374 case TypeTableEntryIdErrorUnion:
3363 case TypeTableEntryIdErrorSet:3375 case TypeTableEntryIdErrorSet:
3364 case TypeTableEntryIdEnum:3376 case TypeTableEntryIdEnum:
...@@ -3736,7 +3748,7 @@ static bool is_container(TypeTableEntry *type_entry) {...@@ -3736,7 +3748,7 @@ static bool is_container(TypeTableEntry *type_entry) {
3736 case TypeTableEntryIdComptimeInt:3748 case TypeTableEntryIdComptimeInt:
3737 case TypeTableEntryIdUndefined:3749 case TypeTableEntryIdUndefined:
3738 case TypeTableEntryIdNull:3750 case TypeTableEntryIdNull:
3739 case TypeTableEntryIdMaybe:3751 case TypeTableEntryIdOptional:
3740 case TypeTableEntryIdErrorUnion:3752 case TypeTableEntryIdErrorUnion:
3741 case TypeTableEntryIdErrorSet:3753 case TypeTableEntryIdErrorSet:
3742 case TypeTableEntryIdFn:3754 case TypeTableEntryIdFn:
...@@ -3751,14 +3763,24 @@ static bool is_container(TypeTableEntry *type_entry) {...@@ -3751,14 +3763,24 @@ static bool is_container(TypeTableEntry *type_entry) {
3751 zig_unreachable();3763 zig_unreachable();
3752}3764}
37533765
3766bool is_ref(TypeTableEntry *type_entry) {
3767 return type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle;
3768}
3769
3770bool is_array_ref(TypeTableEntry *type_entry) {
3771 TypeTableEntry *array = is_ref(type_entry) ?
3772 type_entry->data.pointer.child_type : type_entry;
3773 return array->id == TypeTableEntryIdArray;
3774}
3775
3754bool is_container_ref(TypeTableEntry *type_entry) {3776bool is_container_ref(TypeTableEntry *type_entry) {
3755 return (type_entry->id == TypeTableEntryIdPointer) ?3777 return is_ref(type_entry) ?
3756 is_container(type_entry->data.pointer.child_type) : is_container(type_entry);3778 is_container(type_entry->data.pointer.child_type) : is_container(type_entry);
3757}3779}
37583780
3759TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) {3781TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) {
3760 assert(is_container_ref(type_entry));3782 assert(is_container_ref(type_entry));
3761 return (type_entry->id == TypeTableEntryIdPointer) ?3783 return is_ref(type_entry) ?
3762 type_entry->data.pointer.child_type : type_entry;3784 type_entry->data.pointer.child_type : type_entry;
3763}3785}
37643786
...@@ -3785,7 +3807,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) {...@@ -3785,7 +3807,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) {
3785 case TypeTableEntryIdComptimeInt:3807 case TypeTableEntryIdComptimeInt:
3786 case TypeTableEntryIdUndefined:3808 case TypeTableEntryIdUndefined:
3787 case TypeTableEntryIdNull:3809 case TypeTableEntryIdNull:
3788 case TypeTableEntryIdMaybe:3810 case TypeTableEntryIdOptional:
3789 case TypeTableEntryIdErrorUnion:3811 case TypeTableEntryIdErrorUnion:
3790 case TypeTableEntryIdErrorSet:3812 case TypeTableEntryIdErrorSet:
3791 case TypeTableEntryIdFn:3813 case TypeTableEntryIdFn:
...@@ -3804,7 +3826,7 @@ TypeTableEntry *get_codegen_ptr_type(TypeTableEntry *type) {...@@ -3804,7 +3826,7 @@ TypeTableEntry *get_codegen_ptr_type(TypeTableEntry *type) {
3804 if (type->id == TypeTableEntryIdPointer) return type;3826 if (type->id == TypeTableEntryIdPointer) return type;
3805 if (type->id == TypeTableEntryIdFn) return type;3827 if (type->id == TypeTableEntryIdFn) return type;
3806 if (type->id == TypeTableEntryIdPromise) return type;3828 if (type->id == TypeTableEntryIdPromise) return type;
3807 if (type->id == TypeTableEntryIdMaybe) {3829 if (type->id == TypeTableEntryIdOptional) {
3808 if (type->data.maybe.child_type->id == TypeTableEntryIdPointer) return type->data.maybe.child_type;3830 if (type->data.maybe.child_type->id == TypeTableEntryIdPointer) return type->data.maybe.child_type;
3809 if (type->data.maybe.child_type->id == TypeTableEntryIdFn) return type->data.maybe.child_type;3831 if (type->data.maybe.child_type->id == TypeTableEntryIdFn) return type->data.maybe.child_type;
3810 if (type->data.maybe.child_type->id == TypeTableEntryIdPromise) return type->data.maybe.child_type;3832 if (type->data.maybe.child_type->id == TypeTableEntryIdPromise) return type->data.maybe.child_type;
...@@ -4311,7 +4333,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {...@@ -4311,7 +4333,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
4311 return type_has_bits(type_entry);4333 return type_has_bits(type_entry);
4312 case TypeTableEntryIdErrorUnion:4334 case TypeTableEntryIdErrorUnion:
4313 return type_has_bits(type_entry->data.error_union.payload_type);4335 return type_has_bits(type_entry->data.error_union.payload_type);
4314 case TypeTableEntryIdMaybe:4336 case TypeTableEntryIdOptional:
4315 return type_has_bits(type_entry->data.maybe.child_type) &&4337 return type_has_bits(type_entry->data.maybe.child_type) &&
4316 !type_is_codegen_pointer(type_entry->data.maybe.child_type);4338 !type_is_codegen_pointer(type_entry->data.maybe.child_type);
4317 case TypeTableEntryIdUnion:4339 case TypeTableEntryIdUnion:
...@@ -4558,6 +4580,52 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {...@@ -4558,6 +4580,52 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {
4558 return true;4580 return true;
4559}4581}
45604582
4583static uint32_t hash_const_val_ptr(ConstExprValue *const_val) {
4584 uint32_t hash_val = 0;
4585 switch (const_val->data.x_ptr.mut) {
4586 case ConstPtrMutRuntimeVar:
4587 hash_val += (uint32_t)3500721036;
4588 break;
4589 case ConstPtrMutComptimeConst:
4590 hash_val += (uint32_t)4214318515;
4591 break;
4592 case ConstPtrMutComptimeVar:
4593 hash_val += (uint32_t)1103195694;
4594 break;
4595 }
4596 switch (const_val->data.x_ptr.special) {
4597 case ConstPtrSpecialInvalid:
4598 zig_unreachable();
4599 case ConstPtrSpecialRef:
4600 hash_val += (uint32_t)2478261866;
4601 hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee);
4602 return hash_val;
4603 case ConstPtrSpecialBaseArray:
4604 hash_val += (uint32_t)1764906839;
4605 hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
4606 hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
4607 hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492;
4608 return hash_val;
4609 case ConstPtrSpecialBaseStruct:
4610 hash_val += (uint32_t)3518317043;
4611 hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
4612 hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
4613 return hash_val;
4614 case ConstPtrSpecialHardCodedAddr:
4615 hash_val += (uint32_t)4048518294;
4616 hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
4617 return hash_val;
4618 case ConstPtrSpecialDiscard:
4619 hash_val += 2010123162;
4620 return hash_val;
4621 case ConstPtrSpecialFunction:
4622 hash_val += (uint32_t)2590901619;
4623 hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
4624 return hash_val;
4625 }
4626 zig_unreachable();
4627}
4628
4561static uint32_t hash_const_val(ConstExprValue *const_val) {4629static uint32_t hash_const_val(ConstExprValue *const_val) {
4562 assert(const_val->special == ConstValSpecialStatic);4630 assert(const_val->special == ConstValSpecialStatic);
4563 switch (const_val->type->id) {4631 switch (const_val->type->id) {
...@@ -4626,51 +4694,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {...@@ -4626,51 +4694,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
4626 assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);4694 assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);
4627 return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);4695 return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
4628 case TypeTableEntryIdPointer:4696 case TypeTableEntryIdPointer:
4629 {4697 return hash_const_val_ptr(const_val);
4630 uint32_t hash_val = 0;
4631 switch (const_val->data.x_ptr.mut) {
4632 case ConstPtrMutRuntimeVar:
4633 hash_val += (uint32_t)3500721036;
4634 break;
4635 case ConstPtrMutComptimeConst:
4636 hash_val += (uint32_t)4214318515;
4637 break;
4638 case ConstPtrMutComptimeVar:
4639 hash_val += (uint32_t)1103195694;
4640 break;
4641 }
4642 switch (const_val->data.x_ptr.special) {
4643 case ConstPtrSpecialInvalid:
4644 zig_unreachable();
4645 case ConstPtrSpecialRef:
4646 hash_val += (uint32_t)2478261866;
4647 hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee);
4648 return hash_val;
4649 case ConstPtrSpecialBaseArray:
4650 hash_val += (uint32_t)1764906839;
4651 hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
4652 hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
4653 hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492;
4654 return hash_val;
4655 case ConstPtrSpecialBaseStruct:
4656 hash_val += (uint32_t)3518317043;
4657 hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
4658 hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
4659 return hash_val;
4660 case ConstPtrSpecialHardCodedAddr:
4661 hash_val += (uint32_t)4048518294;
4662 hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
4663 return hash_val;
4664 case ConstPtrSpecialDiscard:
4665 hash_val += 2010123162;
4666 return hash_val;
4667 case ConstPtrSpecialFunction:
4668 hash_val += (uint32_t)2590901619;
4669 hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
4670 return hash_val;
4671 }
4672 zig_unreachable();
4673 }
4674 case TypeTableEntryIdPromise:4698 case TypeTableEntryIdPromise:
4675 // TODO better hashing algorithm4699 // TODO better hashing algorithm
4676 return 223048345;4700 return 223048345;
...@@ -4687,11 +4711,15 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {...@@ -4687,11 +4711,15 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
4687 case TypeTableEntryIdUnion:4711 case TypeTableEntryIdUnion:
4688 // TODO better hashing algorithm4712 // TODO better hashing algorithm
4689 return 2709806591;4713 return 2709806591;
4690 case TypeTableEntryIdMaybe:4714 case TypeTableEntryIdOptional:
4691 if (const_val->data.x_maybe) {4715 if (get_codegen_ptr_type(const_val->type) != nullptr) {
4692 return hash_const_val(const_val->data.x_maybe) * 1992916303;4716 return hash_const_val(const_val) * 1992916303;
4693 } else {4717 } else {
4694 return 4016830364;4718 if (const_val->data.x_optional) {
4719 return hash_const_val(const_val->data.x_optional) * 1992916303;
4720 } else {
4721 return 4016830364;
4722 }
4695 }4723 }
4696 case TypeTableEntryIdErrorUnion:4724 case TypeTableEntryIdErrorUnion:
4697 // TODO better hashing algorithm4725 // TODO better hashing algorithm
...@@ -4791,10 +4819,12 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) {...@@ -4791,10 +4819,12 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) {
4791 }4819 }
4792 return false;4820 return false;
47934821
4794 case TypeTableEntryIdMaybe:4822 case TypeTableEntryIdOptional:
4795 if (value->data.x_maybe == nullptr)4823 if (get_codegen_ptr_type(value->type) != nullptr)
4824 return value->data.x_ptr.mut == ConstPtrMutComptimeVar;
4825 if (value->data.x_optional == nullptr)
4796 return false;4826 return false;
4797 return can_mutate_comptime_var_state(value->data.x_maybe);4827 return can_mutate_comptime_var_state(value->data.x_optional);
47984828
4799 case TypeTableEntryIdErrorUnion:4829 case TypeTableEntryIdErrorUnion:
4800 if (value->data.x_err_union.err != nullptr)4830 if (value->data.x_err_union.err != nullptr)
...@@ -4841,7 +4871,7 @@ static bool return_type_is_cacheable(TypeTableEntry *return_type) {...@@ -4841,7 +4871,7 @@ static bool return_type_is_cacheable(TypeTableEntry *return_type) {
4841 case TypeTableEntryIdUnion:4871 case TypeTableEntryIdUnion:
4842 return false;4872 return false;
48434873
4844 case TypeTableEntryIdMaybe:4874 case TypeTableEntryIdOptional:
4845 return return_type_is_cacheable(return_type->data.maybe.child_type);4875 return return_type_is_cacheable(return_type->data.maybe.child_type);
48464876
4847 case TypeTableEntryIdErrorUnion:4877 case TypeTableEntryIdErrorUnion:
...@@ -4943,17 +4973,29 @@ bool type_requires_comptime(TypeTableEntry *type_entry) {...@@ -4943,17 +4973,29 @@ bool type_requires_comptime(TypeTableEntry *type_entry) {
4943 case TypeTableEntryIdArgTuple:4973 case TypeTableEntryIdArgTuple:
4944 return true;4974 return true;
4945 case TypeTableEntryIdArray:4975 case TypeTableEntryIdArray:
4976 return type_requires_comptime(type_entry->data.array.child_type);
4946 case TypeTableEntryIdStruct:4977 case TypeTableEntryIdStruct:
4978 assert(type_has_zero_bits_known(type_entry));
4979 return type_entry->data.structure.requires_comptime;
4947 case TypeTableEntryIdUnion:4980 case TypeTableEntryIdUnion:
4948 case TypeTableEntryIdMaybe:4981 assert(type_has_zero_bits_known(type_entry));
4982 return type_entry->data.unionation.requires_comptime;
4983 case TypeTableEntryIdOptional:
4984 return type_requires_comptime(type_entry->data.maybe.child_type);
4949 case TypeTableEntryIdErrorUnion:4985 case TypeTableEntryIdErrorUnion:
4986 return type_requires_comptime(type_entry->data.error_union.payload_type);
4987 case TypeTableEntryIdPointer:
4988 if (type_entry->data.pointer.child_type->id == TypeTableEntryIdOpaque) {
4989 return false;
4990 } else {
4991 return type_requires_comptime(type_entry->data.pointer.child_type);
4992 }
4950 case TypeTableEntryIdEnum:4993 case TypeTableEntryIdEnum:
4951 case TypeTableEntryIdErrorSet:4994 case TypeTableEntryIdErrorSet:
4952 case TypeTableEntryIdFn:4995 case TypeTableEntryIdFn:
4953 case TypeTableEntryIdBool:4996 case TypeTableEntryIdBool:
4954 case TypeTableEntryIdInt:4997 case TypeTableEntryIdInt:
4955 case TypeTableEntryIdFloat:4998 case TypeTableEntryIdFloat:
4956 case TypeTableEntryIdPointer:
4957 case TypeTableEntryIdVoid:4999 case TypeTableEntryIdVoid:
4958 case TypeTableEntryIdUnreachable:5000 case TypeTableEntryIdUnreachable:
4959 case TypeTableEntryIdPromise:5001 case TypeTableEntryIdPromise:
...@@ -5308,6 +5350,52 @@ bool ir_get_var_is_comptime(VariableTableEntry *var) {...@@ -5308,6 +5350,52 @@ bool ir_get_var_is_comptime(VariableTableEntry *var) {
5308 return var->is_comptime->value.data.x_bool;5350 return var->is_comptime->value.data.x_bool;
5309}5351}
53105352
5353bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
5354 if (a->data.x_ptr.special != b->data.x_ptr.special)
5355 return false;
5356 if (a->data.x_ptr.mut != b->data.x_ptr.mut)
5357 return false;
5358 switch (a->data.x_ptr.special) {
5359 case ConstPtrSpecialInvalid:
5360 zig_unreachable();
5361 case ConstPtrSpecialRef:
5362 if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
5363 return false;
5364 return true;
5365 case ConstPtrSpecialBaseArray:
5366 if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val &&
5367 a->data.x_ptr.data.base_array.array_val->global_refs !=
5368 b->data.x_ptr.data.base_array.array_val->global_refs)
5369 {
5370 return false;
5371 }
5372 if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
5373 return false;
5374 if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr)
5375 return false;
5376 return true;
5377 case ConstPtrSpecialBaseStruct:
5378 if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val &&
5379 a->data.x_ptr.data.base_struct.struct_val->global_refs !=
5380 b->data.x_ptr.data.base_struct.struct_val->global_refs)
5381 {
5382 return false;
5383 }
5384 if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
5385 return false;
5386 return true;
5387 case ConstPtrSpecialHardCodedAddr:
5388 if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
5389 return false;
5390 return true;
5391 case ConstPtrSpecialDiscard:
5392 return true;
5393 case ConstPtrSpecialFunction:
5394 return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry;
5395 }
5396 zig_unreachable();
5397}
5398
5311bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {5399bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
5312 assert(a->type->id == b->type->id);5400 assert(a->type->id == b->type->id);
5313 assert(a->special == ConstValSpecialStatic);5401 assert(a->special == ConstValSpecialStatic);
...@@ -5359,49 +5447,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {...@@ -5359,49 +5447,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
5359 return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ;5447 return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ;
5360 case TypeTableEntryIdPointer:5448 case TypeTableEntryIdPointer:
5361 case TypeTableEntryIdFn:5449 case TypeTableEntryIdFn:
5362 if (a->data.x_ptr.special != b->data.x_ptr.special)5450 return const_values_equal_ptr(a, b);
5363 return false;
5364 if (a->data.x_ptr.mut != b->data.x_ptr.mut)
5365 return false;
5366 switch (a->data.x_ptr.special) {
5367 case ConstPtrSpecialInvalid:
5368 zig_unreachable();
5369 case ConstPtrSpecialRef:
5370 if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
5371 return false;
5372 return true;
5373 case ConstPtrSpecialBaseArray:
5374 if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val &&
5375 a->data.x_ptr.data.base_array.array_val->global_refs !=
5376 b->data.x_ptr.data.base_array.array_val->global_refs)
5377 {
5378 return false;
5379 }
5380 if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
5381 return false;
5382 if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr)
5383 return false;
5384 return true;
5385 case ConstPtrSpecialBaseStruct:
5386 if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val &&
5387 a->data.x_ptr.data.base_struct.struct_val->global_refs !=
5388 b->data.x_ptr.data.base_struct.struct_val->global_refs)
5389 {
5390 return false;
5391 }
5392 if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
5393 return false;
5394 return true;
5395 case ConstPtrSpecialHardCodedAddr:
5396 if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
5397 return false;
5398 return true;
5399 case ConstPtrSpecialDiscard:
5400 return true;
5401 case ConstPtrSpecialFunction:
5402 return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry;
5403 }
5404 zig_unreachable();
5405 case TypeTableEntryIdArray:5451 case TypeTableEntryIdArray:
5406 zig_panic("TODO");5452 zig_panic("TODO");
5407 case TypeTableEntryIdStruct:5453 case TypeTableEntryIdStruct:
...@@ -5416,11 +5462,13 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {...@@ -5416,11 +5462,13 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
5416 zig_panic("TODO");5462 zig_panic("TODO");
5417 case TypeTableEntryIdNull:5463 case TypeTableEntryIdNull:
5418 zig_panic("TODO");5464 zig_panic("TODO");
5419 case TypeTableEntryIdMaybe:5465 case TypeTableEntryIdOptional:
5420 if (a->data.x_maybe == nullptr || b->data.x_maybe == nullptr) {5466 if (get_codegen_ptr_type(a->type) != nullptr)
5421 return (a->data.x_maybe == nullptr && b->data.x_maybe == nullptr);5467 return const_values_equal_ptr(a, b);
5468 if (a->data.x_optional == nullptr || b->data.x_optional == nullptr) {
5469 return (a->data.x_optional == nullptr && b->data.x_optional == nullptr);
5422 } else {5470 } else {
5423 return const_values_equal(a->data.x_maybe, b->data.x_maybe);5471 return const_values_equal(a->data.x_optional, b->data.x_optional);
5424 }5472 }
5425 case TypeTableEntryIdErrorUnion:5473 case TypeTableEntryIdErrorUnion:
5426 zig_panic("TODO");5474 zig_panic("TODO");
...@@ -5493,6 +5541,41 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *...@@ -5493,6 +5541,41 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *
5493 }5541 }
5494}5542}
54955543
5544void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeTableEntry *type_entry) {
5545 switch (const_val->data.x_ptr.special) {
5546 case ConstPtrSpecialInvalid:
5547 zig_unreachable();
5548 case ConstPtrSpecialRef:
5549 case ConstPtrSpecialBaseStruct:
5550 buf_appendf(buf, "*");
5551 render_const_value(g, buf, const_ptr_pointee(g, const_val));
5552 return;
5553 case ConstPtrSpecialBaseArray:
5554 if (const_val->data.x_ptr.data.base_array.is_cstr) {
5555 buf_appendf(buf, "*(c str lit)");
5556 return;
5557 } else {
5558 buf_appendf(buf, "*");
5559 render_const_value(g, buf, const_ptr_pointee(g, const_val));
5560 return;
5561 }
5562 case ConstPtrSpecialHardCodedAddr:
5563 buf_appendf(buf, "(*%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name),
5564 const_val->data.x_ptr.data.hard_coded_addr.addr);
5565 return;
5566 case ConstPtrSpecialDiscard:
5567 buf_append_str(buf, "*_");
5568 return;
5569 case ConstPtrSpecialFunction:
5570 {
5571 FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
5572 buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name));
5573 return;
5574 }
5575 }
5576 zig_unreachable();
5577}
5578
5496void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {5579void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
5497 switch (const_val->special) {5580 switch (const_val->special) {
5498 case ConstValSpecialRuntime:5581 case ConstValSpecialRuntime:
...@@ -5569,38 +5652,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {...@@ -5569,38 +5652,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
5569 return;5652 return;
5570 }5653 }
5571 case TypeTableEntryIdPointer:5654 case TypeTableEntryIdPointer:
5572 switch (const_val->data.x_ptr.special) {5655 return render_const_val_ptr(g, buf, const_val, type_entry);
5573 case ConstPtrSpecialInvalid:
5574 zig_unreachable();
5575 case ConstPtrSpecialRef:
5576 case ConstPtrSpecialBaseStruct:
5577 buf_appendf(buf, "&");
5578 render_const_value(g, buf, const_ptr_pointee(g, const_val));
5579 return;
5580 case ConstPtrSpecialBaseArray:
5581 if (const_val->data.x_ptr.data.base_array.is_cstr) {
5582 buf_appendf(buf, "&(c str lit)");
5583 return;
5584 } else {
5585 buf_appendf(buf, "&");
5586 render_const_value(g, buf, const_ptr_pointee(g, const_val));
5587 return;
5588 }
5589 case ConstPtrSpecialHardCodedAddr:
5590 buf_appendf(buf, "(&%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name),
5591 const_val->data.x_ptr.data.hard_coded_addr.addr);
5592 return;
5593 case ConstPtrSpecialDiscard:
5594 buf_append_str(buf, "&_");
5595 return;
5596 case ConstPtrSpecialFunction:
5597 {
5598 FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
5599 buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name));
5600 return;
5601 }
5602 }
5603 zig_unreachable();
5604 case TypeTableEntryIdBlock:5656 case TypeTableEntryIdBlock:
5605 {5657 {
5606 AstNode *node = const_val->data.x_block->source_node;5658 AstNode *node = const_val->data.x_block->source_node;
...@@ -5658,10 +5710,12 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {...@@ -5658,10 +5710,12 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
5658 buf_appendf(buf, "undefined");5710 buf_appendf(buf, "undefined");
5659 return;5711 return;
5660 }5712 }
5661 case TypeTableEntryIdMaybe:5713 case TypeTableEntryIdOptional:
5662 {5714 {
5663 if (const_val->data.x_maybe) {5715 if (get_codegen_ptr_type(const_val->type) != nullptr)
5664 render_const_value(g, buf, const_val->data.x_maybe);5716 return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type);
5717 if (const_val->data.x_optional) {
5718 render_const_value(g, buf, const_val->data.x_optional);
5665 } else {5719 } else {
5666 buf_appendf(buf, "null");5720 buf_appendf(buf, "null");
5667 }5721 }
...@@ -5767,7 +5821,7 @@ uint32_t type_id_hash(TypeId x) {...@@ -5767,7 +5821,7 @@ uint32_t type_id_hash(TypeId x) {
5767 case TypeTableEntryIdComptimeInt:5821 case TypeTableEntryIdComptimeInt:
5768 case TypeTableEntryIdUndefined:5822 case TypeTableEntryIdUndefined:
5769 case TypeTableEntryIdNull:5823 case TypeTableEntryIdNull:
5770 case TypeTableEntryIdMaybe:5824 case TypeTableEntryIdOptional:
5771 case TypeTableEntryIdErrorSet:5825 case TypeTableEntryIdErrorSet:
5772 case TypeTableEntryIdEnum:5826 case TypeTableEntryIdEnum:
5773 case TypeTableEntryIdUnion:5827 case TypeTableEntryIdUnion:
...@@ -5813,7 +5867,7 @@ bool type_id_eql(TypeId a, TypeId b) {...@@ -5813,7 +5867,7 @@ bool type_id_eql(TypeId a, TypeId b) {
5813 case TypeTableEntryIdComptimeInt:5867 case TypeTableEntryIdComptimeInt:
5814 case TypeTableEntryIdUndefined:5868 case TypeTableEntryIdUndefined:
5815 case TypeTableEntryIdNull:5869 case TypeTableEntryIdNull:
5816 case TypeTableEntryIdMaybe:5870 case TypeTableEntryIdOptional:
5817 case TypeTableEntryIdPromise:5871 case TypeTableEntryIdPromise:
5818 case TypeTableEntryIdErrorSet:5872 case TypeTableEntryIdErrorSet:
5819 case TypeTableEntryIdEnum:5873 case TypeTableEntryIdEnum:
...@@ -5935,7 +5989,7 @@ static const TypeTableEntryId all_type_ids[] = {...@@ -5935,7 +5989,7 @@ static const TypeTableEntryId all_type_ids[] = {
5935 TypeTableEntryIdComptimeInt,5989 TypeTableEntryIdComptimeInt,
5936 TypeTableEntryIdUndefined,5990 TypeTableEntryIdUndefined,
5937 TypeTableEntryIdNull,5991 TypeTableEntryIdNull,
5938 TypeTableEntryIdMaybe,5992 TypeTableEntryIdOptional,
5939 TypeTableEntryIdErrorUnion,5993 TypeTableEntryIdErrorUnion,
5940 TypeTableEntryIdErrorSet,5994 TypeTableEntryIdErrorSet,
5941 TypeTableEntryIdEnum,5995 TypeTableEntryIdEnum,
...@@ -5980,7 +6034,7 @@ size_t type_id_index(TypeTableEntry *entry) {...@@ -5980,7 +6034,7 @@ size_t type_id_index(TypeTableEntry *entry) {
5980 return 7;6034 return 7;
5981 case TypeTableEntryIdStruct:6035 case TypeTableEntryIdStruct:
5982 if (entry->data.structure.is_slice)6036 if (entry->data.structure.is_slice)
5983 return 25;6037 return 6;
5984 return 8;6038 return 8;
5985 case TypeTableEntryIdComptimeFloat:6039 case TypeTableEntryIdComptimeFloat:
5986 return 9;6040 return 9;
...@@ -5990,7 +6044,7 @@ size_t type_id_index(TypeTableEntry *entry) {...@@ -5990,7 +6044,7 @@ size_t type_id_index(TypeTableEntry *entry) {
5990 return 11;6044 return 11;
5991 case TypeTableEntryIdNull:6045 case TypeTableEntryIdNull:
5992 return 12;6046 return 12;
5993 case TypeTableEntryIdMaybe:6047 case TypeTableEntryIdOptional:
5994 return 13;6048 return 13;
5995 case TypeTableEntryIdErrorUnion:6049 case TypeTableEntryIdErrorUnion:
5996 return 14;6050 return 14;
...@@ -6048,8 +6102,8 @@ const char *type_id_name(TypeTableEntryId id) {...@@ -6048,8 +6102,8 @@ const char *type_id_name(TypeTableEntryId id) {
6048 return "Undefined";6102 return "Undefined";
6049 case TypeTableEntryIdNull:6103 case TypeTableEntryIdNull:
6050 return "Null";6104 return "Null";
6051 case TypeTableEntryIdMaybe:6105 case TypeTableEntryIdOptional:
6052 return "Nullable";6106 return "Optional";
6053 case TypeTableEntryIdErrorUnion:6107 case TypeTableEntryIdErrorUnion:
6054 return "ErrorUnion";6108 return "ErrorUnion";
6055 case TypeTableEntryIdErrorSet:6109 case TypeTableEntryIdErrorSet:
src/analyze.hpp+2
...@@ -70,6 +70,8 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name);...@@ -70,6 +70,8 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name);
70TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag);70TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag);
71TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag);71TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag);
7272
73bool is_ref(TypeTableEntry *type_entry);
74bool is_array_ref(TypeTableEntry *type_entry);
73bool is_container_ref(TypeTableEntry *type_entry);75bool is_container_ref(TypeTableEntry *type_entry);
74void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);76void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
75void scan_import(CodeGen *g, ImportTableEntry *import);77void scan_import(CodeGen *g, ImportTableEntry *import);
src/ast_render.cpp+11-3
...@@ -50,7 +50,7 @@ static const char *bin_op_str(BinOpType bin_op) {...@@ -50,7 +50,7 @@ static const char *bin_op_str(BinOpType bin_op) {
50 case BinOpTypeAssignBitXor: return "^=";50 case BinOpTypeAssignBitXor: return "^=";
51 case BinOpTypeAssignBitOr: return "|=";51 case BinOpTypeAssignBitOr: return "|=";
52 case BinOpTypeAssignMergeErrorSets: return "||=";52 case BinOpTypeAssignMergeErrorSets: return "||=";
53 case BinOpTypeUnwrapMaybe: return "??";53 case BinOpTypeUnwrapOptional: return "orelse";
54 case BinOpTypeArrayCat: return "++";54 case BinOpTypeArrayCat: return "++";
55 case BinOpTypeArrayMult: return "**";55 case BinOpTypeArrayMult: return "**";
56 case BinOpTypeErrorUnion: return "!";56 case BinOpTypeErrorUnion: return "!";
...@@ -66,8 +66,7 @@ static const char *prefix_op_str(PrefixOp prefix_op) {...@@ -66,8 +66,7 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
66 case PrefixOpNegationWrap: return "-%";66 case PrefixOpNegationWrap: return "-%";
67 case PrefixOpBoolNot: return "!";67 case PrefixOpBoolNot: return "!";
68 case PrefixOpBinNot: return "~";68 case PrefixOpBinNot: return "~";
69 case PrefixOpMaybe: return "?";69 case PrefixOpOptional: return "?";
70 case PrefixOpUnwrapMaybe: return "??";
71 case PrefixOpAddrOf: return "&";70 case PrefixOpAddrOf: return "&";
72 }71 }
73 zig_unreachable();72 zig_unreachable();
...@@ -222,6 +221,8 @@ static const char *node_type_str(NodeType node_type) {...@@ -222,6 +221,8 @@ static const char *node_type_str(NodeType node_type) {
222 return "FieldAccessExpr";221 return "FieldAccessExpr";
223 case NodeTypePtrDeref:222 case NodeTypePtrDeref:
224 return "PtrDerefExpr";223 return "PtrDerefExpr";
224 case NodeTypeUnwrapOptional:
225 return "UnwrapOptional";
225 case NodeTypeContainerDecl:226 case NodeTypeContainerDecl:
226 return "ContainerDecl";227 return "ContainerDecl";
227 case NodeTypeStructField:228 case NodeTypeStructField:
...@@ -711,6 +712,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -711,6 +712,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
711 fprintf(ar->f, ".*");712 fprintf(ar->f, ".*");
712 break;713 break;
713 }714 }
715 case NodeTypeUnwrapOptional:
716 {
717 AstNode *lhs = node->data.unwrap_optional.expr;
718 render_node_ungrouped(ar, lhs);
719 fprintf(ar->f, ".?");
720 break;
721 }
714 case NodeTypeUndefinedLiteral:722 case NodeTypeUndefinedLiteral:
715 fprintf(ar->f, "undefined");723 fprintf(ar->f, "undefined");
716 break;724 break;
src/codegen.cpp+251-193
...@@ -869,7 +869,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {...@@ -869,7 +869,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
869 return buf_create_from_str("exact division produced remainder");869 return buf_create_from_str("exact division produced remainder");
870 case PanicMsgIdSliceWidenRemainder:870 case PanicMsgIdSliceWidenRemainder:
871 return buf_create_from_str("slice widening size mismatch");871 return buf_create_from_str("slice widening size mismatch");
872 case PanicMsgIdUnwrapMaybeFail:872 case PanicMsgIdUnwrapOptionalFail:
873 return buf_create_from_str("attempt to unwrap null");873 return buf_create_from_str("attempt to unwrap null");
874 case PanicMsgIdUnreachable:874 case PanicMsgIdUnreachable:
875 return buf_create_from_str("reached unreachable code");875 return buf_create_from_str("reached unreachable code");
...@@ -879,6 +879,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {...@@ -879,6 +879,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
879 return buf_create_from_str("incorrect alignment");879 return buf_create_from_str("incorrect alignment");
880 case PanicMsgIdBadUnionField:880 case PanicMsgIdBadUnionField:
881 return buf_create_from_str("access of inactive union field");881 return buf_create_from_str("access of inactive union field");
882 case PanicMsgIdBadEnumValue:
883 return buf_create_from_str("invalid enum value");
882 }884 }
883 zig_unreachable();885 zig_unreachable();
884}886}
...@@ -2497,7 +2499,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,...@@ -2497,7 +2499,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
2497 assert(wanted_type->data.structure.is_slice);2499 assert(wanted_type->data.structure.is_slice);
2498 assert(actual_type->id == TypeTableEntryIdArray);2500 assert(actual_type->id == TypeTableEntryIdArray);
24992501
2500 TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry;2502 TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
2501 TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type;2503 TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type;
25022504
25032505
...@@ -2543,6 +2545,29 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,...@@ -2543,6 +2545,29 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
2543 return expr_val;2545 return expr_val;
2544 case CastOpBitCast:2546 case CastOpBitCast:
2545 return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, "");2547 return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, "");
2548 case CastOpPtrOfArrayToSlice: {
2549 assert(cast_instruction->tmp_ptr);
2550 assert(actual_type->id == TypeTableEntryIdPointer);
2551 TypeTableEntry *array_type = actual_type->data.pointer.child_type;
2552 assert(array_type->id == TypeTableEntryIdArray);
2553
2554 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr,
2555 slice_ptr_index, "");
2556 LLVMValueRef indices[] = {
2557 LLVMConstNull(g->builtin_types.entry_usize->type_ref),
2558 LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false),
2559 };
2560 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, "");
2561 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
2562
2563 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr,
2564 slice_len_index, "");
2565 LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
2566 array_type->data.array.len, false);
2567 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
2568
2569 return cast_instruction->tmp_ptr;
2570 }
2546 }2571 }
2547 zig_unreachable();2572 zig_unreachable();
2548}2573}
...@@ -2678,7 +2703,7 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst...@@ -2678,7 +2703,7 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst
26782703
2679 switch (op_id) {2704 switch (op_id) {
2680 case IrUnOpInvalid:2705 case IrUnOpInvalid:
2681 case IrUnOpMaybe:2706 case IrUnOpOptional:
2682 case IrUnOpDereference:2707 case IrUnOpDereference:
2683 zig_unreachable();2708 zig_unreachable();
2684 case IrUnOpNegation:2709 case IrUnOpNegation:
...@@ -3249,7 +3274,7 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru...@@ -3249,7 +3274,7 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
3249}3274}
32503275
3251static LLVMValueRef gen_non_null_bit(CodeGen *g, TypeTableEntry *maybe_type, LLVMValueRef maybe_handle) {3276static LLVMValueRef gen_non_null_bit(CodeGen *g, TypeTableEntry *maybe_type, LLVMValueRef maybe_handle) {
3252 assert(maybe_type->id == TypeTableEntryIdMaybe);3277 assert(maybe_type->id == TypeTableEntryIdOptional);
3253 TypeTableEntry *child_type = maybe_type->data.maybe.child_type;3278 TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
3254 if (child_type->zero_bits) {3279 if (child_type->zero_bits) {
3255 return maybe_handle;3280 return maybe_handle;
...@@ -3271,23 +3296,23 @@ static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable...@@ -3271,23 +3296,23 @@ static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable
3271}3296}
32723297
3273static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,3298static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
3274 IrInstructionUnwrapMaybe *instruction)3299 IrInstructionUnwrapOptional *instruction)
3275{3300{
3276 TypeTableEntry *ptr_type = instruction->value->value.type;3301 TypeTableEntry *ptr_type = instruction->value->value.type;
3277 assert(ptr_type->id == TypeTableEntryIdPointer);3302 assert(ptr_type->id == TypeTableEntryIdPointer);
3278 TypeTableEntry *maybe_type = ptr_type->data.pointer.child_type;3303 TypeTableEntry *maybe_type = ptr_type->data.pointer.child_type;
3279 assert(maybe_type->id == TypeTableEntryIdMaybe);3304 assert(maybe_type->id == TypeTableEntryIdOptional);
3280 TypeTableEntry *child_type = maybe_type->data.maybe.child_type;3305 TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
3281 LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value);3306 LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value);
3282 LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type);3307 LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type);
3283 if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) {3308 if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) {
3284 LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle);3309 LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle);
3285 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeOk");3310 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk");
3286 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeFail");3311 LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail");
3287 LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block);3312 LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block);
32883313
3289 LLVMPositionBuilderAtEnd(g->builder, fail_block);3314 LLVMPositionBuilderAtEnd(g->builder, fail_block);
3290 gen_safety_crash(g, PanicMsgIdUnwrapMaybeFail);3315 gen_safety_crash(g, PanicMsgIdUnwrapOptionalFail);
32913316
3292 LLVMPositionBuilderAtEnd(g->builder, ok_block);3317 LLVMPositionBuilderAtEnd(g->builder, ok_block);
3293 }3318 }
...@@ -3432,34 +3457,112 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI...@@ -3432,34 +3457,112 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI
3432 return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, "");3457 return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, "");
3433}3458}
34343459
3460static LLVMValueRef get_enum_tag_name_function(CodeGen *g, TypeTableEntry *enum_type) {
3461 assert(enum_type->id == TypeTableEntryIdEnum);
3462 if (enum_type->data.enumeration.name_function)
3463 return enum_type->data.enumeration.name_function;
3464
3465 TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false,
3466 PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
3467 TypeTableEntry *u8_slice_type = get_slice_type(g, u8_ptr_type);
3468 TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type;
3469
3470 LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMPointerType(u8_slice_type->type_ref, 0),
3471 &tag_int_type->type_ref, 1, false);
3472
3473 Buf *fn_name = get_mangled_name(g, buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name)), false);
3474 LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
3475 LLVMSetLinkage(fn_val, LLVMInternalLinkage);
3476 LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
3477 addLLVMFnAttr(fn_val, "nounwind");
3478 add_uwtable_attr(g, fn_val);
3479 if (g->build_mode == BuildModeDebug) {
3480 ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
3481 ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr);
3482 }
3483
3484 LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
3485 LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder);
3486 FnTableEntry *prev_cur_fn = g->cur_fn;
3487 LLVMValueRef prev_cur_fn_val = g->cur_fn_val;
3488
3489 LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
3490 LLVMPositionBuilderAtEnd(g->builder, entry_block);
3491 ZigLLVMClearCurrentDebugLocation(g->builder);
3492 g->cur_fn = nullptr;
3493 g->cur_fn_val = fn_val;
3494
3495 size_t field_count = enum_type->data.enumeration.src_field_count;
3496 LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue");
3497 LLVMValueRef tag_int_value = LLVMGetParam(fn_val, 0);
3498 LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count);
3499
3500
3501 TypeTableEntry *usize = g->builtin_types.entry_usize;
3502 LLVMValueRef array_ptr_indices[] = {
3503 LLVMConstNull(usize->type_ref),
3504 LLVMConstNull(usize->type_ref),
3505 };
3506
3507 for (size_t field_i = 0; field_i < field_count; field_i += 1) {
3508 Buf *name = enum_type->data.enumeration.fields[field_i].name;
3509 LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true);
3510 LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), "");
3511 LLVMSetInitializer(str_global, str_init);
3512 LLVMSetLinkage(str_global, LLVMPrivateLinkage);
3513 LLVMSetGlobalConstant(str_global, true);
3514 LLVMSetUnnamedAddr(str_global, true);
3515 LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init)));
3516
3517 LLVMValueRef fields[] = {
3518 LLVMConstGEP(str_global, array_ptr_indices, 2),
3519 LLVMConstInt(g->builtin_types.entry_usize->type_ref, buf_len(name), false),
3520 };
3521 LLVMValueRef slice_init_value = LLVMConstNamedStruct(u8_slice_type->type_ref, fields, 2);
3522
3523 LLVMValueRef slice_global = LLVMAddGlobal(g->module, LLVMTypeOf(slice_init_value), "");
3524 LLVMSetInitializer(slice_global, slice_init_value);
3525 LLVMSetLinkage(slice_global, LLVMPrivateLinkage);
3526 LLVMSetGlobalConstant(slice_global, true);
3527 LLVMSetUnnamedAddr(slice_global, true);
3528 LLVMSetAlignment(slice_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(slice_init_value)));
3529
3530 LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "Name");
3531 LLVMValueRef this_tag_int_value = bigint_to_llvm_const(tag_int_type->type_ref,
3532 &enum_type->data.enumeration.fields[field_i].value);
3533 LLVMAddCase(switch_instr, this_tag_int_value, return_block);
3534
3535 LLVMPositionBuilderAtEnd(g->builder, return_block);
3536 LLVMBuildRet(g->builder, slice_global);
3537 }
3538
3539 LLVMPositionBuilderAtEnd(g->builder, bad_value_block);
3540 if (g->build_mode == BuildModeDebug || g->build_mode == BuildModeSafeRelease) {
3541 gen_safety_crash(g, PanicMsgIdBadEnumValue);
3542 } else {
3543 LLVMBuildUnreachable(g->builder);
3544 }
3545
3546 g->cur_fn = prev_cur_fn;
3547 g->cur_fn_val = prev_cur_fn_val;
3548 LLVMPositionBuilderAtEnd(g->builder, prev_block);
3549 LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
3550
3551 enum_type->data.enumeration.name_function = fn_val;
3552 return fn_val;
3553}
3554
3435static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable,3555static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable,
3436 IrInstructionTagName *instruction)3556 IrInstructionTagName *instruction)
3437{3557{
3438 TypeTableEntry *enum_type = instruction->target->value.type;3558 TypeTableEntry *enum_type = instruction->target->value.type;
3439 assert(enum_type->id == TypeTableEntryIdEnum);3559 assert(enum_type->id == TypeTableEntryIdEnum);
3440 assert(enum_type->data.enumeration.generate_name_table);
34413560
3442 TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type;3561 LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type);
3443 LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target);
3444 if (ir_want_runtime_safety(g, &instruction->base)) {
3445 size_t field_count = enum_type->data.enumeration.src_field_count;
3446
3447 // if the field_count can't fit in the bits of the enum_type, then it can't possibly
3448 // be the wrong value
3449 BigInt field_bi;
3450 bigint_init_unsigned(&field_bi, field_count);
3451 if (bigint_fits_in_bits(&field_bi, tag_int_type->data.integral.bit_count, false)) {
3452 LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(enum_tag_value), field_count, false);
3453 add_bounds_check(g, enum_tag_value, LLVMIntEQ, nullptr, LLVMIntULT, end_val);
3454 }
3455 }
34563562
3457 LLVMValueRef indices[] = {3563 LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target);
3458 LLVMConstNull(g->builtin_types.entry_usize->type_ref),3564 return ZigLLVMBuildCall(g->builder, enum_name_function, &enum_tag_value, 1,
3459 gen_widen_or_shorten(g, false, tag_int_type,3565 get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
3460 g->builtin_types.entry_usize, enum_tag_value),
3461 };
3462 return LLVMBuildInBoundsGEP(g->builder, enum_type->data.enumeration.name_table, indices, 2, "");
3463}3566}
34643567
3465static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executable,3568static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executable,
...@@ -3509,17 +3612,17 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I...@@ -3509,17 +3612,17 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
3509 } else if (target_type->id == TypeTableEntryIdFn) {3612 } else if (target_type->id == TypeTableEntryIdFn) {
3510 align_bytes = target_type->data.fn.fn_type_id.alignment;3613 align_bytes = target_type->data.fn.fn_type_id.alignment;
3511 ptr_val = target_val;3614 ptr_val = target_val;
3512 } else if (target_type->id == TypeTableEntryIdMaybe &&3615 } else if (target_type->id == TypeTableEntryIdOptional &&
3513 target_type->data.maybe.child_type->id == TypeTableEntryIdPointer)3616 target_type->data.maybe.child_type->id == TypeTableEntryIdPointer)
3514 {3617 {
3515 align_bytes = target_type->data.maybe.child_type->data.pointer.alignment;3618 align_bytes = target_type->data.maybe.child_type->data.pointer.alignment;
3516 ptr_val = target_val;3619 ptr_val = target_val;
3517 } else if (target_type->id == TypeTableEntryIdMaybe &&3620 } else if (target_type->id == TypeTableEntryIdOptional &&
3518 target_type->data.maybe.child_type->id == TypeTableEntryIdFn)3621 target_type->data.maybe.child_type->id == TypeTableEntryIdFn)
3519 {3622 {
3520 align_bytes = target_type->data.maybe.child_type->data.fn.fn_type_id.alignment;3623 align_bytes = target_type->data.maybe.child_type->data.fn.fn_type_id.alignment;
3521 ptr_val = target_val;3624 ptr_val = target_val;
3522 } else if (target_type->id == TypeTableEntryIdMaybe &&3625 } else if (target_type->id == TypeTableEntryIdOptional &&
3523 target_type->data.maybe.child_type->id == TypeTableEntryIdPromise)3626 target_type->data.maybe.child_type->id == TypeTableEntryIdPromise)
3524 {3627 {
3525 zig_panic("TODO audit this function");3628 zig_panic("TODO audit this function");
...@@ -3621,7 +3724,7 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn...@@ -3621,7 +3724,7 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn
3621 success_order, failure_order, instruction->is_weak);3724 success_order, failure_order, instruction->is_weak);
36223725
3623 TypeTableEntry *maybe_type = instruction->base.value.type;3726 TypeTableEntry *maybe_type = instruction->base.value.type;
3624 assert(maybe_type->id == TypeTableEntryIdMaybe);3727 assert(maybe_type->id == TypeTableEntryIdOptional);
3625 TypeTableEntry *child_type = maybe_type->data.maybe.child_type;3728 TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
36263729
3627 if (type_is_codegen_pointer(child_type)) {3730 if (type_is_codegen_pointer(child_type)) {
...@@ -3730,7 +3833,6 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst...@@ -3730,7 +3833,6 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
3730 } else {3833 } else {
3731 end_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false);3834 end_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false);
3732 }3835 }
3733
3734 if (want_runtime_safety) {3836 if (want_runtime_safety) {
3735 add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);3837 add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
3736 if (instruction->end) {3838 if (instruction->end) {
...@@ -4008,10 +4110,10 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu...@@ -4008,10 +4110,10 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu
4008 }4110 }
4009}4111}
40104112
4011static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, IrInstructionMaybeWrap *instruction) {4113static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, IrInstructionOptionalWrap *instruction) {
4012 TypeTableEntry *wanted_type = instruction->base.value.type;4114 TypeTableEntry *wanted_type = instruction->base.value.type;
40134115
4014 assert(wanted_type->id == TypeTableEntryIdMaybe);4116 assert(wanted_type->id == TypeTableEntryIdOptional);
40154117
4016 TypeTableEntry *child_type = wanted_type->data.maybe.child_type;4118 TypeTableEntry *child_type = wanted_type->data.maybe.child_type;
40174119
...@@ -4540,7 +4642,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -4540,7 +4642,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
4540 case IrInstructionIdCheckSwitchProngs:4642 case IrInstructionIdCheckSwitchProngs:
4541 case IrInstructionIdCheckStatementIsVoid:4643 case IrInstructionIdCheckStatementIsVoid:
4542 case IrInstructionIdTypeName:4644 case IrInstructionIdTypeName:
4543 case IrInstructionIdCanImplicitCast:
4544 case IrInstructionIdDeclRef:4645 case IrInstructionIdDeclRef:
4545 case IrInstructionIdSwitchVar:4646 case IrInstructionIdSwitchVar:
4546 case IrInstructionIdOffsetOf:4647 case IrInstructionIdOffsetOf:
...@@ -4593,8 +4694,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -4593,8 +4694,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
4593 return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);4694 return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);
4594 case IrInstructionIdTestNonNull:4695 case IrInstructionIdTestNonNull:
4595 return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction);4696 return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction);
4596 case IrInstructionIdUnwrapMaybe:4697 case IrInstructionIdUnwrapOptional:
4597 return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapMaybe *)instruction);4698 return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapOptional *)instruction);
4598 case IrInstructionIdClz:4699 case IrInstructionIdClz:
4599 return ir_render_clz(g, executable, (IrInstructionClz *)instruction);4700 return ir_render_clz(g, executable, (IrInstructionClz *)instruction);
4600 case IrInstructionIdCtz:4701 case IrInstructionIdCtz:
...@@ -4635,8 +4736,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -4635,8 +4736,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
4635 return ir_render_unwrap_err_code(g, executable, (IrInstructionUnwrapErrCode *)instruction);4736 return ir_render_unwrap_err_code(g, executable, (IrInstructionUnwrapErrCode *)instruction);
4636 case IrInstructionIdUnwrapErrPayload:4737 case IrInstructionIdUnwrapErrPayload:
4637 return ir_render_unwrap_err_payload(g, executable, (IrInstructionUnwrapErrPayload *)instruction);4738 return ir_render_unwrap_err_payload(g, executable, (IrInstructionUnwrapErrPayload *)instruction);
4638 case IrInstructionIdMaybeWrap:4739 case IrInstructionIdOptionalWrap:
4639 return ir_render_maybe_wrap(g, executable, (IrInstructionMaybeWrap *)instruction);4740 return ir_render_maybe_wrap(g, executable, (IrInstructionOptionalWrap *)instruction);
4640 case IrInstructionIdErrWrapCode:4741 case IrInstructionIdErrWrapCode:
4641 return ir_render_err_wrap_code(g, executable, (IrInstructionErrWrapCode *)instruction);4742 return ir_render_err_wrap_code(g, executable, (IrInstructionErrWrapCode *)instruction);
4642 case IrInstructionIdErrWrapPayload:4743 case IrInstructionIdErrWrapPayload:
...@@ -4866,7 +4967,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con...@@ -4866,7 +4967,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
4866 }4967 }
4867 case TypeTableEntryIdPointer:4968 case TypeTableEntryIdPointer:
4868 case TypeTableEntryIdFn:4969 case TypeTableEntryIdFn:
4869 case TypeTableEntryIdMaybe:4970 case TypeTableEntryIdOptional:
4870 case TypeTableEntryIdPromise:4971 case TypeTableEntryIdPromise:
4871 {4972 {
4872 LLVMValueRef ptr_val = gen_const_val(g, const_val, "");4973 LLVMValueRef ptr_val = gen_const_val(g, const_val, "");
...@@ -4914,6 +5015,79 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef...@@ -4914,6 +5015,79 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef
4914 return LLVMTypeOf(val) != type_entry->type_ref;5015 return LLVMTypeOf(val) != type_entry->type_ref;
4915}5016}
49165017
5018static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, const char *name) {
5019 render_const_val_global(g, const_val, name);
5020 switch (const_val->data.x_ptr.special) {
5021 case ConstPtrSpecialInvalid:
5022 case ConstPtrSpecialDiscard:
5023 zig_unreachable();
5024 case ConstPtrSpecialRef:
5025 {
5026 ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
5027 render_const_val(g, pointee, "");
5028 render_const_val_global(g, pointee, "");
5029 ConstExprValue *other_val = pointee;
5030 const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref);
5031 render_const_val_global(g, const_val, "");
5032 return const_val->global_refs->llvm_value;
5033 }
5034 case ConstPtrSpecialBaseArray:
5035 {
5036 ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
5037 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
5038 assert(array_const_val->type->id == TypeTableEntryIdArray);
5039 if (array_const_val->type->zero_bits) {
5040 // make this a null pointer
5041 TypeTableEntry *usize = g->builtin_types.entry_usize;
5042 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
5043 const_val->type->type_ref);
5044 render_const_val_global(g, const_val, "");
5045 return const_val->global_refs->llvm_value;
5046 }
5047 LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val,
5048 elem_index);
5049 LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
5050 const_val->global_refs->llvm_value = ptr_val;
5051 render_const_val_global(g, const_val, "");
5052 return ptr_val;
5053 }
5054 case ConstPtrSpecialBaseStruct:
5055 {
5056 ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val;
5057 assert(struct_const_val->type->id == TypeTableEntryIdStruct);
5058 if (struct_const_val->type->zero_bits) {
5059 // make this a null pointer
5060 TypeTableEntry *usize = g->builtin_types.entry_usize;
5061 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
5062 const_val->type->type_ref);
5063 render_const_val_global(g, const_val, "");
5064 return const_val->global_refs->llvm_value;
5065 }
5066 size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index;
5067 size_t gen_field_index =
5068 struct_const_val->type->data.structure.fields[src_field_index].gen_index;
5069 LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val,
5070 gen_field_index);
5071 LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
5072 const_val->global_refs->llvm_value = ptr_val;
5073 render_const_val_global(g, const_val, "");
5074 return ptr_val;
5075 }
5076 case ConstPtrSpecialHardCodedAddr:
5077 {
5078 uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr;
5079 TypeTableEntry *usize = g->builtin_types.entry_usize;
5080 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false),
5081 const_val->type->type_ref);
5082 render_const_val_global(g, const_val, "");
5083 return const_val->global_refs->llvm_value;
5084 }
5085 case ConstPtrSpecialFunction:
5086 return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref);
5087 }
5088 zig_unreachable();
5089}
5090
4917static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {5091static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {
4918 TypeTableEntry *type_entry = const_val->type;5092 TypeTableEntry *type_entry = const_val->type;
4919 assert(!type_entry->zero_bits);5093 assert(!type_entry->zero_bits);
...@@ -4958,23 +5132,19 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c...@@ -4958,23 +5132,19 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
4958 } else {5132 } else {
4959 return LLVMConstNull(LLVMInt1Type());5133 return LLVMConstNull(LLVMInt1Type());
4960 }5134 }
4961 case TypeTableEntryIdMaybe:5135 case TypeTableEntryIdOptional:
4962 {5136 {
4963 TypeTableEntry *child_type = type_entry->data.maybe.child_type;5137 TypeTableEntry *child_type = type_entry->data.maybe.child_type;
4964 if (child_type->zero_bits) {5138 if (child_type->zero_bits) {
4965 return LLVMConstInt(LLVMInt1Type(), const_val->data.x_maybe ? 1 : 0, false);5139 return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false);
4966 } else if (type_is_codegen_pointer(child_type)) {5140 } else if (type_is_codegen_pointer(child_type)) {
4967 if (const_val->data.x_maybe) {5141 return gen_const_val_ptr(g, const_val, name);
4968 return gen_const_val(g, const_val->data.x_maybe, "");
4969 } else {
4970 return LLVMConstNull(child_type->type_ref);
4971 }
4972 } else {5142 } else {
4973 LLVMValueRef child_val;5143 LLVMValueRef child_val;
4974 LLVMValueRef maybe_val;5144 LLVMValueRef maybe_val;
4975 bool make_unnamed_struct;5145 bool make_unnamed_struct;
4976 if (const_val->data.x_maybe) {5146 if (const_val->data.x_optional) {
4977 child_val = gen_const_val(g, const_val->data.x_maybe, "");5147 child_val = gen_const_val(g, const_val->data.x_optional, "");
4978 maybe_val = LLVMConstAllOnes(LLVMInt1Type());5148 maybe_val = LLVMConstAllOnes(LLVMInt1Type());
49795149
4980 make_unnamed_struct = is_llvm_value_unnamed_type(const_val->type, child_val);5150 make_unnamed_struct = is_llvm_value_unnamed_type(const_val->type, child_val);
...@@ -5164,78 +5334,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c...@@ -5164,78 +5334,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
5164 assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst);5334 assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
5165 return fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry);5335 return fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry);
5166 case TypeTableEntryIdPointer:5336 case TypeTableEntryIdPointer:
5167 {5337 return gen_const_val_ptr(g, const_val, name);
5168 render_const_val_global(g, const_val, name);
5169 switch (const_val->data.x_ptr.special) {
5170 case ConstPtrSpecialInvalid:
5171 case ConstPtrSpecialDiscard:
5172 zig_unreachable();
5173 case ConstPtrSpecialRef:
5174 {
5175 ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
5176 render_const_val(g, pointee, "");
5177 render_const_val_global(g, pointee, "");
5178 ConstExprValue *other_val = pointee;
5179 const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref);
5180 render_const_val_global(g, const_val, "");
5181 return const_val->global_refs->llvm_value;
5182 }
5183 case ConstPtrSpecialBaseArray:
5184 {
5185 ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
5186 size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
5187 assert(array_const_val->type->id == TypeTableEntryIdArray);
5188 if (array_const_val->type->zero_bits) {
5189 // make this a null pointer
5190 TypeTableEntry *usize = g->builtin_types.entry_usize;
5191 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
5192 const_val->type->type_ref);
5193 render_const_val_global(g, const_val, "");
5194 return const_val->global_refs->llvm_value;
5195 }
5196 LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val,
5197 elem_index);
5198 LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
5199 const_val->global_refs->llvm_value = ptr_val;
5200 render_const_val_global(g, const_val, "");
5201 return ptr_val;
5202 }
5203 case ConstPtrSpecialBaseStruct:
5204 {
5205 ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val;
5206 assert(struct_const_val->type->id == TypeTableEntryIdStruct);
5207 if (struct_const_val->type->zero_bits) {
5208 // make this a null pointer
5209 TypeTableEntry *usize = g->builtin_types.entry_usize;
5210 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
5211 const_val->type->type_ref);
5212 render_const_val_global(g, const_val, "");
5213 return const_val->global_refs->llvm_value;
5214 }
5215 size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index;
5216 size_t gen_field_index =
5217 struct_const_val->type->data.structure.fields[src_field_index].gen_index;
5218 LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val,
5219 gen_field_index);
5220 LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
5221 const_val->global_refs->llvm_value = ptr_val;
5222 render_const_val_global(g, const_val, "");
5223 return ptr_val;
5224 }
5225 case ConstPtrSpecialHardCodedAddr:
5226 {
5227 uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr;
5228 TypeTableEntry *usize = g->builtin_types.entry_usize;
5229 const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false),
5230 const_val->type->type_ref);
5231 render_const_val_global(g, const_val, "");
5232 return const_val->global_refs->llvm_value;
5233 }
5234 case ConstPtrSpecialFunction:
5235 return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref);
5236 }
5237 }
5238 zig_unreachable();
5239 case TypeTableEntryIdErrorUnion:5338 case TypeTableEntryIdErrorUnion:
5240 {5339 {
5241 TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;5340 TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;
...@@ -5367,55 +5466,6 @@ static void generate_error_name_table(CodeGen *g) {...@@ -5367,55 +5466,6 @@ static void generate_error_name_table(CodeGen *g) {
5367 LLVMSetAlignment(g->err_name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(err_name_table_init)));5466 LLVMSetAlignment(g->err_name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(err_name_table_init)));
5368}5467}
53695468
5370static void generate_enum_name_tables(CodeGen *g) {
5371 TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
5372 PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
5373 TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type);
5374
5375 TypeTableEntry *usize = g->builtin_types.entry_usize;
5376 LLVMValueRef array_ptr_indices[] = {
5377 LLVMConstNull(usize->type_ref),
5378 LLVMConstNull(usize->type_ref),
5379 };
5380
5381
5382 for (size_t enum_i = 0; enum_i < g->name_table_enums.length; enum_i += 1) {
5383 TypeTableEntry *enum_type = g->name_table_enums.at(enum_i);
5384 assert(enum_type->id == TypeTableEntryIdEnum);
5385
5386 size_t field_count = enum_type->data.enumeration.src_field_count;
5387 LLVMValueRef *values = allocate<LLVMValueRef>(field_count);
5388 for (size_t field_i = 0; field_i < field_count; field_i += 1) {
5389 Buf *name = enum_type->data.enumeration.fields[field_i].name;
5390
5391 LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true);
5392 LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), "");
5393 LLVMSetInitializer(str_global, str_init);
5394 LLVMSetLinkage(str_global, LLVMPrivateLinkage);
5395 LLVMSetGlobalConstant(str_global, true);
5396 LLVMSetUnnamedAddr(str_global, true);
5397 LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init)));
5398
5399 LLVMValueRef fields[] = {
5400 LLVMConstGEP(str_global, array_ptr_indices, 2),
5401 LLVMConstInt(g->builtin_types.entry_usize->type_ref, buf_len(name), false),
5402 };
5403 values[field_i] = LLVMConstNamedStruct(str_type->type_ref, fields, 2);
5404 }
5405
5406 LLVMValueRef name_table_init = LLVMConstArray(str_type->type_ref, values, (unsigned)field_count);
5407
5408 Buf *table_name = get_mangled_name(g, buf_sprintf("%s_name_table", buf_ptr(&enum_type->name)), false);
5409 LLVMValueRef name_table = LLVMAddGlobal(g->module, LLVMTypeOf(name_table_init), buf_ptr(table_name));
5410 LLVMSetInitializer(name_table, name_table_init);
5411 LLVMSetLinkage(name_table, LLVMPrivateLinkage);
5412 LLVMSetGlobalConstant(name_table, true);
5413 LLVMSetUnnamedAddr(name_table, true);
5414 LLVMSetAlignment(name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(name_table_init)));
5415 enum_type->data.enumeration.name_table = name_table;
5416 }
5417}
5418
5419static void build_all_basic_blocks(CodeGen *g, FnTableEntry *fn) {5469static void build_all_basic_blocks(CodeGen *g, FnTableEntry *fn) {
5420 IrExecutable *executable = &fn->analyzed_executable;5470 IrExecutable *executable = &fn->analyzed_executable;
5421 assert(executable->basic_block_list.length > 0);5471 assert(executable->basic_block_list.length > 0);
...@@ -5512,7 +5562,6 @@ static void do_code_gen(CodeGen *g) {...@@ -5512,7 +5562,6 @@ static void do_code_gen(CodeGen *g) {
5512 }5562 }
55135563
5514 generate_error_name_table(g);5564 generate_error_name_table(g);
5515 generate_enum_name_tables(g);
55165565
5517 // Generate module level variables5566 // Generate module level variables
5518 for (size_t i = 0; i < g->global_vars.length; i += 1) {5567 for (size_t i = 0; i < g->global_vars.length; i += 1) {
...@@ -5651,8 +5700,8 @@ static void do_code_gen(CodeGen *g) {...@@ -5651,8 +5700,8 @@ static void do_code_gen(CodeGen *g) {
5651 } else if (instruction->id == IrInstructionIdSlice) {5700 } else if (instruction->id == IrInstructionIdSlice) {
5652 IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction;5701 IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction;
5653 slot = &slice_instruction->tmp_ptr;5702 slot = &slice_instruction->tmp_ptr;
5654 } else if (instruction->id == IrInstructionIdMaybeWrap) {5703 } else if (instruction->id == IrInstructionIdOptionalWrap) {
5655 IrInstructionMaybeWrap *maybe_wrap_instruction = (IrInstructionMaybeWrap *)instruction;5704 IrInstructionOptionalWrap *maybe_wrap_instruction = (IrInstructionOptionalWrap *)instruction;
5656 slot = &maybe_wrap_instruction->tmp_ptr;5705 slot = &maybe_wrap_instruction->tmp_ptr;
5657 } else if (instruction->id == IrInstructionIdErrWrapPayload) {5706 } else if (instruction->id == IrInstructionIdErrWrapPayload) {
5658 IrInstructionErrWrapPayload *err_wrap_payload_instruction = (IrInstructionErrWrapPayload *)instruction;5707 IrInstructionErrWrapPayload *err_wrap_payload_instruction = (IrInstructionErrWrapPayload *)instruction;
...@@ -6192,7 +6241,6 @@ static void define_builtin_fns(CodeGen *g) {...@@ -6192,7 +6241,6 @@ static void define_builtin_fns(CodeGen *g) {
6192 create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1);6241 create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1);
6193 create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1);6242 create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1);
6194 create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1);6243 create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1);
6195 create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2);
6196 create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);6244 create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);
6197 create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6);6245 create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6);
6198 create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6);6246 create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6);
...@@ -6250,13 +6298,7 @@ static const char *build_mode_to_str(BuildMode build_mode) {...@@ -6250,13 +6298,7 @@ static const char *build_mode_to_str(BuildMode build_mode) {
6250 zig_unreachable();6298 zig_unreachable();
6251}6299}
62526300
6253static void define_builtin_compile_vars(CodeGen *g) {6301Buf *codegen_generate_builtin_source(CodeGen *g) {
6254 if (g->std_package == nullptr)
6255 return;
6256
6257 const char *builtin_zig_basename = "builtin.zig";
6258 Buf *builtin_zig_path = buf_alloc();
6259 os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
6260 Buf *contents = buf_alloc();6302 Buf *contents = buf_alloc();
62616303
6262 // Modifications to this struct must be coordinated with code that does anything with6304 // Modifications to this struct must be coordinated with code that does anything with
...@@ -6396,7 +6438,6 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -6396,7 +6438,6 @@ static void define_builtin_compile_vars(CodeGen *g) {
6396 const TypeTableEntryId id = type_id_at_index(i);6438 const TypeTableEntryId id = type_id_at_index(i);
6397 buf_appendf(contents, " %s,\n", type_id_name(id));6439 buf_appendf(contents, " %s,\n", type_id_name(id));
6398 }6440 }
6399 buf_appendf(contents, " Slice,\n");
6400 buf_appendf(contents, "};\n\n");6441 buf_appendf(contents, "};\n\n");
6401 }6442 }
6402 {6443 {
...@@ -6409,14 +6450,13 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -6409,14 +6450,13 @@ static void define_builtin_compile_vars(CodeGen *g) {
6409 " Int: Int,\n"6450 " Int: Int,\n"
6410 " Float: Float,\n"6451 " Float: Float,\n"
6411 " Pointer: Pointer,\n"6452 " Pointer: Pointer,\n"
6412 " Slice: Slice,\n"
6413 " Array: Array,\n"6453 " Array: Array,\n"
6414 " Struct: Struct,\n"6454 " Struct: Struct,\n"
6415 " ComptimeFloat: void,\n"6455 " ComptimeFloat: void,\n"
6416 " ComptimeInt: void,\n"6456 " ComptimeInt: void,\n"
6417 " Undefined: void,\n"6457 " Undefined: void,\n"
6418 " Null: void,\n"6458 " Null: void,\n"
6419 " Nullable: Nullable,\n"6459 " Optional: Optional,\n"
6420 " ErrorUnion: ErrorUnion,\n"6460 " ErrorUnion: ErrorUnion,\n"
6421 " ErrorSet: ErrorSet,\n"6461 " ErrorSet: ErrorSet,\n"
6422 " Enum: Enum,\n"6462 " Enum: Enum,\n"
...@@ -6439,13 +6479,18 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -6439,13 +6479,18 @@ static void define_builtin_compile_vars(CodeGen *g) {
6439 " };\n"6479 " };\n"
6440 "\n"6480 "\n"
6441 " pub const Pointer = struct {\n"6481 " pub const Pointer = struct {\n"
6482 " size: Size,\n"
6442 " is_const: bool,\n"6483 " is_const: bool,\n"
6443 " is_volatile: bool,\n"6484 " is_volatile: bool,\n"
6444 " alignment: u32,\n"6485 " alignment: u32,\n"
6445 " child: type,\n"6486 " child: type,\n"
6446 " };\n"
6447 "\n"6487 "\n"
6448 " pub const Slice = Pointer;\n"6488 " pub const Size = enum {\n"
6489 " One,\n"
6490 " Many,\n"
6491 " Slice,\n"
6492 " };\n"
6493 " };\n"
6449 "\n"6494 "\n"
6450 " pub const Array = struct {\n"6495 " pub const Array = struct {\n"
6451 " len: usize,\n"6496 " len: usize,\n"
...@@ -6470,7 +6515,7 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -6470,7 +6515,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
6470 " defs: []Definition,\n"6515 " defs: []Definition,\n"
6471 " };\n"6516 " };\n"
6472 "\n"6517 "\n"
6473 " pub const Nullable = struct {\n"6518 " pub const Optional = struct {\n"
6474 " child: type,\n"6519 " child: type,\n"
6475 " };\n"6520 " };\n"
6476 "\n"6521 "\n"
...@@ -6619,6 +6664,19 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -6619,6 +6664,19 @@ static void define_builtin_compile_vars(CodeGen *g) {
66196664
6620 buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n");6665 buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n");
66216666
6667
6668 return contents;
6669}
6670
6671static void define_builtin_compile_vars(CodeGen *g) {
6672 if (g->std_package == nullptr)
6673 return;
6674
6675 const char *builtin_zig_basename = "builtin.zig";
6676 Buf *builtin_zig_path = buf_alloc();
6677 os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
6678
6679 Buf *contents = codegen_generate_builtin_source(g);
6622 ensure_cache_dir(g);6680 ensure_cache_dir(g);
6623 os_write_file(builtin_zig_path, contents);6681 os_write_file(builtin_zig_path, contents);
66246682
...@@ -7032,7 +7090,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry...@@ -7032,7 +7090,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry
7032 case TypeTableEntryIdArray:7090 case TypeTableEntryIdArray:
7033 prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type);7091 prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type);
7034 return;7092 return;
7035 case TypeTableEntryIdMaybe:7093 case TypeTableEntryIdOptional:
7036 prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type);7094 prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type);
7037 return;7095 return;
7038 case TypeTableEntryIdFn:7096 case TypeTableEntryIdFn:
...@@ -7121,7 +7179,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf...@@ -7121,7 +7179,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf
7121 buf_appendf(out_buf, "%s%s *", const_str, buf_ptr(&child_buf));7179 buf_appendf(out_buf, "%s%s *", const_str, buf_ptr(&child_buf));
7122 break;7180 break;
7123 }7181 }
7124 case TypeTableEntryIdMaybe:7182 case TypeTableEntryIdOptional:
7125 {7183 {
7126 TypeTableEntry *child_type = type_entry->data.maybe.child_type;7184 TypeTableEntry *child_type = type_entry->data.maybe.child_type;
7127 if (child_type->zero_bits) {7185 if (child_type->zero_bits) {
...@@ -7335,7 +7393,7 @@ static void gen_h_file(CodeGen *g) {...@@ -7335,7 +7393,7 @@ static void gen_h_file(CodeGen *g) {
7335 case TypeTableEntryIdBlock:7393 case TypeTableEntryIdBlock:
7336 case TypeTableEntryIdBoundFn:7394 case TypeTableEntryIdBoundFn:
7337 case TypeTableEntryIdArgTuple:7395 case TypeTableEntryIdArgTuple:
7338 case TypeTableEntryIdMaybe:7396 case TypeTableEntryIdOptional:
7339 case TypeTableEntryIdFn:7397 case TypeTableEntryIdFn:
7340 case TypeTableEntryIdPromise:7398 case TypeTableEntryIdPromise:
7341 zig_unreachable();7399 zig_unreachable();
src/codegen.hpp+2
...@@ -59,5 +59,7 @@ void codegen_add_object(CodeGen *g, Buf *object_path);...@@ -59,5 +59,7 @@ void codegen_add_object(CodeGen *g, Buf *object_path);
5959
60void codegen_translate_c(CodeGen *g, Buf *path);60void codegen_translate_c(CodeGen *g, Buf *path);
6161
62Buf *codegen_generate_builtin_source(CodeGen *g);
63
6264
63#endif65#endif
src/ir.cpp+603-453
...@@ -47,7 +47,7 @@ enum ConstCastResultId {...@@ -47,7 +47,7 @@ enum ConstCastResultId {
47 ConstCastResultIdErrSetGlobal,47 ConstCastResultIdErrSetGlobal,
48 ConstCastResultIdPointerChild,48 ConstCastResultIdPointerChild,
49 ConstCastResultIdSliceChild,49 ConstCastResultIdSliceChild,
50 ConstCastResultIdNullableChild,50 ConstCastResultIdOptionalChild,
51 ConstCastResultIdErrorUnionPayload,51 ConstCastResultIdErrorUnionPayload,
52 ConstCastResultIdErrorUnionErrorSet,52 ConstCastResultIdErrorUnionErrorSet,
53 ConstCastResultIdFnAlign,53 ConstCastResultIdFnAlign,
...@@ -62,6 +62,7 @@ enum ConstCastResultId {...@@ -62,6 +62,7 @@ enum ConstCastResultId {
62 ConstCastResultIdType,62 ConstCastResultIdType,
63 ConstCastResultIdUnresolvedInferredErrSet,63 ConstCastResultIdUnresolvedInferredErrSet,
64 ConstCastResultIdAsyncAllocatorType,64 ConstCastResultIdAsyncAllocatorType,
65 ConstCastResultIdNullWrapPtr,
65};66};
6667
67struct ConstCastErrSetMismatch {68struct ConstCastErrSetMismatch {
...@@ -85,11 +86,12 @@ struct ConstCastOnly {...@@ -85,11 +86,12 @@ struct ConstCastOnly {
85 ConstCastErrSetMismatch error_set;86 ConstCastErrSetMismatch error_set;
86 ConstCastOnly *pointer_child;87 ConstCastOnly *pointer_child;
87 ConstCastOnly *slice_child;88 ConstCastOnly *slice_child;
88 ConstCastOnly *nullable_child;89 ConstCastOnly *optional_child;
89 ConstCastOnly *error_union_payload;90 ConstCastOnly *error_union_payload;
90 ConstCastOnly *error_union_error_set;91 ConstCastOnly *error_union_error_set;
91 ConstCastOnly *return_type;92 ConstCastOnly *return_type;
92 ConstCastOnly *async_allocator_type;93 ConstCastOnly *async_allocator_type;
94 ConstCastOnly *null_wrap_ptr_child;
93 ConstCastArg fn_arg;95 ConstCastArg fn_arg;
94 ConstCastArgNoAlias arg_no_alias;96 ConstCastArgNoAlias arg_no_alias;
95 } data;97 } data;
...@@ -108,9 +110,10 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,...@@ -108,9 +110,10 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
108static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op);110static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op);
109static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval);111static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval);
110static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, uint32_t new_align);112static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, uint32_t new_align);
113static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type, uint32_t new_align);
111114
112ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {115ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
113 assert(const_val->type->id == TypeTableEntryIdPointer);116 assert(get_codegen_ptr_type(const_val->type) != nullptr);
114 assert(const_val->special == ConstValSpecialStatic);117 assert(const_val->special == ConstValSpecialStatic);
115 switch (const_val->data.x_ptr.special) {118 switch (const_val->data.x_ptr.special) {
116 case ConstPtrSpecialInvalid:119 case ConstPtrSpecialInvalid:
...@@ -369,8 +372,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) {...@@ -369,8 +372,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTestNonNull *) {
369 return IrInstructionIdTestNonNull;372 return IrInstructionIdTestNonNull;
370}373}
371374
372static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapMaybe *) {375static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapOptional *) {
373 return IrInstructionIdUnwrapMaybe;376 return IrInstructionIdUnwrapOptional;
374}377}
375378
376static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) {379static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) {
...@@ -521,8 +524,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrPayload...@@ -521,8 +524,8 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapErrPayload
521 return IrInstructionIdUnwrapErrPayload;524 return IrInstructionIdUnwrapErrPayload;
522}525}
523526
524static constexpr IrInstructionId ir_instruction_id(IrInstructionMaybeWrap *) {527static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalWrap *) {
525 return IrInstructionIdMaybeWrap;528 return IrInstructionIdOptionalWrap;
526}529}
527530
528static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapPayload *) {531static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapPayload *) {
...@@ -585,10 +588,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeName *) {...@@ -585,10 +588,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeName *) {
585 return IrInstructionIdTypeName;588 return IrInstructionIdTypeName;
586}589}
587590
588static constexpr IrInstructionId ir_instruction_id(IrInstructionCanImplicitCast *) {
589 return IrInstructionIdCanImplicitCast;
590}
591
592static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclRef *) {591static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclRef *) {
593 return IrInstructionIdDeclRef;592 return IrInstructionIdDeclRef;
594}593}
...@@ -1572,7 +1571,7 @@ static IrInstruction *ir_build_test_nonnull_from(IrBuilder *irb, IrInstruction *...@@ -1572,7 +1571,7 @@ static IrInstruction *ir_build_test_nonnull_from(IrBuilder *irb, IrInstruction *
1572static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value,1571static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value,
1573 bool safety_check_on)1572 bool safety_check_on)
1574{1573{
1575 IrInstructionUnwrapMaybe *instruction = ir_build_instruction<IrInstructionUnwrapMaybe>(irb, scope, source_node);1574 IrInstructionUnwrapOptional *instruction = ir_build_instruction<IrInstructionUnwrapOptional>(irb, scope, source_node);
1576 instruction->value = value;1575 instruction->value = value;
1577 instruction->safety_check_on = safety_check_on;1576 instruction->safety_check_on = safety_check_on;
15781577
...@@ -1591,7 +1590,7 @@ static IrInstruction *ir_build_unwrap_maybe_from(IrBuilder *irb, IrInstruction *...@@ -1591,7 +1590,7 @@ static IrInstruction *ir_build_unwrap_maybe_from(IrBuilder *irb, IrInstruction *
1591}1590}
15921591
1593static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {1592static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
1594 IrInstructionMaybeWrap *instruction = ir_build_instruction<IrInstructionMaybeWrap>(irb, scope, source_node);1593 IrInstructionOptionalWrap *instruction = ir_build_instruction<IrInstructionOptionalWrap>(irb, scope, source_node);
1595 instruction->value = value;1594 instruction->value = value;
15961595
1597 ir_ref_instruction(value, irb->current_basic_block);1596 ir_ref_instruction(value, irb->current_basic_block);
...@@ -2348,20 +2347,6 @@ static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode *...@@ -2348,20 +2347,6 @@ static IrInstruction *ir_build_type_name(IrBuilder *irb, Scope *scope, AstNode *
2348 return &instruction->base;2347 return &instruction->base;
2349}2348}
23502349
2351static IrInstruction *ir_build_can_implicit_cast(IrBuilder *irb, Scope *scope, AstNode *source_node,
2352 IrInstruction *type_value, IrInstruction *target_value)
2353{
2354 IrInstructionCanImplicitCast *instruction = ir_build_instruction<IrInstructionCanImplicitCast>(
2355 irb, scope, source_node);
2356 instruction->type_value = type_value;
2357 instruction->target_value = target_value;
2358
2359 ir_ref_instruction(type_value, irb->current_basic_block);
2360 ir_ref_instruction(target_value, irb->current_basic_block);
2361
2362 return &instruction->base;
2363}
2364
2365static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node,2350static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node,
2366 Tld *tld, LVal lval)2351 Tld *tld, LVal lval)
2367{2352{
...@@ -2511,9 +2496,9 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s...@@ -2511,9 +2496,9 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s
2511 return &instruction->base;2496 return &instruction->base;
2512}2497}
25132498
2514static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Nullable nullable) {2499static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Optional optional) {
2515 IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node);2500 IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node);
2516 instruction->nullable = nullable;2501 instruction->optional = optional;
25172502
2518 return &instruction->base;2503 return &instruction->base;
2519}2504}
...@@ -3310,9 +3295,9 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As...@@ -3310,9 +3295,9 @@ static IrInstruction *ir_gen_maybe_ok_or(IrBuilder *irb, Scope *parent_scope, As
3310 is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null);3295 is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null);
3311 }3296 }
33123297
3313 IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "MaybeNonNull");3298 IrBasicBlock *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull");
3314 IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "MaybeNull");3299 IrBasicBlock *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull");
3315 IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "MaybeEnd");3300 IrBasicBlock *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd");
3316 ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime);3301 ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime);
33173302
3318 ir_set_cursor_at_end_and_append_block(irb, null_block);3303 ir_set_cursor_at_end_and_append_block(irb, null_block);
...@@ -3441,7 +3426,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)...@@ -3441,7 +3426,7 @@ static IrInstruction *ir_gen_bin_op(IrBuilder *irb, Scope *scope, AstNode *node)
3441 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult);3426 return ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult);
3442 case BinOpTypeMergeErrorSets:3427 case BinOpTypeMergeErrorSets:
3443 return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets);3428 return ir_gen_bin_op_id(irb, scope, node, IrBinOpMergeErrorSets);
3444 case BinOpTypeUnwrapMaybe:3429 case BinOpTypeUnwrapOptional:
3445 return ir_gen_maybe_ok_or(irb, scope, node);3430 return ir_gen_maybe_ok_or(irb, scope, node);
3446 case BinOpTypeErrorUnion:3431 case BinOpTypeErrorUnion:
3447 return ir_gen_error_union(irb, scope, node);3432 return ir_gen_error_union(irb, scope, node);
...@@ -4132,21 +4117,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4132,21 +4117,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4132 IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value);4117 IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value);
4133 return ir_lval_wrap(irb, scope, type_name, lval);4118 return ir_lval_wrap(irb, scope, type_name, lval);
4134 }4119 }
4135 case BuiltinFnIdCanImplicitCast:
4136 {
4137 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4138 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4139 if (arg0_value == irb->codegen->invalid_instruction)
4140 return arg0_value;
4141
4142 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4143 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
4144 if (arg1_value == irb->codegen->invalid_instruction)
4145 return arg1_value;
4146
4147 IrInstruction *can_implicit_cast = ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value);
4148 return ir_lval_wrap(irb, scope, can_implicit_cast, lval);
4149 }
4150 case BuiltinFnIdPanic:4120 case BuiltinFnIdPanic:
4151 {4121 {
4152 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4122 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -4620,11 +4590,8 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *...@@ -4620,11 +4590,8 @@ static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *
46204590
4621static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) {4591static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) {
4622 assert(node->type == NodeTypePointerType);4592 assert(node->type == NodeTypePointerType);
4623 // The null check here is for C imports which don't set a token on the AST node. We could potentially4593 PtrLen ptr_len = (node->data.pointer_type.star_token->id == TokenIdStar ||
4624 // update that code to create a fake token and then remove this check.4594 node->data.pointer_type.star_token->id == TokenIdStarStar) ? PtrLenSingle : PtrLenUnknown;
4625 PtrLen ptr_len = (node->data.pointer_type.star_token != nullptr &&
4626 (node->data.pointer_type.star_token->id == TokenIdStar ||
4627 node->data.pointer_type.star_token->id == TokenIdStarStar)) ? PtrLenSingle : PtrLenUnknown;
4628 bool is_const = node->data.pointer_type.is_const;4595 bool is_const = node->data.pointer_type.is_const;
4629 bool is_volatile = node->data.pointer_type.is_volatile;4596 bool is_volatile = node->data.pointer_type.is_volatile;
4630 AstNode *expr_node = node->data.pointer_type.op_expr;4597 AstNode *expr_node = node->data.pointer_type.op_expr;
...@@ -4694,21 +4661,6 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode...@@ -4694,21 +4661,6 @@ static IrInstruction *ir_gen_err_assert_ok(IrBuilder *irb, Scope *scope, AstNode
4694 return ir_build_load_ptr(irb, scope, source_node, payload_ptr);4661 return ir_build_load_ptr(irb, scope, source_node, payload_ptr);
4695}4662}
46964663
4697static IrInstruction *ir_gen_maybe_assert_ok(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
4698 assert(node->type == NodeTypePrefixOpExpr);
4699 AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
4700
4701 IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR);
4702 if (maybe_ptr == irb->codegen->invalid_instruction)
4703 return irb->codegen->invalid_instruction;
4704
4705 IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true);
4706 if (lval.is_ptr)
4707 return unwrapped_ptr;
4708
4709 return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
4710}
4711
4712static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) {4664static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) {
4713 assert(node->type == NodeTypePrefixOpExpr);4665 assert(node->type == NodeTypePrefixOpExpr);
4714 AstNode *expr_node = node->data.prefix_op_expr.primary_expr;4666 AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
...@@ -4736,10 +4688,8 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod...@@ -4736,10 +4688,8 @@ static IrInstruction *ir_gen_prefix_op_expr(IrBuilder *irb, Scope *scope, AstNod
4736 return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval);4688 return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval);
4737 case PrefixOpNegationWrap:4689 case PrefixOpNegationWrap:
4738 return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval);4690 return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval);
4739 case PrefixOpMaybe:4691 case PrefixOpOptional:
4740 return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpMaybe), lval);4692 return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval);
4741 case PrefixOpUnwrapMaybe:
4742 return ir_gen_maybe_assert_ok(irb, scope, node, lval);
4743 case PrefixOpAddrOf: {4693 case PrefixOpAddrOf: {
4744 AstNode *expr_node = node->data.prefix_op_expr.primary_expr;4694 AstNode *expr_node = node->data.prefix_op_expr.primary_expr;
4745 return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR), lval);4695 return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR), lval);
...@@ -5403,9 +5353,9 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no...@@ -5403,9 +5353,9 @@ static IrInstruction *ir_gen_test_expr(IrBuilder *irb, Scope *scope, AstNode *no
5403 IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr);5353 IrInstruction *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr);
5404 IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val);5354 IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_val);
54055355
5406 IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "MaybeThen");5356 IrBasicBlock *then_block = ir_create_basic_block(irb, scope, "OptionalThen");
5407 IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "MaybeElse");5357 IrBasicBlock *else_block = ir_create_basic_block(irb, scope, "OptionalElse");
5408 IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "MaybeEndIf");5358 IrBasicBlock *endif_block = ir_create_basic_block(irb, scope, "OptionalEndIf");
54095359
5410 IrInstruction *is_comptime;5360 IrInstruction *is_comptime;
5411 if (ir_should_inline(irb->exec, scope)) {5361 if (ir_should_inline(irb->exec, scope)) {
...@@ -6574,7 +6524,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -6574,7 +6524,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
6574 return ir_build_load_ptr(irb, scope, node, ptr_instruction);6524 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
6575 }6525 }
6576 case NodeTypePtrDeref: {6526 case NodeTypePtrDeref: {
6577 assert(node->type == NodeTypePtrDeref);
6578 AstNode *expr_node = node->data.ptr_deref_expr.target;6527 AstNode *expr_node = node->data.ptr_deref_expr.target;
6579 IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval);6528 IrInstruction *value = ir_gen_node_extra(irb, expr_node, scope, lval);
6580 if (value == irb->codegen->invalid_instruction)6529 if (value == irb->codegen->invalid_instruction)
...@@ -6582,6 +6531,19 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -6582,6 +6531,19 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
65826531
6583 return ir_build_un_op(irb, scope, node, IrUnOpDereference, value);6532 return ir_build_un_op(irb, scope, node, IrUnOpDereference, value);
6584 }6533 }
6534 case NodeTypeUnwrapOptional: {
6535 AstNode *expr_node = node->data.unwrap_optional.expr;
6536
6537 IrInstruction *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LVAL_PTR);
6538 if (maybe_ptr == irb->codegen->invalid_instruction)
6539 return irb->codegen->invalid_instruction;
6540
6541 IrInstruction *unwrapped_ptr = ir_build_unwrap_maybe(irb, scope, node, maybe_ptr, true);
6542 if (lval.is_ptr)
6543 return unwrapped_ptr;
6544
6545 return ir_build_load_ptr(irb, scope, node, unwrapped_ptr);
6546 }
6585 case NodeTypeThisLiteral:6547 case NodeTypeThisLiteral:
6586 return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval);6548 return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval);
6587 case NodeTypeBoolLiteral:6549 case NodeTypeBoolLiteral:
...@@ -7552,7 +7514,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc...@@ -7552,7 +7514,7 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc
7552 }7514 }
7553 } else if (const_val_fits_in_num_lit(const_val, other_type)) {7515 } else if (const_val_fits_in_num_lit(const_val, other_type)) {
7554 return true;7516 return true;
7555 } else if (other_type->id == TypeTableEntryIdMaybe) {7517 } else if (other_type->id == TypeTableEntryIdOptional) {
7556 TypeTableEntry *child_type = other_type->data.maybe.child_type;7518 TypeTableEntry *child_type = other_type->data.maybe.child_type;
7557 if (const_val_fits_in_num_lit(const_val, child_type)) {7519 if (const_val_fits_in_num_lit(const_val, child_type)) {
7558 return true;7520 return true;
...@@ -7685,27 +7647,44 @@ static TypeTableEntry *get_error_set_intersection(IrAnalyze *ira, TypeTableEntry...@@ -7685,27 +7647,44 @@ static TypeTableEntry *get_error_set_intersection(IrAnalyze *ira, TypeTableEntry
7685}7647}
76867648
76877649
7688static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry *expected_type,7650static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry *wanted_type,
7689 TypeTableEntry *actual_type, AstNode *source_node)7651 TypeTableEntry *actual_type, AstNode *source_node, bool wanted_is_mutable)
7690{7652{
7691 CodeGen *g = ira->codegen;7653 CodeGen *g = ira->codegen;
7692 ConstCastOnly result = {};7654 ConstCastOnly result = {};
7693 result.id = ConstCastResultIdOk;7655 result.id = ConstCastResultIdOk;
76947656
7695 if (expected_type == actual_type)7657 if (wanted_type == actual_type)
7658 return result;
7659
7660 // * and [*] can do a const-cast-only to ?* and ?[*], respectively
7661 // but not if there is a mutable parent pointer
7662 if (!wanted_is_mutable && wanted_type->id == TypeTableEntryIdOptional &&
7663 wanted_type->data.maybe.child_type->id == TypeTableEntryIdPointer &&
7664 actual_type->id == TypeTableEntryIdPointer)
7665 {
7666 ConstCastOnly child = types_match_const_cast_only(ira,
7667 wanted_type->data.maybe.child_type, actual_type, source_node, wanted_is_mutable);
7668 if (child.id != ConstCastResultIdOk) {
7669 result.id = ConstCastResultIdNullWrapPtr;
7670 result.data.null_wrap_ptr_child = allocate_nonzero<ConstCastOnly>(1);
7671 *result.data.null_wrap_ptr_child = child;
7672 }
7696 return result;7673 return result;
7674 }
76977675
7698 // pointer const7676 // pointer const
7699 if (expected_type->id == TypeTableEntryIdPointer &&7677 if (wanted_type->id == TypeTableEntryIdPointer &&
7700 actual_type->id == TypeTableEntryIdPointer &&7678 actual_type->id == TypeTableEntryIdPointer &&
7701 (actual_type->data.pointer.ptr_len == expected_type->data.pointer.ptr_len) &&7679 (actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) &&
7702 (!actual_type->data.pointer.is_const || expected_type->data.pointer.is_const) &&7680 (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) &&
7703 (!actual_type->data.pointer.is_volatile || expected_type->data.pointer.is_volatile) &&7681 (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) &&
7704 actual_type->data.pointer.bit_offset == expected_type->data.pointer.bit_offset &&7682 actual_type->data.pointer.bit_offset == wanted_type->data.pointer.bit_offset &&
7705 actual_type->data.pointer.unaligned_bit_count == expected_type->data.pointer.unaligned_bit_count &&7683 actual_type->data.pointer.unaligned_bit_count == wanted_type->data.pointer.unaligned_bit_count &&
7706 actual_type->data.pointer.alignment >= expected_type->data.pointer.alignment)7684 actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment)
7707 {7685 {
7708 ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.pointer.child_type, actual_type->data.pointer.child_type, source_node);7686 ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
7687 actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const);
7709 if (child.id != ConstCastResultIdOk) {7688 if (child.id != ConstCastResultIdOk) {
7710 result.id = ConstCastResultIdPointerChild;7689 result.id = ConstCastResultIdPointerChild;
7711 result.data.pointer_child = allocate_nonzero<ConstCastOnly>(1);7690 result.data.pointer_child = allocate_nonzero<ConstCastOnly>(1);
...@@ -7715,17 +7694,17 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry...@@ -7715,17 +7694,17 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
7715 }7694 }
77167695
7717 // slice const7696 // slice const
7718 if (is_slice(expected_type) && is_slice(actual_type)) {7697 if (is_slice(wanted_type) && is_slice(actual_type)) {
7719 TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry;7698 TypeTableEntry *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index].type_entry;
7720 TypeTableEntry *expected_ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry;7699 TypeTableEntry *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
7721 if ((!actual_ptr_type->data.pointer.is_const || expected_ptr_type->data.pointer.is_const) &&7700 if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
7722 (!actual_ptr_type->data.pointer.is_volatile || expected_ptr_type->data.pointer.is_volatile) &&7701 (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
7723 actual_ptr_type->data.pointer.bit_offset == expected_ptr_type->data.pointer.bit_offset &&7702 actual_ptr_type->data.pointer.bit_offset == wanted_ptr_type->data.pointer.bit_offset &&
7724 actual_ptr_type->data.pointer.unaligned_bit_count == expected_ptr_type->data.pointer.unaligned_bit_count &&7703 actual_ptr_type->data.pointer.unaligned_bit_count == wanted_ptr_type->data.pointer.unaligned_bit_count &&
7725 actual_ptr_type->data.pointer.alignment >= expected_ptr_type->data.pointer.alignment)7704 actual_ptr_type->data.pointer.alignment >= wanted_ptr_type->data.pointer.alignment)
7726 {7705 {
7727 ConstCastOnly child = types_match_const_cast_only(ira, expected_ptr_type->data.pointer.child_type,7706 ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type,
7728 actual_ptr_type->data.pointer.child_type, source_node);7707 actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const);
7729 if (child.id != ConstCastResultIdOk) {7708 if (child.id != ConstCastResultIdOk) {
7730 result.id = ConstCastResultIdSliceChild;7709 result.id = ConstCastResultIdSliceChild;
7731 result.data.slice_child = allocate_nonzero<ConstCastOnly>(1);7710 result.data.slice_child = allocate_nonzero<ConstCastOnly>(1);
...@@ -7736,26 +7715,29 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry...@@ -7736,26 +7715,29 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
7736 }7715 }
77377716
7738 // maybe7717 // maybe
7739 if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) {7718 if (wanted_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdOptional) {
7740 ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.maybe.child_type, actual_type->data.maybe.child_type, source_node);7719 ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type,
7720 actual_type->data.maybe.child_type, source_node, wanted_is_mutable);
7741 if (child.id != ConstCastResultIdOk) {7721 if (child.id != ConstCastResultIdOk) {
7742 result.id = ConstCastResultIdNullableChild;7722 result.id = ConstCastResultIdOptionalChild;
7743 result.data.nullable_child = allocate_nonzero<ConstCastOnly>(1);7723 result.data.optional_child = allocate_nonzero<ConstCastOnly>(1);
7744 *result.data.nullable_child = child;7724 *result.data.optional_child = child;
7745 }7725 }
7746 return result;7726 return result;
7747 }7727 }
77487728
7749 // error union7729 // error union
7750 if (expected_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) {7730 if (wanted_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdErrorUnion) {
7751 ConstCastOnly payload_child = types_match_const_cast_only(ira, expected_type->data.error_union.payload_type, actual_type->data.error_union.payload_type, source_node);7731 ConstCastOnly payload_child = types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type,
7732 actual_type->data.error_union.payload_type, source_node, wanted_is_mutable);
7752 if (payload_child.id != ConstCastResultIdOk) {7733 if (payload_child.id != ConstCastResultIdOk) {
7753 result.id = ConstCastResultIdErrorUnionPayload;7734 result.id = ConstCastResultIdErrorUnionPayload;
7754 result.data.error_union_payload = allocate_nonzero<ConstCastOnly>(1);7735 result.data.error_union_payload = allocate_nonzero<ConstCastOnly>(1);
7755 *result.data.error_union_payload = payload_child;7736 *result.data.error_union_payload = payload_child;
7756 return result;7737 return result;
7757 }7738 }
7758 ConstCastOnly error_set_child = types_match_const_cast_only(ira, expected_type->data.error_union.err_set_type, actual_type->data.error_union.err_set_type, source_node);7739 ConstCastOnly error_set_child = types_match_const_cast_only(ira, wanted_type->data.error_union.err_set_type,
7740 actual_type->data.error_union.err_set_type, source_node, wanted_is_mutable);
7759 if (error_set_child.id != ConstCastResultIdOk) {7741 if (error_set_child.id != ConstCastResultIdOk) {
7760 result.id = ConstCastResultIdErrorUnionErrorSet;7742 result.id = ConstCastResultIdErrorUnionErrorSet;
7761 result.data.error_union_error_set = allocate_nonzero<ConstCastOnly>(1);7743 result.data.error_union_error_set = allocate_nonzero<ConstCastOnly>(1);
...@@ -7766,9 +7748,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry...@@ -7766,9 +7748,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
7766 }7748 }
77677749
7768 // error set7750 // error set
7769 if (expected_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdErrorSet) {7751 if (wanted_type->id == TypeTableEntryIdErrorSet && actual_type->id == TypeTableEntryIdErrorSet) {
7770 TypeTableEntry *contained_set = actual_type;7752 TypeTableEntry *contained_set = actual_type;
7771 TypeTableEntry *container_set = expected_type;7753 TypeTableEntry *container_set = wanted_type;
77727754
7773 // if the container set is inferred, then this will always work.7755 // if the container set is inferred, then this will always work.
7774 if (container_set->data.error_set.infer_fn != nullptr) {7756 if (container_set->data.error_set.infer_fn != nullptr) {
...@@ -7809,36 +7791,37 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry...@@ -7809,36 +7791,37 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
7809 return result;7791 return result;
7810 }7792 }
78117793
7812 if (expected_type == ira->codegen->builtin_types.entry_promise &&7794 if (wanted_type == ira->codegen->builtin_types.entry_promise &&
7813 actual_type->id == TypeTableEntryIdPromise)7795 actual_type->id == TypeTableEntryIdPromise)
7814 {7796 {
7815 return result;7797 return result;
7816 }7798 }
78177799
7818 // fn7800 // fn
7819 if (expected_type->id == TypeTableEntryIdFn &&7801 if (wanted_type->id == TypeTableEntryIdFn &&
7820 actual_type->id == TypeTableEntryIdFn)7802 actual_type->id == TypeTableEntryIdFn)
7821 {7803 {
7822 if (expected_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) {7804 if (wanted_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) {
7823 result.id = ConstCastResultIdFnAlign;7805 result.id = ConstCastResultIdFnAlign;
7824 return result;7806 return result;
7825 }7807 }
7826 if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {7808 if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
7827 result.id = ConstCastResultIdFnCC;7809 result.id = ConstCastResultIdFnCC;
7828 return result;7810 return result;
7829 }7811 }
7830 if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {7812 if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {
7831 result.id = ConstCastResultIdFnVarArgs;7813 result.id = ConstCastResultIdFnVarArgs;
7832 return result;7814 return result;
7833 }7815 }
7834 if (expected_type->data.fn.is_generic != actual_type->data.fn.is_generic) {7816 if (wanted_type->data.fn.is_generic != actual_type->data.fn.is_generic) {
7835 result.id = ConstCastResultIdFnIsGeneric;7817 result.id = ConstCastResultIdFnIsGeneric;
7836 return result;7818 return result;
7837 }7819 }
7838 if (!expected_type->data.fn.is_generic &&7820 if (!wanted_type->data.fn.is_generic &&
7839 actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable)7821 actual_type->data.fn.fn_type_id.return_type->id != TypeTableEntryIdUnreachable)
7840 {7822 {
7841 ConstCastOnly child = types_match_const_cast_only(ira, expected_type->data.fn.fn_type_id.return_type, actual_type->data.fn.fn_type_id.return_type, source_node);7823 ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.fn.fn_type_id.return_type,
7824 actual_type->data.fn.fn_type_id.return_type, source_node, false);
7842 if (child.id != ConstCastResultIdOk) {7825 if (child.id != ConstCastResultIdOk) {
7843 result.id = ConstCastResultIdFnReturnType;7826 result.id = ConstCastResultIdFnReturnType;
7844 result.data.return_type = allocate_nonzero<ConstCastOnly>(1);7827 result.data.return_type = allocate_nonzero<ConstCastOnly>(1);
...@@ -7846,9 +7829,11 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry...@@ -7846,9 +7829,11 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
7846 return result;7829 return result;
7847 }7830 }
7848 }7831 }
7849 if (!expected_type->data.fn.is_generic && expected_type->data.fn.fn_type_id.cc == CallingConventionAsync) {7832 if (!wanted_type->data.fn.is_generic && wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync) {
7850 ConstCastOnly child = types_match_const_cast_only(ira, actual_type->data.fn.fn_type_id.async_allocator_type,7833 ConstCastOnly child = types_match_const_cast_only(ira,
7851 expected_type->data.fn.fn_type_id.async_allocator_type, source_node);7834 actual_type->data.fn.fn_type_id.async_allocator_type,
7835 wanted_type->data.fn.fn_type_id.async_allocator_type,
7836 source_node, false);
7852 if (child.id != ConstCastResultIdOk) {7837 if (child.id != ConstCastResultIdOk) {
7853 result.id = ConstCastResultIdAsyncAllocatorType;7838 result.id = ConstCastResultIdAsyncAllocatorType;
7854 result.data.async_allocator_type = allocate_nonzero<ConstCastOnly>(1);7839 result.data.async_allocator_type = allocate_nonzero<ConstCastOnly>(1);
...@@ -7856,22 +7841,23 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry...@@ -7856,22 +7841,23 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
7856 return result;7841 return result;
7857 }7842 }
7858 }7843 }
7859 if (expected_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {7844 if (wanted_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) {
7860 result.id = ConstCastResultIdFnArgCount;7845 result.id = ConstCastResultIdFnArgCount;
7861 return result;7846 return result;
7862 }7847 }
7863 if (expected_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) {7848 if (wanted_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) {
7864 result.id = ConstCastResultIdFnGenericArgCount;7849 result.id = ConstCastResultIdFnGenericArgCount;
7865 return result;7850 return result;
7866 }7851 }
7867 assert(expected_type->data.fn.is_generic ||7852 assert(wanted_type->data.fn.is_generic ||
7868 expected_type->data.fn.fn_type_id.next_param_index == expected_type->data.fn.fn_type_id.param_count);7853 wanted_type->data.fn.fn_type_id.next_param_index == wanted_type->data.fn.fn_type_id.param_count);
7869 for (size_t i = 0; i < expected_type->data.fn.fn_type_id.next_param_index; i += 1) {7854 for (size_t i = 0; i < wanted_type->data.fn.fn_type_id.next_param_index; i += 1) {
7870 // note it's reversed for parameters7855 // note it's reversed for parameters
7871 FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i];7856 FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i];
7872 FnTypeParamInfo *expected_param_info = &expected_type->data.fn.fn_type_id.param_info[i];7857 FnTypeParamInfo *expected_param_info = &wanted_type->data.fn.fn_type_id.param_info[i];
78737858
7874 ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type, expected_param_info->type, source_node);7859 ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type,
7860 expected_param_info->type, source_node, false);
7875 if (arg_child.id != ConstCastResultIdOk) {7861 if (arg_child.id != ConstCastResultIdOk) {
7876 result.id = ConstCastResultIdFnArg;7862 result.id = ConstCastResultIdFnArg;
7877 result.data.fn_arg.arg_index = i;7863 result.data.fn_arg.arg_index = i;
...@@ -7899,11 +7885,12 @@ enum ImplicitCastMatchResult {...@@ -7899,11 +7885,12 @@ enum ImplicitCastMatchResult {
7899 ImplicitCastMatchResultReportedError,7885 ImplicitCastMatchResultReportedError,
7900};7886};
79017887
7902static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type,7888static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *wanted_type,
7903 TypeTableEntry *actual_type, IrInstruction *value)7889 TypeTableEntry *actual_type, IrInstruction *value)
7904{7890{
7905 AstNode *source_node = value->source_node;7891 AstNode *source_node = value->source_node;
7906 ConstCastOnly const_cast_result = types_match_const_cast_only(ira, expected_type, actual_type, source_node);7892 ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type,
7893 source_node, false);
7907 if (const_cast_result.id == ConstCastResultIdOk) {7894 if (const_cast_result.id == ConstCastResultIdOk) {
7908 return ImplicitCastMatchResultYes;7895 return ImplicitCastMatchResultYes;
7909 }7896 }
...@@ -7918,21 +7905,21 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -7918,21 +7905,21 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
7918 missing_errors = &const_cast_result.data.error_union_error_set->data.error_set.missing_errors;7905 missing_errors = &const_cast_result.data.error_union_error_set->data.error_set.missing_errors;
7919 } else if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSetGlobal) {7906 } else if (const_cast_result.data.error_union_error_set->id == ConstCastResultIdErrSetGlobal) {
7920 ErrorMsg *msg = ir_add_error(ira, value,7907 ErrorMsg *msg = ir_add_error(ira, value,
7921 buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));7908 buf_sprintf("expected '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name)));
7922 add_error_note(ira->codegen, msg, value->source_node,7909 add_error_note(ira->codegen, msg, value->source_node,
7923 buf_sprintf("unable to cast global error set into smaller set"));7910 buf_sprintf("unable to cast global error set into smaller set"));
7924 return ImplicitCastMatchResultReportedError;7911 return ImplicitCastMatchResultReportedError;
7925 }7912 }
7926 } else if (const_cast_result.id == ConstCastResultIdErrSetGlobal) {7913 } else if (const_cast_result.id == ConstCastResultIdErrSetGlobal) {
7927 ErrorMsg *msg = ir_add_error(ira, value,7914 ErrorMsg *msg = ir_add_error(ira, value,
7928 buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));7915 buf_sprintf("expected '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name)));
7929 add_error_note(ira->codegen, msg, value->source_node,7916 add_error_note(ira->codegen, msg, value->source_node,
7930 buf_sprintf("unable to cast global error set into smaller set"));7917 buf_sprintf("unable to cast global error set into smaller set"));
7931 return ImplicitCastMatchResultReportedError;7918 return ImplicitCastMatchResultReportedError;
7932 }7919 }
7933 if (missing_errors != nullptr) {7920 if (missing_errors != nullptr) {
7934 ErrorMsg *msg = ir_add_error(ira, value,7921 ErrorMsg *msg = ir_add_error(ira, value,
7935 buf_sprintf("expected '%s', found '%s'", buf_ptr(&expected_type->name), buf_ptr(&actual_type->name)));7922 buf_sprintf("expected '%s', found '%s'", buf_ptr(&wanted_type->name), buf_ptr(&actual_type->name)));
7936 for (size_t i = 0; i < missing_errors->length; i += 1) {7923 for (size_t i = 0; i < missing_errors->length; i += 1) {
7937 ErrorTableEntry *error_entry = missing_errors->at(i);7924 ErrorTableEntry *error_entry = missing_errors->at(i);
7938 add_error_note(ira->codegen, msg, error_entry->decl_node,7925 add_error_note(ira->codegen, msg, error_entry->decl_node,
...@@ -7943,133 +7930,168 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -7943,133 +7930,168 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
7943 }7930 }
79447931
7945 // implicit conversion from ?T to ?U7932 // implicit conversion from ?T to ?U
7946 if (expected_type->id == TypeTableEntryIdMaybe && actual_type->id == TypeTableEntryIdMaybe) {7933 if (wanted_type->id == TypeTableEntryIdOptional && actual_type->id == TypeTableEntryIdOptional) {
7947 ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type,7934 ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, wanted_type->data.maybe.child_type,
7948 actual_type->data.maybe.child_type, value);7935 actual_type->data.maybe.child_type, value);
7949 if (res != ImplicitCastMatchResultNo)7936 if (res != ImplicitCastMatchResultNo)
7950 return res;7937 return res;
7951 }7938 }
79527939
7953 // implicit conversion from non maybe type to maybe type7940 // implicit conversion from non maybe type to maybe type
7954 if (expected_type->id == TypeTableEntryIdMaybe) {7941 if (wanted_type->id == TypeTableEntryIdOptional) {
7955 ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type,7942 ImplicitCastMatchResult res = ir_types_match_with_implicit_cast(ira, wanted_type->data.maybe.child_type,
7956 actual_type, value);7943 actual_type, value);
7957 if (res != ImplicitCastMatchResultNo)7944 if (res != ImplicitCastMatchResultNo)
7958 return res;7945 return res;
7959 }7946 }
79607947
7961 // implicit conversion from null literal to maybe type7948 // implicit conversion from null literal to maybe type
7962 if (expected_type->id == TypeTableEntryIdMaybe &&7949 if (wanted_type->id == TypeTableEntryIdOptional &&
7963 actual_type->id == TypeTableEntryIdNull)7950 actual_type->id == TypeTableEntryIdNull)
7964 {7951 {
7965 return ImplicitCastMatchResultYes;7952 return ImplicitCastMatchResultYes;
7966 }7953 }
79677954
7968 // implicit T to U!T7955 // implicit T to U!T
7969 if (expected_type->id == TypeTableEntryIdErrorUnion &&7956 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
7970 ir_types_match_with_implicit_cast(ira, expected_type->data.error_union.payload_type, actual_type, value))7957 ir_types_match_with_implicit_cast(ira, wanted_type->data.error_union.payload_type, actual_type, value))
7971 {7958 {
7972 return ImplicitCastMatchResultYes;7959 return ImplicitCastMatchResultYes;
7973 }7960 }
79747961
7975 // implicit conversion from error set to error union type7962 // implicit conversion from error set to error union type
7976 if (expected_type->id == TypeTableEntryIdErrorUnion &&7963 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
7977 actual_type->id == TypeTableEntryIdErrorSet)7964 actual_type->id == TypeTableEntryIdErrorSet)
7978 {7965 {
7979 return ImplicitCastMatchResultYes;7966 return ImplicitCastMatchResultYes;
7980 }7967 }
79817968
7982 // implicit conversion from T to U!?T7969 // implicit conversion from T to U!?T
7983 if (expected_type->id == TypeTableEntryIdErrorUnion &&7970 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
7984 expected_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe &&7971 wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional &&
7985 ir_types_match_with_implicit_cast(ira,7972 ir_types_match_with_implicit_cast(ira,
7986 expected_type->data.error_union.payload_type->data.maybe.child_type,7973 wanted_type->data.error_union.payload_type->data.maybe.child_type,
7987 actual_type, value))7974 actual_type, value))
7988 {7975 {
7989 return ImplicitCastMatchResultYes;7976 return ImplicitCastMatchResultYes;
7990 }7977 }
79917978
7992 // implicit widening conversion7979 // implicit widening conversion
7993 if (expected_type->id == TypeTableEntryIdInt &&7980 if (wanted_type->id == TypeTableEntryIdInt &&
7994 actual_type->id == TypeTableEntryIdInt &&7981 actual_type->id == TypeTableEntryIdInt &&
7995 expected_type->data.integral.is_signed == actual_type->data.integral.is_signed &&7982 wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed &&
7996 expected_type->data.integral.bit_count >= actual_type->data.integral.bit_count)7983 wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count)
7997 {7984 {
7998 return ImplicitCastMatchResultYes;7985 return ImplicitCastMatchResultYes;
7999 }7986 }
80007987
8001 // small enough unsigned ints can get casted to large enough signed ints7988 // small enough unsigned ints can get casted to large enough signed ints
8002 if (expected_type->id == TypeTableEntryIdInt && expected_type->data.integral.is_signed &&7989 if (wanted_type->id == TypeTableEntryIdInt && wanted_type->data.integral.is_signed &&
8003 actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed &&7990 actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed &&
8004 expected_type->data.integral.bit_count > actual_type->data.integral.bit_count)7991 wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count)
8005 {7992 {
8006 return ImplicitCastMatchResultYes;7993 return ImplicitCastMatchResultYes;
8007 }7994 }
80087995
8009 // implicit float widening conversion7996 // implicit float widening conversion
8010 if (expected_type->id == TypeTableEntryIdFloat &&7997 if (wanted_type->id == TypeTableEntryIdFloat &&
8011 actual_type->id == TypeTableEntryIdFloat &&7998 actual_type->id == TypeTableEntryIdFloat &&
8012 expected_type->data.floating.bit_count >= actual_type->data.floating.bit_count)7999 wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count)
8013 {8000 {
8014 return ImplicitCastMatchResultYes;8001 return ImplicitCastMatchResultYes;
8015 }8002 }
80168003
8017 // implicit [N]T to []const T8004 // implicit [N]T to []const T
8018 if (is_slice(expected_type) && actual_type->id == TypeTableEntryIdArray) {8005 if (is_slice(wanted_type) && actual_type->id == TypeTableEntryIdArray) {
8019 TypeTableEntry *ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry;8006 TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
8020 assert(ptr_type->id == TypeTableEntryIdPointer);8007 assert(ptr_type->id == TypeTableEntryIdPointer);
80218008
8022 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&8009 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
8023 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)8010 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
8011 source_node, false).id == ConstCastResultIdOk)
8024 {8012 {
8025 return ImplicitCastMatchResultYes;8013 return ImplicitCastMatchResultYes;
8026 }8014 }
8027 }8015 }
80288016
8029 // implicit &const [N]T to []const T8017 // implicit &const [N]T to []const T
8030 if (is_slice(expected_type) &&8018 if (is_slice(wanted_type) &&
8031 actual_type->id == TypeTableEntryIdPointer &&8019 actual_type->id == TypeTableEntryIdPointer &&
8020 actual_type->data.pointer.ptr_len == PtrLenSingle &&
8032 actual_type->data.pointer.is_const &&8021 actual_type->data.pointer.is_const &&
8033 actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)8022 actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
8034 {8023 {
8035 TypeTableEntry *ptr_type = expected_type->data.structure.fields[slice_ptr_index].type_entry;8024 TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
8036 assert(ptr_type->id == TypeTableEntryIdPointer);8025 assert(ptr_type->id == TypeTableEntryIdPointer);
80378026
8038 TypeTableEntry *array_type = actual_type->data.pointer.child_type;8027 TypeTableEntry *array_type = actual_type->data.pointer.child_type;
80398028
8040 if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&8029 if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&
8041 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk)8030 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type,
8031 source_node, false).id == ConstCastResultIdOk)
8042 {8032 {
8043 return ImplicitCastMatchResultYes;8033 return ImplicitCastMatchResultYes;
8044 }8034 }
8045 }8035 }
80468036
8047 // implicit [N]T to &const []const T8037 // implicit [N]T to &const []const T
8048 if (expected_type->id == TypeTableEntryIdPointer &&8038 if (wanted_type->id == TypeTableEntryIdPointer &&
8049 expected_type->data.pointer.is_const &&8039 wanted_type->data.pointer.is_const &&
8050 is_slice(expected_type->data.pointer.child_type) &&8040 wanted_type->data.pointer.ptr_len == PtrLenSingle &&
8041 is_slice(wanted_type->data.pointer.child_type) &&
8051 actual_type->id == TypeTableEntryIdArray)8042 actual_type->id == TypeTableEntryIdArray)
8052 {8043 {
8053 TypeTableEntry *ptr_type =8044 TypeTableEntry *ptr_type =
8054 expected_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;8045 wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;
8055 assert(ptr_type->id == TypeTableEntryIdPointer);8046 assert(ptr_type->id == TypeTableEntryIdPointer);
8056 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&8047 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
8057 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)8048 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type,
8049 actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk)
8050 {
8051 return ImplicitCastMatchResultYes;
8052 }
8053 }
8054
8055 // implicit *[N]T to [*]T
8056 if (wanted_type->id == TypeTableEntryIdPointer &&
8057 wanted_type->data.pointer.ptr_len == PtrLenUnknown &&
8058 actual_type->id == TypeTableEntryIdPointer &&
8059 actual_type->data.pointer.ptr_len == PtrLenSingle &&
8060 actual_type->data.pointer.child_type->id == TypeTableEntryIdArray &&
8061 types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
8062 actual_type->data.pointer.child_type->data.array.child_type, source_node,
8063 !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
8064 {
8065 return ImplicitCastMatchResultYes;
8066 }
8067
8068 // implicit *[N]T to []T
8069 if (is_slice(wanted_type) &&
8070 actual_type->id == TypeTableEntryIdPointer &&
8071 actual_type->data.pointer.ptr_len == PtrLenSingle &&
8072 actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
8073 {
8074 TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
8075 assert(slice_ptr_type->id == TypeTableEntryIdPointer);
8076 if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
8077 actual_type->data.pointer.child_type->data.array.child_type, source_node,
8078 !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
8058 {8079 {
8059 return ImplicitCastMatchResultYes;8080 return ImplicitCastMatchResultYes;
8060 }8081 }
8061 }8082 }
80628083
8063 // implicit [N]T to ?[]const T8084 // implicit [N]T to ?[]const T
8064 if (expected_type->id == TypeTableEntryIdMaybe &&8085 if (wanted_type->id == TypeTableEntryIdOptional &&
8065 is_slice(expected_type->data.maybe.child_type) &&8086 is_slice(wanted_type->data.maybe.child_type) &&
8066 actual_type->id == TypeTableEntryIdArray)8087 actual_type->id == TypeTableEntryIdArray)
8067 {8088 {
8068 TypeTableEntry *ptr_type =8089 TypeTableEntry *ptr_type =
8069 expected_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;8090 wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
8070 assert(ptr_type->id == TypeTableEntryIdPointer);8091 assert(ptr_type->id == TypeTableEntryIdPointer);
8071 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&8092 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
8072 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)8093 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type,
8094 actual_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk)
8073 {8095 {
8074 return ImplicitCastMatchResultYes;8096 return ImplicitCastMatchResultYes;
8075 }8097 }
...@@ -8081,15 +8103,16 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -8081,15 +8103,16 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
8081 if (actual_type->id == TypeTableEntryIdComptimeFloat ||8103 if (actual_type->id == TypeTableEntryIdComptimeFloat ||
8082 actual_type->id == TypeTableEntryIdComptimeInt)8104 actual_type->id == TypeTableEntryIdComptimeInt)
8083 {8105 {
8084 if (expected_type->id == TypeTableEntryIdPointer &&8106 if (wanted_type->id == TypeTableEntryIdPointer &&
8085 expected_type->data.pointer.is_const)8107 wanted_type->data.pointer.ptr_len == PtrLenSingle &&
8108 wanted_type->data.pointer.is_const)
8086 {8109 {
8087 if (ir_num_lit_fits_in_other_type(ira, value, expected_type->data.pointer.child_type, false)) {8110 if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.pointer.child_type, false)) {
8088 return ImplicitCastMatchResultYes;8111 return ImplicitCastMatchResultYes;
8089 } else {8112 } else {
8090 return ImplicitCastMatchResultReportedError;8113 return ImplicitCastMatchResultReportedError;
8091 }8114 }
8092 } else if (ir_num_lit_fits_in_other_type(ira, value, expected_type, false)) {8115 } else if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, false)) {
8093 return ImplicitCastMatchResultYes;8116 return ImplicitCastMatchResultYes;
8094 } else {8117 } else {
8095 return ImplicitCastMatchResultReportedError;8118 return ImplicitCastMatchResultReportedError;
...@@ -8099,38 +8122,41 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -8099,38 +8122,41 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
8099 // implicit typed number to integer or float literal.8122 // implicit typed number to integer or float literal.
8100 // works when the number is known8123 // works when the number is known
8101 if (value->value.special == ConstValSpecialStatic) {8124 if (value->value.special == ConstValSpecialStatic) {
8102 if (actual_type->id == TypeTableEntryIdInt && expected_type->id == TypeTableEntryIdComptimeInt) {8125 if (actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdComptimeInt) {
8103 return ImplicitCastMatchResultYes;8126 return ImplicitCastMatchResultYes;
8104 } else if (actual_type->id == TypeTableEntryIdFloat && expected_type->id == TypeTableEntryIdComptimeFloat) {8127 } else if (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdComptimeFloat) {
8105 return ImplicitCastMatchResultYes;8128 return ImplicitCastMatchResultYes;
8106 }8129 }
8107 }8130 }
81088131
8109 // implicit union to its enum tag type8132 // implicit union to its enum tag type
8110 if (expected_type->id == TypeTableEntryIdEnum && actual_type->id == TypeTableEntryIdUnion &&8133 if (wanted_type->id == TypeTableEntryIdEnum && actual_type->id == TypeTableEntryIdUnion &&
8111 (actual_type->data.unionation.decl_node->data.container_decl.auto_enum ||8134 (actual_type->data.unionation.decl_node->data.container_decl.auto_enum ||
8112 actual_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))8135 actual_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
8113 {8136 {
8114 type_ensure_zero_bits_known(ira->codegen, actual_type);8137 type_ensure_zero_bits_known(ira->codegen, actual_type);
8115 if (actual_type->data.unionation.tag_type == expected_type) {8138 if (actual_type->data.unionation.tag_type == wanted_type) {
8116 return ImplicitCastMatchResultYes;8139 return ImplicitCastMatchResultYes;
8117 }8140 }
8118 }8141 }
81198142
8120 // implicit enum to union which has the enum as the tag type8143 // implicit enum to union which has the enum as the tag type
8121 if (expected_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&8144 if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
8122 (expected_type->data.unionation.decl_node->data.container_decl.auto_enum ||8145 (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
8123 expected_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))8146 wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
8124 {8147 {
8125 type_ensure_zero_bits_known(ira->codegen, expected_type);8148 type_ensure_zero_bits_known(ira->codegen, wanted_type);
8126 if (expected_type->data.unionation.tag_type == actual_type) {8149 if (wanted_type->data.unionation.tag_type == actual_type) {
8127 return ImplicitCastMatchResultYes;8150 return ImplicitCastMatchResultYes;
8128 }8151 }
8129 }8152 }
81308153
8131 // implicit enum to &const union which has the enum as the tag type8154 // implicit enum to &const union which has the enum as the tag type
8132 if (actual_type->id == TypeTableEntryIdEnum && expected_type->id == TypeTableEntryIdPointer) {8155 if (actual_type->id == TypeTableEntryIdEnum &&
8133 TypeTableEntry *union_type = expected_type->data.pointer.child_type;8156 wanted_type->id == TypeTableEntryIdPointer &&
8157 wanted_type->data.pointer.ptr_len == PtrLenSingle)
8158 {
8159 TypeTableEntry *union_type = wanted_type->data.pointer.child_type;
8134 if (union_type->data.unionation.decl_node->data.container_decl.auto_enum ||8160 if (union_type->data.unionation.decl_node->data.container_decl.auto_enum ||
8135 union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)8161 union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)
8136 {8162 {
...@@ -8141,6 +8167,17 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -8141,6 +8167,17 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
8141 }8167 }
8142 }8168 }
81438169
8170 // implicit T to *T where T is zero bits
8171 if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
8172 types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
8173 actual_type, source_node, false).id == ConstCastResultIdOk)
8174 {
8175 type_ensure_zero_bits_known(ira->codegen, actual_type);
8176 if (!type_has_bits(actual_type)) {
8177 return ImplicitCastMatchResultYes;
8178 }
8179 }
8180
8144 // implicit undefined literal to anything8181 // implicit undefined literal to anything
8145 if (actual_type->id == TypeTableEntryIdUndefined) {8182 if (actual_type->id == TypeTableEntryIdUndefined) {
8146 return ImplicitCastMatchResultYes;8183 return ImplicitCastMatchResultYes;
...@@ -8149,7 +8186,11 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,...@@ -8149,7 +8186,11 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
8149 // implicitly take a const pointer to something8186 // implicitly take a const pointer to something
8150 if (!type_requires_comptime(actual_type)) {8187 if (!type_requires_comptime(actual_type)) {
8151 TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);8188 TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
8152 if (types_match_const_cast_only(ira, expected_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) {8189 if (wanted_type->id == TypeTableEntryIdPointer &&
8190 wanted_type->data.pointer.ptr_len == PtrLenSingle &&
8191 types_match_const_cast_only(ira, wanted_type, const_ptr_actual,
8192 source_node, false).id == ConstCastResultIdOk)
8193 {
8153 return ImplicitCastMatchResultYes;8194 return ImplicitCastMatchResultYes;
8154 }8195 }
8155 }8196 }
...@@ -8390,9 +8431,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8390,9 +8431,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8390 TypeTableEntry *cur_payload_type = cur_type->data.error_union.payload_type;8431 TypeTableEntry *cur_payload_type = cur_type->data.error_union.payload_type;
83918432
8392 bool const_cast_prev = types_match_const_cast_only(ira, prev_payload_type, cur_payload_type,8433 bool const_cast_prev = types_match_const_cast_only(ira, prev_payload_type, cur_payload_type,
8393 source_node).id == ConstCastResultIdOk;8434 source_node, false).id == ConstCastResultIdOk;
8394 bool const_cast_cur = types_match_const_cast_only(ira, cur_payload_type, prev_payload_type,8435 bool const_cast_cur = types_match_const_cast_only(ira, cur_payload_type, prev_payload_type,
8395 source_node).id == ConstCastResultIdOk;8436 source_node, false).id == ConstCastResultIdOk;
83968437
8397 if (const_cast_prev || const_cast_cur) {8438 if (const_cast_prev || const_cast_cur) {
8398 if (const_cast_cur) {8439 if (const_cast_cur) {
...@@ -8479,11 +8520,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8479,11 +8520,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8479 continue;8520 continue;
8480 }8521 }
84818522
8482 if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) {8523 if (types_match_const_cast_only(ira, prev_type, cur_type, source_node, false).id == ConstCastResultIdOk) {
8483 continue;8524 continue;
8484 }8525 }
84858526
8486 if (types_match_const_cast_only(ira, cur_type, prev_type, source_node).id == ConstCastResultIdOk) {8527 if (types_match_const_cast_only(ira, cur_type, prev_type, source_node, false).id == ConstCastResultIdOk) {
8487 prev_inst = cur_inst;8528 prev_inst = cur_inst;
8488 continue;8529 continue;
8489 }8530 }
...@@ -8506,13 +8547,15 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8506,13 +8547,15 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8506 }8547 }
85078548
8508 if (prev_type->id == TypeTableEntryIdErrorUnion &&8549 if (prev_type->id == TypeTableEntryIdErrorUnion &&
8509 types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type, source_node).id == ConstCastResultIdOk)8550 types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type,
8551 source_node, false).id == ConstCastResultIdOk)
8510 {8552 {
8511 continue;8553 continue;
8512 }8554 }
85138555
8514 if (cur_type->id == TypeTableEntryIdErrorUnion &&8556 if (cur_type->id == TypeTableEntryIdErrorUnion &&
8515 types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, source_node).id == ConstCastResultIdOk)8557 types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type,
8558 source_node, false).id == ConstCastResultIdOk)
8516 {8559 {
8517 if (err_set_type != nullptr) {8560 if (err_set_type != nullptr) {
8518 TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type;8561 TypeTableEntry *cur_err_set_type = cur_type->data.error_union.err_set_type;
...@@ -8533,14 +8576,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8533,14 +8576,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8533 continue;8576 continue;
8534 }8577 }
85358578
8536 if (prev_type->id == TypeTableEntryIdMaybe &&8579 if (prev_type->id == TypeTableEntryIdOptional &&
8537 types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, source_node).id == ConstCastResultIdOk)8580 types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type,
8581 source_node, false).id == ConstCastResultIdOk)
8538 {8582 {
8539 continue;8583 continue;
8540 }8584 }
85418585
8542 if (cur_type->id == TypeTableEntryIdMaybe &&8586 if (cur_type->id == TypeTableEntryIdOptional &&
8543 types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, source_node).id == ConstCastResultIdOk)8587 types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type,
8588 source_node, false).id == ConstCastResultIdOk)
8544 {8589 {
8545 prev_inst = cur_inst;8590 prev_inst = cur_inst;
8546 continue;8591 continue;
...@@ -8577,8 +8622,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8577,8 +8622,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8577 }8622 }
85788623
8579 if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&8624 if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&
8580 cur_type->data.array.len != prev_type->data.array.len &&8625 cur_type->data.array.len != prev_type->data.array.len &&
8581 types_match_const_cast_only(ira, cur_type->data.array.child_type, prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk)8626 types_match_const_cast_only(ira, cur_type->data.array.child_type, prev_type->data.array.child_type,
8627 source_node, false).id == ConstCastResultIdOk)
8582 {8628 {
8583 convert_to_const_slice = true;8629 convert_to_const_slice = true;
8584 prev_inst = cur_inst;8630 prev_inst = cur_inst;
...@@ -8586,8 +8632,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8586,8 +8632,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8586 }8632 }
85878633
8588 if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&8634 if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&
8589 cur_type->data.array.len != prev_type->data.array.len &&8635 cur_type->data.array.len != prev_type->data.array.len &&
8590 types_match_const_cast_only(ira, prev_type->data.array.child_type, cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk)8636 types_match_const_cast_only(ira, prev_type->data.array.child_type, cur_type->data.array.child_type,
8637 source_node, false).id == ConstCastResultIdOk)
8591 {8638 {
8592 convert_to_const_slice = true;8639 convert_to_const_slice = true;
8593 continue;8640 continue;
...@@ -8596,8 +8643,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8596,8 +8643,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8596 if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) &&8643 if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) &&
8597 (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||8644 (prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
8598 cur_type->data.array.len == 0) &&8645 cur_type->data.array.len == 0) &&
8599 types_match_const_cast_only(ira, prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,8646 types_match_const_cast_only(ira,
8600 cur_type->data.array.child_type, source_node).id == ConstCastResultIdOk)8647 prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
8648 cur_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk)
8601 {8649 {
8602 convert_to_const_slice = false;8650 convert_to_const_slice = false;
8603 continue;8651 continue;
...@@ -8606,8 +8654,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8606,8 +8654,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8606 if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) &&8654 if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) &&
8607 (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||8655 (cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
8608 prev_type->data.array.len == 0) &&8656 prev_type->data.array.len == 0) &&
8609 types_match_const_cast_only(ira, cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,8657 types_match_const_cast_only(ira,
8610 prev_type->data.array.child_type, source_node).id == ConstCastResultIdOk)8658 cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
8659 prev_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk)
8611 {8660 {
8612 prev_inst = cur_inst;8661 prev_inst = cur_inst;
8613 convert_to_const_slice = false;8662 convert_to_const_slice = false;
...@@ -8692,7 +8741,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8692,7 +8741,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8692 ir_add_error_node(ira, source_node,8741 ir_add_error_node(ira, source_node,
8693 buf_sprintf("unable to make maybe out of number literal"));8742 buf_sprintf("unable to make maybe out of number literal"));
8694 return ira->codegen->builtin_types.entry_invalid;8743 return ira->codegen->builtin_types.entry_invalid;
8695 } else if (prev_inst->value.type->id == TypeTableEntryIdMaybe) {8744 } else if (prev_inst->value.type->id == TypeTableEntryIdOptional) {
8696 return prev_inst->value.type;8745 return prev_inst->value.type;
8697 } else {8746 } else {
8698 return get_maybe_type(ira->codegen, prev_inst->value.type);8747 return get_maybe_type(ira->codegen, prev_inst->value.type);
...@@ -8735,10 +8784,12 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,...@@ -8735,10 +8784,12 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
8735 zig_unreachable();8784 zig_unreachable();
8736 case CastOpErrSet:8785 case CastOpErrSet:
8737 case CastOpBitCast:8786 case CastOpBitCast:
8787 case CastOpPtrOfArrayToSlice:
8738 zig_panic("TODO");8788 zig_panic("TODO");
8739 case CastOpNoop:8789 case CastOpNoop:
8740 {8790 {
8741 copy_const_val(const_val, other_val, other_val->special == ConstValSpecialStatic);8791 bool same_global_refs = other_val->special == ConstValSpecialStatic;
8792 copy_const_val(const_val, other_val, same_global_refs);
8742 const_val->type = new_type;8793 const_val->type = new_type;
8743 break;8794 break;
8744 }8795 }
...@@ -8804,7 +8855,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,...@@ -8804,7 +8855,7 @@ static void eval_const_expr_implicit_cast(CastOp cast_op,
8804static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,8855static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value,
8805 TypeTableEntry *wanted_type, CastOp cast_op, bool need_alloca)8856 TypeTableEntry *wanted_type, CastOp cast_op, bool need_alloca)
8806{8857{
8807 if (value->value.special != ConstValSpecialRuntime &&8858 if ((instr_is_comptime(value) || !type_has_bits(wanted_type)) &&
8808 cast_op != CastOpResizeSlice && cast_op != CastOpBytesToSlice)8859 cast_op != CastOpResizeSlice && cast_op != CastOpBytesToSlice)
8809 {8860 {
8810 IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,8861 IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
...@@ -8822,6 +8873,63 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -8822,6 +8873,63 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst
8822 }8873 }
8823}8874}
88248875
8876static IrInstruction *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, IrInstruction *source_instr,
8877 IrInstruction *value, TypeTableEntry *wanted_type)
8878{
8879 assert(value->value.type->id == TypeTableEntryIdPointer);
8880 wanted_type = adjust_ptr_align(ira->codegen, wanted_type, value->value.type->data.pointer.alignment);
8881
8882 if (instr_is_comptime(value)) {
8883 ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value);
8884 if (pointee->special != ConstValSpecialRuntime) {
8885 IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
8886 source_instr->source_node, wanted_type);
8887 result->value.type = wanted_type;
8888 result->value.data.x_ptr.special = ConstPtrSpecialBaseArray;
8889 result->value.data.x_ptr.mut = value->value.data.x_ptr.mut;
8890 result->value.data.x_ptr.data.base_array.array_val = pointee;
8891 result->value.data.x_ptr.data.base_array.elem_index = 0;
8892 result->value.data.x_ptr.data.base_array.is_cstr = false;
8893 return result;
8894 }
8895 }
8896
8897 IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node,
8898 wanted_type, value, CastOpBitCast);
8899 result->value.type = wanted_type;
8900 return result;
8901}
8902
8903static IrInstruction *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInstruction *source_instr,
8904 IrInstruction *value, TypeTableEntry *wanted_type)
8905{
8906 wanted_type = adjust_slice_align(ira->codegen, wanted_type, value->value.type->data.pointer.alignment);
8907
8908 if (instr_is_comptime(value)) {
8909 ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &value->value);
8910 if (pointee->special != ConstValSpecialRuntime) {
8911 assert(value->value.type->id == TypeTableEntryIdPointer);
8912 TypeTableEntry *array_type = value->value.type->data.pointer.child_type;
8913 assert(is_slice(wanted_type));
8914 bool is_const = wanted_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const;
8915
8916 IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
8917 source_instr->source_node, wanted_type);
8918 init_const_slice(ira->codegen, &result->value, pointee, 0, array_type->data.array.len, is_const);
8919 result->value.data.x_struct.fields[slice_ptr_index].data.x_ptr.mut =
8920 value->value.data.x_ptr.mut;
8921 result->value.type = wanted_type;
8922 return result;
8923 }
8924 }
8925
8926 IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node,
8927 wanted_type, value, CastOpPtrOfArrayToSlice);
8928 result->value.type = wanted_type;
8929 ir_add_alloca(ira, result, wanted_type);
8930 return result;
8931}
8932
8825static bool is_container(TypeTableEntry *type) {8933static bool is_container(TypeTableEntry *type) {
8826 return type->id == TypeTableEntryIdStruct ||8934 return type->id == TypeTableEntryIdStruct ||
8827 type->id == TypeTableEntryIdEnum ||8935 type->id == TypeTableEntryIdEnum ||
...@@ -9115,7 +9223,7 @@ static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) {...@@ -9115,7 +9223,7 @@ static FnTableEntry *ir_resolve_fn(IrAnalyze *ira, IrInstruction *fn_value) {
9115}9223}
91169224
9117static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {9225static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
9118 assert(wanted_type->id == TypeTableEntryIdMaybe);9226 assert(wanted_type->id == TypeTableEntryIdOptional);
91199227
9120 if (instr_is_comptime(value)) {9228 if (instr_is_comptime(value)) {
9121 TypeTableEntry *payload_type = wanted_type->data.maybe.child_type;9229 TypeTableEntry *payload_type = wanted_type->data.maybe.child_type;
...@@ -9129,15 +9237,19 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc...@@ -9129,15 +9237,19 @@ static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *sourc
91299237
9130 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,9238 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb,
9131 source_instr->scope, source_instr->source_node);9239 source_instr->scope, source_instr->source_node);
9132 const_instruction->base.value.type = wanted_type;
9133 const_instruction->base.value.special = ConstValSpecialStatic;9240 const_instruction->base.value.special = ConstValSpecialStatic;
9134 const_instruction->base.value.data.x_maybe = val;9241 if (get_codegen_ptr_type(wanted_type) != nullptr) {
9242 copy_const_val(&const_instruction->base.value, val, val->data.x_ptr.mut == ConstPtrMutComptimeConst);
9243 } else {
9244 const_instruction->base.value.data.x_optional = val;
9245 }
9246 const_instruction->base.value.type = wanted_type;
9135 return &const_instruction->base;9247 return &const_instruction->base;
9136 }9248 }
91379249
9138 IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value);9250 IrInstruction *result = ir_build_maybe_wrap(&ira->new_irb, source_instr->scope, source_instr->source_node, value);
9139 result->value.type = wanted_type;9251 result->value.type = wanted_type;
9140 result->value.data.rh_maybe = RuntimeHintMaybeNonNull;9252 result->value.data.rh_maybe = RuntimeHintOptionalNonNull;
9141 ir_add_alloca(ira, result, wanted_type);9253 ir_add_alloca(ira, result, wanted_type);
9142 return result;9254 return result;
9143}9255}
...@@ -9279,16 +9391,21 @@ static IrInstruction *ir_analyze_cast_ref(IrAnalyze *ira, IrInstruction *source_...@@ -9279,16 +9391,21 @@ static IrInstruction *ir_analyze_cast_ref(IrAnalyze *ira, IrInstruction *source_
9279}9391}
92809392
9281static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {9393static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) {
9282 assert(wanted_type->id == TypeTableEntryIdMaybe);9394 assert(wanted_type->id == TypeTableEntryIdOptional);
9283 assert(instr_is_comptime(value));9395 assert(instr_is_comptime(value));
92849396
9285 ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);9397 ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
9286 assert(val);9398 assert(val);
92879399
9288 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb, source_instr->scope, source_instr->source_node);9400 IrInstructionConst *const_instruction = ir_create_instruction<IrInstructionConst>(&ira->new_irb, source_instr->scope, source_instr->source_node);
9289 const_instruction->base.value.type = wanted_type;
9290 const_instruction->base.value.special = ConstValSpecialStatic;9401 const_instruction->base.value.special = ConstValSpecialStatic;
9291 const_instruction->base.value.data.x_maybe = nullptr;9402 if (get_codegen_ptr_type(wanted_type) != nullptr) {
9403 const_instruction->base.value.data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
9404 const_instruction->base.value.data.x_ptr.data.hard_coded_addr.addr = 0;
9405 } else {
9406 const_instruction->base.value.data.x_optional = nullptr;
9407 }
9408 const_instruction->base.value.type = wanted_type;
9292 return &const_instruction->base;9409 return &const_instruction->base;
9293}9410}
92949411
...@@ -9300,9 +9417,19 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi...@@ -9300,9 +9417,19 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi
93009417
9301 if (value->id == IrInstructionIdLoadPtr) {9418 if (value->id == IrInstructionIdLoadPtr) {
9302 IrInstructionLoadPtr *load_ptr_inst = (IrInstructionLoadPtr *) value;9419 IrInstructionLoadPtr *load_ptr_inst = (IrInstructionLoadPtr *) value;
9420
9303 if (load_ptr_inst->ptr->value.type->data.pointer.is_const) {9421 if (load_ptr_inst->ptr->value.type->data.pointer.is_const) {
9304 return load_ptr_inst->ptr;9422 return load_ptr_inst->ptr;
9305 }9423 }
9424
9425 type_ensure_zero_bits_known(ira->codegen, value->value.type);
9426 if (type_is_invalid(value->value.type)) {
9427 return ira->codegen->invalid_instruction;
9428 }
9429
9430 if (!type_has_bits(value->value.type)) {
9431 return load_ptr_inst->ptr;
9432 }
9306 }9433 }
93079434
9308 if (instr_is_comptime(value)) {9435 if (instr_is_comptime(value)) {
...@@ -9810,7 +9937,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -9810,7 +9937,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
9810 }9937 }
98119938
9812 // explicit match or non-const to const9939 // explicit match or non-const to const
9813 if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node).id == ConstCastResultIdOk) {9940 if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node, false).id == ConstCastResultIdOk) {
9814 return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);9941 return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false);
9815 }9942 }
98169943
...@@ -9856,7 +9983,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -9856,7 +9983,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
9856 TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;9983 TypeTableEntry *ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
9857 assert(ptr_type->id == TypeTableEntryIdPointer);9984 assert(ptr_type->id == TypeTableEntryIdPointer);
9858 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&9985 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
9859 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)9986 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
9987 source_node, false).id == ConstCastResultIdOk)
9860 {9988 {
9861 return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);9989 return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
9862 }9990 }
...@@ -9874,7 +10002,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -9874,7 +10002,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
9874 TypeTableEntry *array_type = actual_type->data.pointer.child_type;10002 TypeTableEntry *array_type = actual_type->data.pointer.child_type;
987510003
9876 if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&10004 if ((ptr_type->data.pointer.is_const || array_type->data.array.len == 0) &&
9877 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type, source_node).id == ConstCastResultIdOk)10005 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, array_type->data.array.child_type,
10006 source_node, false).id == ConstCastResultIdOk)
9878 {10007 {
9879 return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);10008 return ir_analyze_array_to_slice(ira, source_instr, value, wanted_type);
9880 }10009 }
...@@ -9890,7 +10019,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -9890,7 +10019,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
9890 wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;10019 wanted_type->data.pointer.child_type->data.structure.fields[slice_ptr_index].type_entry;
9891 assert(ptr_type->id == TypeTableEntryIdPointer);10020 assert(ptr_type->id == TypeTableEntryIdPointer);
9892 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&10021 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
9893 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)10022 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
10023 source_node, false).id == ConstCastResultIdOk)
9894 {10024 {
9895 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);10025 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
9896 if (type_is_invalid(cast1->value.type))10026 if (type_is_invalid(cast1->value.type))
...@@ -9905,7 +10035,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -9905,7 +10035,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
9905 }10035 }
990610036
9907 // explicit cast from [N]T to ?[]const N10037 // explicit cast from [N]T to ?[]const N
9908 if (wanted_type->id == TypeTableEntryIdMaybe &&10038 if (wanted_type->id == TypeTableEntryIdOptional &&
9909 is_slice(wanted_type->data.maybe.child_type) &&10039 is_slice(wanted_type->data.maybe.child_type) &&
9910 actual_type->id == TypeTableEntryIdArray)10040 actual_type->id == TypeTableEntryIdArray)
9911 {10041 {
...@@ -9913,7 +10043,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -9913,7 +10043,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
9913 wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;10043 wanted_type->data.maybe.child_type->data.structure.fields[slice_ptr_index].type_entry;
9914 assert(ptr_type->id == TypeTableEntryIdPointer);10044 assert(ptr_type->id == TypeTableEntryIdPointer);
9915 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&10045 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
9916 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)10046 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
10047 source_node, false).id == ConstCastResultIdOk)
9917 {10048 {
9918 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value);10049 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value);
9919 if (type_is_invalid(cast1->value.type))10050 if (type_is_invalid(cast1->value.type))
...@@ -9973,10 +10104,44 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -9973,10 +10104,44 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
9973 }10104 }
9974 }10105 }
997510106
9976 // explicit cast from child type of maybe type to maybe type10107 // explicit *[N]T to [*]T
9977 if (wanted_type->id == TypeTableEntryIdMaybe) {10108 if (wanted_type->id == TypeTableEntryIdPointer &&
10109 wanted_type->data.pointer.ptr_len == PtrLenUnknown &&
10110 actual_type->id == TypeTableEntryIdPointer &&
10111 actual_type->data.pointer.ptr_len == PtrLenSingle &&
10112 actual_type->data.pointer.child_type->id == TypeTableEntryIdArray &&
10113 actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment &&
10114 types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
10115 actual_type->data.pointer.child_type->data.array.child_type, source_node,
10116 !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
10117 {
10118 return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type);
10119 }
10120
10121 // explicit *[N]T to []T
10122 if (is_slice(wanted_type) &&
10123 actual_type->id == TypeTableEntryIdPointer &&
10124 actual_type->data.pointer.ptr_len == PtrLenSingle &&
10125 actual_type->data.pointer.child_type->id == TypeTableEntryIdArray)
10126 {
10127 TypeTableEntry *slice_ptr_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
10128 assert(slice_ptr_type->id == TypeTableEntryIdPointer);
10129 if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type,
10130 actual_type->data.pointer.child_type->data.array.child_type, source_node,
10131 !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
10132 {
10133 return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type);
10134 }
10135 }
10136
10137
10138 // explicit cast from T to ?T
10139 // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
10140 if (wanted_type->id == TypeTableEntryIdOptional) {
9978 TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type;10141 TypeTableEntry *wanted_child_type = wanted_type->data.maybe.child_type;
9979 if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk) {10142 if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node,
10143 false).id == ConstCastResultIdOk)
10144 {
9980 return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);10145 return ir_analyze_maybe_wrap(ira, source_instr, value, wanted_type);
9981 } else if (actual_type->id == TypeTableEntryIdComptimeInt ||10146 } else if (actual_type->id == TypeTableEntryIdComptimeInt ||
9982 actual_type->id == TypeTableEntryIdComptimeFloat)10147 actual_type->id == TypeTableEntryIdComptimeFloat)
...@@ -10003,7 +10168,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10003,7 +10168,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10003 }10168 }
1000410169
10005 // explicit cast from null literal to maybe type10170 // explicit cast from null literal to maybe type
10006 if (wanted_type->id == TypeTableEntryIdMaybe &&10171 if (wanted_type->id == TypeTableEntryIdOptional &&
10007 actual_type->id == TypeTableEntryIdNull)10172 actual_type->id == TypeTableEntryIdNull)
10008 {10173 {
10009 return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);10174 return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type);
...@@ -10011,7 +10176,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10011,7 +10176,9 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
1001110176
10012 // explicit cast from child type of error type to error type10177 // explicit cast from child type of error type to error type
10013 if (wanted_type->id == TypeTableEntryIdErrorUnion) {10178 if (wanted_type->id == TypeTableEntryIdErrorUnion) {
10014 if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, source_node).id == ConstCastResultIdOk) {10179 if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type,
10180 source_node, false).id == ConstCastResultIdOk)
10181 {
10015 return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);10182 return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type);
10016 } else if (actual_type->id == TypeTableEntryIdComptimeInt ||10183 } else if (actual_type->id == TypeTableEntryIdComptimeInt ||
10017 actual_type->id == TypeTableEntryIdComptimeFloat)10184 actual_type->id == TypeTableEntryIdComptimeFloat)
...@@ -10024,7 +10191,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10024,7 +10191,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10024 }10191 }
10025 }10192 }
1002610193
10027 // explicit cast from [N]T to %[]const T10194 // explicit cast from [N]T to E![]const T
10028 if (wanted_type->id == TypeTableEntryIdErrorUnion &&10195 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
10029 is_slice(wanted_type->data.error_union.payload_type) &&10196 is_slice(wanted_type->data.error_union.payload_type) &&
10030 actual_type->id == TypeTableEntryIdArray)10197 actual_type->id == TypeTableEntryIdArray)
...@@ -10033,7 +10200,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10033,7 +10200,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10033 wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry;10200 wanted_type->data.error_union.payload_type->data.structure.fields[slice_ptr_index].type_entry;
10034 assert(ptr_type->id == TypeTableEntryIdPointer);10201 assert(ptr_type->id == TypeTableEntryIdPointer);
10035 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&10202 if ((ptr_type->data.pointer.is_const || actual_type->data.array.len == 0) &&
10036 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type, source_node).id == ConstCastResultIdOk)10203 types_match_const_cast_only(ira, ptr_type->data.pointer.child_type, actual_type->data.array.child_type,
10204 source_node, false).id == ConstCastResultIdOk)
10037 {10205 {
10038 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);10206 IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value);
10039 if (type_is_invalid(cast1->value.type))10207 if (type_is_invalid(cast1->value.type))
...@@ -10054,13 +10222,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10054,13 +10222,13 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10054 return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);10222 return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type);
10055 }10223 }
1005610224
10057 // explicit cast from T to %?T10225 // explicit cast from T to E!?T
10058 if (wanted_type->id == TypeTableEntryIdErrorUnion &&10226 if (wanted_type->id == TypeTableEntryIdErrorUnion &&
10059 wanted_type->data.error_union.payload_type->id == TypeTableEntryIdMaybe &&10227 wanted_type->data.error_union.payload_type->id == TypeTableEntryIdOptional &&
10060 actual_type->id != TypeTableEntryIdMaybe)10228 actual_type->id != TypeTableEntryIdOptional)
10061 {10229 {
10062 TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type;10230 TypeTableEntry *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type;
10063 if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node).id == ConstCastResultIdOk ||10231 if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk ||
10064 actual_type->id == TypeTableEntryIdNull ||10232 actual_type->id == TypeTableEntryIdNull ||
10065 actual_type->id == TypeTableEntryIdComptimeInt ||10233 actual_type->id == TypeTableEntryIdComptimeInt ||
10066 actual_type->id == TypeTableEntryIdComptimeFloat)10234 actual_type->id == TypeTableEntryIdComptimeFloat)
...@@ -10078,7 +10246,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10078,7 +10246,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10078 }10246 }
1007910247
10080 // explicit cast from number literal to another type10248 // explicit cast from number literal to another type
10081 // explicit cast from number literal to &const integer10249 // explicit cast from number literal to *const integer
10082 if (actual_type->id == TypeTableEntryIdComptimeFloat ||10250 if (actual_type->id == TypeTableEntryIdComptimeFloat ||
10083 actual_type->id == TypeTableEntryIdComptimeInt)10251 actual_type->id == TypeTableEntryIdComptimeInt)
10084 {10252 {
...@@ -10212,7 +10380,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10212,7 +10380,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10212 TypeTableEntry *array_type = wanted_type->data.pointer.child_type;10380 TypeTableEntry *array_type = wanted_type->data.pointer.child_type;
10213 if (array_type->id == TypeTableEntryIdArray && array_type->data.array.len == 1 &&10381 if (array_type->id == TypeTableEntryIdArray && array_type->data.array.len == 1 &&
10214 types_match_const_cast_only(ira, array_type->data.array.child_type,10382 types_match_const_cast_only(ira, array_type->data.array.child_type,
10215 actual_type->data.pointer.child_type, source_node).id == ConstCastResultIdOk)10383 actual_type->data.pointer.child_type, source_node,
10384 !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
10216 {10385 {
10217 if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) {10386 if (wanted_type->data.pointer.alignment > actual_type->data.pointer.alignment) {
10218 ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment"));10387 ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment"));
...@@ -10228,6 +10397,20 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10228,6 +10397,20 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10228 }10397 }
10229 }10398 }
1023010399
10400 // explicit cast from T to *T where T is zero bits
10401 if (wanted_type->id == TypeTableEntryIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle &&
10402 types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
10403 actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk)
10404 {
10405 type_ensure_zero_bits_known(ira->codegen, actual_type);
10406 if (type_is_invalid(actual_type)) {
10407 return ira->codegen->invalid_instruction;
10408 }
10409 if (!type_has_bits(actual_type)) {
10410 return ir_get_ref(ira, source_instr, value, false, false);
10411 }
10412 }
10413
1023110414
10232 // explicit cast from undefined to anything10415 // explicit cast from undefined to anything
10233 if (actual_type->id == TypeTableEntryIdUndefined) {10416 if (actual_type->id == TypeTableEntryIdUndefined) {
...@@ -10237,7 +10420,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -10237,7 +10420,7 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
10237 // explicit cast from something to const pointer of it10420 // explicit cast from something to const pointer of it
10238 if (!type_requires_comptime(actual_type)) {10421 if (!type_requires_comptime(actual_type)) {
10239 TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);10422 TypeTableEntry *const_ptr_actual = get_pointer_to_type(ira->codegen, actual_type, true);
10240 if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node).id == ConstCastResultIdOk) {10423 if (types_match_const_cast_only(ira, wanted_type, const_ptr_actual, source_node, false).id == ConstCastResultIdOk) {
10241 return ir_analyze_cast_ref(ira, source_instr, value, wanted_type);10424 return ir_analyze_cast_ref(ira, source_instr, value, wanted_type);
10242 }10425 }
10243 }10426 }
...@@ -10302,6 +10485,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc...@@ -10302,6 +10485,7 @@ static IrInstruction *ir_get_deref(IrAnalyze *ira, IrInstruction *source_instruc
10302 IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope,10485 IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope,
10303 source_instruction->source_node, child_type);10486 source_instruction->source_node, child_type);
10304 copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst);10487 copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst);
10488 result->value.type = child_type;
10305 return result;10489 return result;
10306 }10490 }
10307 }10491 }
...@@ -10619,6 +10803,16 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {...@@ -10619,6 +10803,16 @@ static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) {
10619 }10803 }
10620}10804}
1062110805
10806static bool optional_value_is_null(ConstExprValue *val) {
10807 assert(val->special == ConstValSpecialStatic);
10808 if (get_codegen_ptr_type(val->type) != nullptr) {
10809 return val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
10810 val->data.x_ptr.data.hard_coded_addr.addr == 0;
10811 } else {
10812 return val->data.x_optional == nullptr;
10813 }
10814}
10815
10622static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {10816static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
10623 IrInstruction *op1 = bin_op_instruction->op1->other;10817 IrInstruction *op1 = bin_op_instruction->op1->other;
10624 IrInstruction *op2 = bin_op_instruction->op2->other;10818 IrInstruction *op2 = bin_op_instruction->op2->other;
...@@ -10627,8 +10821,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -10627,8 +10821,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
10627 IrBinOp op_id = bin_op_instruction->op_id;10821 IrBinOp op_id = bin_op_instruction->op_id;
10628 bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);10822 bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq);
10629 if (is_equality_cmp &&10823 if (is_equality_cmp &&
10630 ((op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdMaybe) ||10824 ((op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdOptional) ||
10631 (op2->value.type->id == TypeTableEntryIdNull && op1->value.type->id == TypeTableEntryIdMaybe) ||10825 (op2->value.type->id == TypeTableEntryIdNull && op1->value.type->id == TypeTableEntryIdOptional) ||
10632 (op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdNull)))10826 (op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdNull)))
10633 {10827 {
10634 if (op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdNull) {10828 if (op1->value.type->id == TypeTableEntryIdNull && op2->value.type->id == TypeTableEntryIdNull) {
...@@ -10648,7 +10842,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -10648,7 +10842,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
10648 ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad);10842 ConstExprValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad);
10649 if (!maybe_val)10843 if (!maybe_val)
10650 return ira->codegen->builtin_types.entry_invalid;10844 return ira->codegen->builtin_types.entry_invalid;
10651 bool is_null = (maybe_val->data.x_maybe == nullptr);10845 bool is_null = optional_value_is_null(maybe_val);
10652 ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base);10846 ConstExprValue *out_val = ir_build_const_from(ira, &bin_op_instruction->base);
10653 out_val->data.x_bool = (op_id == IrBinOpCmpEq) ? is_null : !is_null;10847 out_val->data.x_bool = (op_id == IrBinOpCmpEq) ? is_null : !is_null;
10654 return ira->codegen->builtin_types.entry_bool;10848 return ira->codegen->builtin_types.entry_bool;
...@@ -10797,7 +10991,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -10797,7 +10991,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
10797 case TypeTableEntryIdStruct:10991 case TypeTableEntryIdStruct:
10798 case TypeTableEntryIdUndefined:10992 case TypeTableEntryIdUndefined:
10799 case TypeTableEntryIdNull:10993 case TypeTableEntryIdNull:
10800 case TypeTableEntryIdMaybe:10994 case TypeTableEntryIdOptional:
10801 case TypeTableEntryIdErrorUnion:10995 case TypeTableEntryIdErrorUnion:
10802 case TypeTableEntryIdUnion:10996 case TypeTableEntryIdUnion:
10803 ir_add_error_node(ira, source_node,10997 ir_add_error_node(ira, source_node,
...@@ -11135,7 +11329,13 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *...@@ -11135,7 +11329,13 @@ static TypeTableEntry *ir_analyze_bit_shift(IrAnalyze *ira, IrInstructionBinOp *
1113511329
11136static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {11330static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) {
11137 IrInstruction *op1 = bin_op_instruction->op1->other;11331 IrInstruction *op1 = bin_op_instruction->op1->other;
11332 if (type_is_invalid(op1->value.type))
11333 return ira->codegen->builtin_types.entry_invalid;
11334
11138 IrInstruction *op2 = bin_op_instruction->op2->other;11335 IrInstruction *op2 = bin_op_instruction->op2->other;
11336 if (type_is_invalid(op2->value.type))
11337 return ira->codegen->builtin_types.entry_invalid;
11338
11139 IrBinOp op_id = bin_op_instruction->op_id;11339 IrBinOp op_id = bin_op_instruction->op_id;
1114011340
11141 // look for pointer math11341 // look for pointer math
...@@ -11621,61 +11821,6 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi...@@ -11621,61 +11821,6 @@ static TypeTableEntry *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstructi
11621 zig_unreachable();11821 zig_unreachable();
11622}11822}
1162311823
11624enum VarClassRequired {
11625 VarClassRequiredAny,
11626 VarClassRequiredConst,
11627 VarClassRequiredIllegal,
11628};
11629
11630static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) {
11631 switch (type_entry->id) {
11632 case TypeTableEntryIdInvalid:
11633 zig_unreachable();
11634 case TypeTableEntryIdUnreachable:
11635 return VarClassRequiredIllegal;
11636 case TypeTableEntryIdBool:
11637 case TypeTableEntryIdInt:
11638 case TypeTableEntryIdFloat:
11639 case TypeTableEntryIdVoid:
11640 case TypeTableEntryIdErrorSet:
11641 case TypeTableEntryIdFn:
11642 case TypeTableEntryIdPromise:
11643 return VarClassRequiredAny;
11644 case TypeTableEntryIdComptimeFloat:
11645 case TypeTableEntryIdComptimeInt:
11646 case TypeTableEntryIdUndefined:
11647 case TypeTableEntryIdBlock:
11648 case TypeTableEntryIdNull:
11649 case TypeTableEntryIdOpaque:
11650 case TypeTableEntryIdMetaType:
11651 case TypeTableEntryIdNamespace:
11652 case TypeTableEntryIdBoundFn:
11653 case TypeTableEntryIdArgTuple:
11654 return VarClassRequiredConst;
11655
11656 case TypeTableEntryIdPointer:
11657 if (type_entry->data.pointer.child_type->id == TypeTableEntryIdOpaque) {
11658 return VarClassRequiredAny;
11659 } else {
11660 return get_var_class_required(type_entry->data.pointer.child_type);
11661 }
11662 case TypeTableEntryIdArray:
11663 return get_var_class_required(type_entry->data.array.child_type);
11664 case TypeTableEntryIdMaybe:
11665 return get_var_class_required(type_entry->data.maybe.child_type);
11666 case TypeTableEntryIdErrorUnion:
11667 return get_var_class_required(type_entry->data.error_union.payload_type);
11668
11669 case TypeTableEntryIdStruct:
11670 case TypeTableEntryIdEnum:
11671 case TypeTableEntryIdUnion:
11672 // TODO check the fields of these things and make sure that they don't recursively
11673 // contain any of the other variable classes
11674 return VarClassRequiredAny;
11675 }
11676 zig_unreachable();
11677}
11678
11679static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) {11824static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) {
11680 VariableTableEntry *var = decl_var_instruction->var;11825 VariableTableEntry *var = decl_var_instruction->var;
1168111826
...@@ -11710,36 +11855,41 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc...@@ -11710,36 +11855,41 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
11710 if (type_is_invalid(result_type)) {11855 if (type_is_invalid(result_type)) {
11711 result_type = ira->codegen->builtin_types.entry_invalid;11856 result_type = ira->codegen->builtin_types.entry_invalid;
11712 } else {11857 } else {
11713 switch (get_var_class_required(result_type)) {11858 type_ensure_zero_bits_known(ira->codegen, result_type);
11714 case VarClassRequiredIllegal:11859 if (type_is_invalid(result_type)) {
11860 result_type = ira->codegen->builtin_types.entry_invalid;
11861 }
11862 }
11863
11864 if (!type_is_invalid(result_type)) {
11865 if (result_type->id == TypeTableEntryIdUnreachable ||
11866 result_type->id == TypeTableEntryIdOpaque)
11867 {
11868 ir_add_error_node(ira, source_node,
11869 buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name)));
11870 result_type = ira->codegen->builtin_types.entry_invalid;
11871 } else if (type_requires_comptime(result_type)) {
11872 var_class_requires_const = true;
11873 if (!var->src_is_const && !is_comptime_var) {
11715 ir_add_error_node(ira, source_node,11874 ir_add_error_node(ira, source_node,
11716 buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name)));11875 buf_sprintf("variable of type '%s' must be const or comptime",
11876 buf_ptr(&result_type->name)));
11717 result_type = ira->codegen->builtin_types.entry_invalid;11877 result_type = ira->codegen->builtin_types.entry_invalid;
11718 break;11878 }
11719 case VarClassRequiredConst:11879 } else {
11880 if (casted_init_value->value.special == ConstValSpecialStatic &&
11881 casted_init_value->value.type->id == TypeTableEntryIdFn &&
11882 casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways)
11883 {
11720 var_class_requires_const = true;11884 var_class_requires_const = true;
11721 if (!var->src_is_const && !is_comptime_var) {11885 if (!var->src_is_const && !is_comptime_var) {
11722 ir_add_error_node(ira, source_node,11886 ErrorMsg *msg = ir_add_error_node(ira, source_node,
11723 buf_sprintf("variable of type '%s' must be const or comptime",11887 buf_sprintf("functions marked inline must be stored in const or comptime var"));
11724 buf_ptr(&result_type->name)));11888 AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node;
11889 add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here"));
11725 result_type = ira->codegen->builtin_types.entry_invalid;11890 result_type = ira->codegen->builtin_types.entry_invalid;
11726 }11891 }
11727 break;11892 }
11728 case VarClassRequiredAny:
11729 if (casted_init_value->value.special == ConstValSpecialStatic &&
11730 casted_init_value->value.type->id == TypeTableEntryIdFn &&
11731 casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways)
11732 {
11733 var_class_requires_const = true;
11734 if (!var->src_is_const && !is_comptime_var) {
11735 ErrorMsg *msg = ir_add_error_node(ira, source_node,
11736 buf_sprintf("functions marked inline must be stored in const or comptime var"));
11737 AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node;
11738 add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here"));
11739 result_type = ira->codegen->builtin_types.entry_invalid;
11740 }
11741 }
11742 break;
11743 }11893 }
11744 }11894 }
1174511895
...@@ -11914,7 +12064,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi...@@ -11914,7 +12064,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
11914 case TypeTableEntryIdComptimeInt:12064 case TypeTableEntryIdComptimeInt:
11915 case TypeTableEntryIdUndefined:12065 case TypeTableEntryIdUndefined:
11916 case TypeTableEntryIdNull:12066 case TypeTableEntryIdNull:
11917 case TypeTableEntryIdMaybe:12067 case TypeTableEntryIdOptional:
11918 case TypeTableEntryIdErrorUnion:12068 case TypeTableEntryIdErrorUnion:
11919 case TypeTableEntryIdErrorSet:12069 case TypeTableEntryIdErrorSet:
11920 case TypeTableEntryIdNamespace:12070 case TypeTableEntryIdNamespace:
...@@ -11938,7 +12088,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi...@@ -11938,7 +12088,7 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
11938 case TypeTableEntryIdComptimeInt:12088 case TypeTableEntryIdComptimeInt:
11939 case TypeTableEntryIdUndefined:12089 case TypeTableEntryIdUndefined:
11940 case TypeTableEntryIdNull:12090 case TypeTableEntryIdNull:
11941 case TypeTableEntryIdMaybe:12091 case TypeTableEntryIdOptional:
11942 case TypeTableEntryIdErrorUnion:12092 case TypeTableEntryIdErrorUnion:
11943 case TypeTableEntryIdErrorSet:12093 case TypeTableEntryIdErrorSet:
11944 zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name));12094 zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name));
...@@ -11965,22 +12115,24 @@ static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) {...@@ -11965,22 +12115,24 @@ static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) {
11965static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,12115static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
11966 IrInstructionErrorReturnTrace *instruction)12116 IrInstructionErrorReturnTrace *instruction)
11967{12117{
11968 if (instruction->nullable == IrInstructionErrorReturnTrace::Null) {12118 if (instruction->optional == IrInstructionErrorReturnTrace::Null) {
11969 TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);12119 TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
11970 TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);12120 TypeTableEntry *optional_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
11971 if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) {12121 if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) {
11972 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);12122 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
11973 out_val->data.x_maybe = nullptr;12123 assert(get_codegen_ptr_type(optional_type) != nullptr);
11974 return nullable_type;12124 out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr;
12125 out_val->data.x_ptr.data.hard_coded_addr.addr = 0;
12126 return optional_type;
11975 }12127 }
11976 IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,12128 IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
11977 instruction->base.source_node, instruction->nullable);12129 instruction->base.source_node, instruction->optional);
11978 ir_link_new_instruction(new_instruction, &instruction->base);12130 ir_link_new_instruction(new_instruction, &instruction->base);
11979 return nullable_type;12131 return optional_type;
11980 } else {12132 } else {
11981 assert(ira->codegen->have_err_ret_tracing);12133 assert(ira->codegen->have_err_ret_tracing);
11982 IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,12134 IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
11983 instruction->base.source_node, instruction->nullable);12135 instruction->base.source_node, instruction->optional);
11984 ir_link_new_instruction(new_instruction, &instruction->base);12136 ir_link_new_instruction(new_instruction, &instruction->base);
11985 return get_ptr_to_stack_trace_type(ira->codegen);12137 return get_ptr_to_stack_trace_type(ira->codegen);
11986 }12138 }
...@@ -12620,6 +12772,10 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -12620,6 +12772,10 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
12620 inst_fn_type_id.return_type = specified_return_type;12772 inst_fn_type_id.return_type = specified_return_type;
12621 }12773 }
1262212774
12775 type_ensure_zero_bits_known(ira->codegen, specified_return_type);
12776 if (type_is_invalid(specified_return_type))
12777 return ira->codegen->builtin_types.entry_invalid;
12778
12623 if (type_requires_comptime(specified_return_type)) {12779 if (type_requires_comptime(specified_return_type)) {
12624 // Throw out our work and call the function as if it were comptime.12780 // Throw out our work and call the function as if it were comptime.
12625 return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto);12781 return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto);
...@@ -12854,6 +13010,12 @@ static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp...@@ -12854,6 +13010,12 @@ static TypeTableEntry *ir_analyze_dereference(IrAnalyze *ira, IrInstructionUnOp
12854 if (type_is_invalid(ptr_type)) {13010 if (type_is_invalid(ptr_type)) {
12855 return ira->codegen->builtin_types.entry_invalid;13011 return ira->codegen->builtin_types.entry_invalid;
12856 } else if (ptr_type->id == TypeTableEntryIdPointer) {13012 } else if (ptr_type->id == TypeTableEntryIdPointer) {
13013 if (ptr_type->data.pointer.ptr_len == PtrLenUnknown) {
13014 ir_add_error_node(ira, un_op_instruction->base.source_node,
13015 buf_sprintf("index syntax required for unknown-length pointer type '%s'",
13016 buf_ptr(&ptr_type->name)));
13017 return ira->codegen->builtin_types.entry_invalid;
13018 }
12857 child_type = ptr_type->data.pointer.child_type;13019 child_type = ptr_type->data.pointer.child_type;
12858 } else {13020 } else {
12859 ir_add_error_node(ira, un_op_instruction->base.source_node,13021 ir_add_error_node(ira, un_op_instruction->base.source_node,
...@@ -12902,7 +13064,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op...@@ -12902,7 +13064,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op
12902 case TypeTableEntryIdComptimeInt:13064 case TypeTableEntryIdComptimeInt:
12903 case TypeTableEntryIdUndefined:13065 case TypeTableEntryIdUndefined:
12904 case TypeTableEntryIdNull:13066 case TypeTableEntryIdNull:
12905 case TypeTableEntryIdMaybe:13067 case TypeTableEntryIdOptional:
12906 case TypeTableEntryIdErrorUnion:13068 case TypeTableEntryIdErrorUnion:
12907 case TypeTableEntryIdErrorSet:13069 case TypeTableEntryIdErrorSet:
12908 case TypeTableEntryIdEnum:13070 case TypeTableEntryIdEnum:
...@@ -12921,7 +13083,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op...@@ -12921,7 +13083,7 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op
12921 case TypeTableEntryIdUnreachable:13083 case TypeTableEntryIdUnreachable:
12922 case TypeTableEntryIdOpaque:13084 case TypeTableEntryIdOpaque:
12923 ir_add_error_node(ira, un_op_instruction->base.source_node,13085 ir_add_error_node(ira, un_op_instruction->base.source_node,
12924 buf_sprintf("type '%s' not nullable", buf_ptr(&type_entry->name)));13086 buf_sprintf("type '%s' not optional", buf_ptr(&type_entry->name)));
12925 return ira->codegen->builtin_types.entry_invalid;13087 return ira->codegen->builtin_types.entry_invalid;
12926 }13088 }
12927 zig_unreachable();13089 zig_unreachable();
...@@ -13013,7 +13175,7 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio...@@ -13013,7 +13175,7 @@ static TypeTableEntry *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstructio
13013 return ir_analyze_negation(ira, un_op_instruction);13175 return ir_analyze_negation(ira, un_op_instruction);
13014 case IrUnOpDereference:13176 case IrUnOpDereference:
13015 return ir_analyze_dereference(ira, un_op_instruction);13177 return ir_analyze_dereference(ira, un_op_instruction);
13016 case IrUnOpMaybe:13178 case IrUnOpOptional:
13017 return ir_analyze_maybe(ira, un_op_instruction);13179 return ir_analyze_maybe(ira, un_op_instruction);
13018 }13180 }
13019 zig_unreachable();13181 zig_unreachable();
...@@ -13220,6 +13382,13 @@ static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, ui...@@ -13220,6 +13382,13 @@ static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, ui
13220 ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count);13382 ptr_type->data.pointer.bit_offset, ptr_type->data.pointer.unaligned_bit_count);
13221}13383}
1322213384
13385static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type, uint32_t new_align) {
13386 assert(is_slice(slice_type));
13387 TypeTableEntry *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index].type_entry,
13388 new_align);
13389 return get_slice_type(g, ptr_type);
13390}
13391
13223static TypeTableEntry *adjust_ptr_len(CodeGen *g, TypeTableEntry *ptr_type, PtrLen ptr_len) {13392static TypeTableEntry *adjust_ptr_len(CodeGen *g, TypeTableEntry *ptr_type, PtrLen ptr_len) {
13224 assert(ptr_type->id == TypeTableEntryIdPointer);13393 assert(ptr_type->id == TypeTableEntryIdPointer);
13225 return get_pointer_to_type_extra(g,13394 return get_pointer_to_type_extra(g,
...@@ -13794,10 +13963,14 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -13794,10 +13963,14 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
13794 ir_link_new_instruction(result, &field_ptr_instruction->base);13963 ir_link_new_instruction(result, &field_ptr_instruction->base);
13795 return result->value.type;13964 return result->value.type;
13796 }13965 }
13797 } else if (container_type->id == TypeTableEntryIdArray) {13966 } else if (is_array_ref(container_type)) {
13798 if (buf_eql_str(field_name, "len")) {13967 if (buf_eql_str(field_name, "len")) {
13799 ConstExprValue *len_val = create_const_vals(1);13968 ConstExprValue *len_val = create_const_vals(1);
13800 init_const_usize(ira->codegen, len_val, container_type->data.array.len);13969 if (container_type->id == TypeTableEntryIdPointer) {
13970 init_const_usize(ira->codegen, len_val, container_type->data.pointer.child_type->data.array.len);
13971 } else {
13972 init_const_usize(ira->codegen, len_val, container_type->data.array.len);
13973 }
1380113974
13802 TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;13975 TypeTableEntry *usize = ira->codegen->builtin_types.entry_usize;
13803 bool ptr_is_const = true;13976 bool ptr_is_const = true;
...@@ -14048,7 +14221,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -14048,7 +14221,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
14048 buf_ptr(&child_type->name), buf_ptr(field_name)));14221 buf_ptr(&child_type->name), buf_ptr(field_name)));
14049 return ira->codegen->builtin_types.entry_invalid;14222 return ira->codegen->builtin_types.entry_invalid;
14050 }14223 }
14051 } else if (child_type->id == TypeTableEntryIdMaybe) {14224 } else if (child_type->id == TypeTableEntryIdOptional) {
14052 if (buf_eql_str(field_name, "Child")) {14225 if (buf_eql_str(field_name, "Child")) {
14053 bool ptr_is_const = true;14226 bool ptr_is_const = true;
14054 bool ptr_is_volatile = false;14227 bool ptr_is_volatile = false;
...@@ -14141,6 +14314,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -14141,6 +14314,9 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
1414114314
14142static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) {14315static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) {
14143 IrInstruction *ptr = load_ptr_instruction->ptr->other;14316 IrInstruction *ptr = load_ptr_instruction->ptr->other;
14317 if (type_is_invalid(ptr->value.type))
14318 return ira->codegen->builtin_types.entry_invalid;
14319
14144 IrInstruction *result = ir_get_deref(ira, &load_ptr_instruction->base, ptr);14320 IrInstruction *result = ir_get_deref(ira, &load_ptr_instruction->base, ptr);
14145 ir_link_new_instruction(result, &load_ptr_instruction->base);14321 ir_link_new_instruction(result, &load_ptr_instruction->base);
14146 assert(result->value.type);14322 assert(result->value.type);
...@@ -14229,7 +14405,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi...@@ -14229,7 +14405,7 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi
14229 case TypeTableEntryIdPointer:14405 case TypeTableEntryIdPointer:
14230 case TypeTableEntryIdArray:14406 case TypeTableEntryIdArray:
14231 case TypeTableEntryIdStruct:14407 case TypeTableEntryIdStruct:
14232 case TypeTableEntryIdMaybe:14408 case TypeTableEntryIdOptional:
14233 case TypeTableEntryIdErrorUnion:14409 case TypeTableEntryIdErrorUnion:
14234 case TypeTableEntryIdErrorSet:14410 case TypeTableEntryIdErrorSet:
14235 case TypeTableEntryIdEnum:14411 case TypeTableEntryIdEnum:
...@@ -14497,7 +14673,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,...@@ -14497,7 +14673,7 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
14497 case TypeTableEntryIdStruct:14673 case TypeTableEntryIdStruct:
14498 case TypeTableEntryIdComptimeFloat:14674 case TypeTableEntryIdComptimeFloat:
14499 case TypeTableEntryIdComptimeInt:14675 case TypeTableEntryIdComptimeInt:
14500 case TypeTableEntryIdMaybe:14676 case TypeTableEntryIdOptional:
14501 case TypeTableEntryIdErrorUnion:14677 case TypeTableEntryIdErrorUnion:
14502 case TypeTableEntryIdErrorSet:14678 case TypeTableEntryIdErrorSet:
14503 case TypeTableEntryIdEnum:14679 case TypeTableEntryIdEnum:
...@@ -14605,7 +14781,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira,...@@ -14605,7 +14781,7 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira,
14605 case TypeTableEntryIdStruct:14781 case TypeTableEntryIdStruct:
14606 case TypeTableEntryIdComptimeFloat:14782 case TypeTableEntryIdComptimeFloat:
14607 case TypeTableEntryIdComptimeInt:14783 case TypeTableEntryIdComptimeInt:
14608 case TypeTableEntryIdMaybe:14784 case TypeTableEntryIdOptional:
14609 case TypeTableEntryIdErrorUnion:14785 case TypeTableEntryIdErrorUnion:
14610 case TypeTableEntryIdErrorSet:14786 case TypeTableEntryIdErrorSet:
14611 case TypeTableEntryIdEnum:14787 case TypeTableEntryIdEnum:
...@@ -14676,7 +14852,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,...@@ -14676,7 +14852,7 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
14676 case TypeTableEntryIdPointer:14852 case TypeTableEntryIdPointer:
14677 case TypeTableEntryIdArray:14853 case TypeTableEntryIdArray:
14678 case TypeTableEntryIdStruct:14854 case TypeTableEntryIdStruct:
14679 case TypeTableEntryIdMaybe:14855 case TypeTableEntryIdOptional:
14680 case TypeTableEntryIdErrorUnion:14856 case TypeTableEntryIdErrorUnion:
14681 case TypeTableEntryIdErrorSet:14857 case TypeTableEntryIdErrorSet:
14682 case TypeTableEntryIdEnum:14858 case TypeTableEntryIdEnum:
...@@ -14700,14 +14876,14 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn...@@ -14700,14 +14876,14 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn
1470014876
14701 TypeTableEntry *type_entry = value->value.type;14877 TypeTableEntry *type_entry = value->value.type;
1470214878
14703 if (type_entry->id == TypeTableEntryIdMaybe) {14879 if (type_entry->id == TypeTableEntryIdOptional) {
14704 if (instr_is_comptime(value)) {14880 if (instr_is_comptime(value)) {
14705 ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefBad);14881 ConstExprValue *maybe_val = ir_resolve_const(ira, value, UndefBad);
14706 if (!maybe_val)14882 if (!maybe_val)
14707 return ira->codegen->builtin_types.entry_invalid;14883 return ira->codegen->builtin_types.entry_invalid;
1470814884
14709 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);14885 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
14710 out_val->data.x_bool = (maybe_val->data.x_maybe != nullptr);14886 out_val->data.x_bool = !optional_value_is_null(maybe_val);
14711 return ira->codegen->builtin_types.entry_bool;14887 return ira->codegen->builtin_types.entry_bool;
14712 }14888 }
1471314889
...@@ -14725,7 +14901,7 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn...@@ -14725,7 +14901,7 @@ static TypeTableEntry *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrIn
14725}14901}
1472614902
14727static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,14903static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,
14728 IrInstructionUnwrapMaybe *unwrap_maybe_instruction)14904 IrInstructionUnwrapOptional *unwrap_maybe_instruction)
14729{14905{
14730 IrInstruction *value = unwrap_maybe_instruction->value->other;14906 IrInstruction *value = unwrap_maybe_instruction->value->other;
14731 if (type_is_invalid(value->value.type))14907 if (type_is_invalid(value->value.type))
...@@ -14737,25 +14913,9 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,...@@ -14737,25 +14913,9 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,
14737 TypeTableEntry *type_entry = ptr_type->data.pointer.child_type;14913 TypeTableEntry *type_entry = ptr_type->data.pointer.child_type;
14738 if (type_is_invalid(type_entry)) {14914 if (type_is_invalid(type_entry)) {
14739 return ira->codegen->builtin_types.entry_invalid;14915 return ira->codegen->builtin_types.entry_invalid;
14740 } else if (type_entry->id == TypeTableEntryIdMetaType) {14916 } else if (type_entry->id != TypeTableEntryIdOptional) {
14741 // surprise! actually this is just ??T not an unwrap maybe instruction
14742 ConstExprValue *ptr_val = const_ptr_pointee(ira->codegen, &value->value);
14743 assert(ptr_val->type->id == TypeTableEntryIdMetaType);
14744 TypeTableEntry *child_type = ptr_val->data.x_type;
14745
14746 type_ensure_zero_bits_known(ira->codegen, child_type);
14747 TypeTableEntry *layer1 = get_maybe_type(ira->codegen, child_type);
14748 TypeTableEntry *layer2 = get_maybe_type(ira->codegen, layer1);
14749
14750 IrInstruction *const_instr = ir_build_const_type(&ira->new_irb, unwrap_maybe_instruction->base.scope,
14751 unwrap_maybe_instruction->base.source_node, layer2);
14752 IrInstruction *result_instr = ir_get_ref(ira, &unwrap_maybe_instruction->base, const_instr,
14753 ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile);
14754 ir_link_new_instruction(result_instr, &unwrap_maybe_instruction->base);
14755 return result_instr->value.type;
14756 } else if (type_entry->id != TypeTableEntryIdMaybe) {
14757 ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node,14917 ir_add_error_node(ira, unwrap_maybe_instruction->value->source_node,
14758 buf_sprintf("expected nullable type, found '%s'", buf_ptr(&type_entry->name)));14918 buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name)));
14759 return ira->codegen->builtin_types.entry_invalid;14919 return ira->codegen->builtin_types.entry_invalid;
14760 }14920 }
14761 TypeTableEntry *child_type = type_entry->data.maybe.child_type;14921 TypeTableEntry *child_type = type_entry->data.maybe.child_type;
...@@ -14771,13 +14931,18 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,...@@ -14771,13 +14931,18 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira,
14771 ConstExprValue *maybe_val = const_ptr_pointee(ira->codegen, val);14931 ConstExprValue *maybe_val = const_ptr_pointee(ira->codegen, val);
1477214932
14773 if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {14933 if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) {
14774 if (!maybe_val->data.x_maybe) {14934 if (optional_value_is_null(maybe_val)) {
14775 ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null"));14935 ir_add_error(ira, &unwrap_maybe_instruction->base, buf_sprintf("unable to unwrap null"));
14776 return ira->codegen->builtin_types.entry_invalid;14936 return ira->codegen->builtin_types.entry_invalid;
14777 }14937 }
14778 ConstExprValue *out_val = ir_build_const_from(ira, &unwrap_maybe_instruction->base);14938 ConstExprValue *out_val = ir_build_const_from(ira, &unwrap_maybe_instruction->base);
14779 out_val->data.x_ptr.special = ConstPtrSpecialRef;14939 out_val->data.x_ptr.special = ConstPtrSpecialRef;
14780 out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_maybe;14940 out_val->data.x_ptr.mut = val->data.x_ptr.mut;
14941 if (type_is_codegen_pointer(child_type)) {
14942 out_val->data.x_ptr.data.ref.pointee = maybe_val;
14943 } else {
14944 out_val->data.x_ptr.data.ref.pointee = maybe_val->data.x_optional;
14945 }
14781 return result_type;14946 return result_type;
14782 }14947 }
14783 }14948 }
...@@ -15101,7 +15266,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -15101,7 +15266,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
15101 case TypeTableEntryIdStruct:15266 case TypeTableEntryIdStruct:
15102 case TypeTableEntryIdUndefined:15267 case TypeTableEntryIdUndefined:
15103 case TypeTableEntryIdNull:15268 case TypeTableEntryIdNull:
15104 case TypeTableEntryIdMaybe:15269 case TypeTableEntryIdOptional:
15105 case TypeTableEntryIdBlock:15270 case TypeTableEntryIdBlock:
15106 case TypeTableEntryIdBoundFn:15271 case TypeTableEntryIdBoundFn:
15107 case TypeTableEntryIdArgTuple:15272 case TypeTableEntryIdArgTuple:
...@@ -15622,7 +15787,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_...@@ -15622,7 +15787,7 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_
15622 case TypeTableEntryIdComptimeInt:15787 case TypeTableEntryIdComptimeInt:
15623 case TypeTableEntryIdUndefined:15788 case TypeTableEntryIdUndefined:
15624 case TypeTableEntryIdNull:15789 case TypeTableEntryIdNull:
15625 case TypeTableEntryIdMaybe:15790 case TypeTableEntryIdOptional:
15626 case TypeTableEntryIdErrorUnion:15791 case TypeTableEntryIdErrorUnion:
15627 case TypeTableEntryIdErrorSet:15792 case TypeTableEntryIdErrorSet:
15628 case TypeTableEntryIdUnion:15793 case TypeTableEntryIdUnion:
...@@ -15743,11 +15908,6 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn...@@ -15743,11 +15908,6 @@ static TypeTableEntry *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrIn
15743 return out_val->type;15908 return out_val->type;
15744 }15909 }
1574515910
15746 if (!target->value.type->data.enumeration.generate_name_table) {
15747 target->value.type->data.enumeration.generate_name_table = true;
15748 ira->codegen->name_table_enums.append(target->value.type);
15749 }
15750
15751 IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope,15911 IrInstruction *result = ir_build_tag_name(&ira->new_irb, instruction->base.scope,
15752 instruction->base.source_node, target);15912 instruction->base.source_node, target);
15753 ir_link_new_instruction(result, &instruction->base);15913 ir_link_new_instruction(result, &instruction->base);
...@@ -16140,12 +16300,12 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop...@@ -16140,12 +16300,12 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
16140 0, 0);16300 0, 0);
16141 fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));16301 fn_def_fields[6].type = get_maybe_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr));
16142 if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) {16302 if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0) {
16143 fn_def_fields[6].data.x_maybe = create_const_vals(1);16303 fn_def_fields[6].data.x_optional = create_const_vals(1);
16144 ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name);16304 ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name);
16145 init_const_slice(ira->codegen, fn_def_fields[6].data.x_maybe, lib_name, 0, buf_len(fn_node->lib_name), true);16305 init_const_slice(ira->codegen, fn_def_fields[6].data.x_optional, lib_name, 0, buf_len(fn_node->lib_name), true);
16306 } else {
16307 fn_def_fields[6].data.x_optional = nullptr;
16146 }16308 }
16147 else
16148 fn_def_fields[6].data.x_maybe = nullptr;
16149 // return_type: type16309 // return_type: type
16150 ensure_field_index(fn_def_val->type, "return_type", 7);16310 ensure_field_index(fn_def_val->type, "return_type", 7);
16151 fn_def_fields[7].special = ConstValSpecialStatic;16311 fn_def_fields[7].special = ConstValSpecialStatic;
...@@ -16213,8 +16373,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop...@@ -16213,8 +16373,7 @@ static bool ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Scop
16213 return true;16373 return true;
16214}16374}
1621516375
16216static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry)16376static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) {
16217{
16218 assert(type_entry != nullptr);16377 assert(type_entry != nullptr);
16219 assert(!type_is_invalid(type_entry));16378 assert(!type_is_invalid(type_entry));
1622016379
...@@ -16239,38 +16398,67 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -16239,38 +16398,67 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
16239 enum_field_val->data.x_struct.fields = inner_fields;16398 enum_field_val->data.x_struct.fields = inner_fields;
16240 };16399 };
1624116400
16242 const auto create_ptr_like_type_info = [ira](const char *name, TypeTableEntry *ptr_type_entry) {16401 const auto create_ptr_like_type_info = [ira](TypeTableEntry *ptr_type_entry) {
16402 TypeTableEntry *attrs_type;
16403 uint32_t size_enum_index;
16404 if (is_slice(ptr_type_entry)) {
16405 attrs_type = ptr_type_entry->data.structure.fields[slice_ptr_index].type_entry;
16406 size_enum_index = 2;
16407 } else if (ptr_type_entry->id == TypeTableEntryIdPointer) {
16408 attrs_type = ptr_type_entry;
16409 size_enum_index = (ptr_type_entry->data.pointer.ptr_len == PtrLenSingle) ? 0 : 1;
16410 } else {
16411 zig_unreachable();
16412 }
16413
16414 TypeTableEntry *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer");
16415 ensure_complete_type(ira->codegen, type_info_pointer_type);
16416 assert(!type_is_invalid(type_info_pointer_type));
16417
16243 ConstExprValue *result = create_const_vals(1);16418 ConstExprValue *result = create_const_vals(1);
16244 result->special = ConstValSpecialStatic;16419 result->special = ConstValSpecialStatic;
16245 result->type = ir_type_info_get_type(ira, name);16420 result->type = type_info_pointer_type;
1624616421
16247 ConstExprValue *fields = create_const_vals(4);16422 ConstExprValue *fields = create_const_vals(5);
16248 result->data.x_struct.fields = fields;16423 result->data.x_struct.fields = fields;
1624916424
16250 // is_const: bool16425 // size: Size
16251 ensure_field_index(result->type, "is_const", 0);16426 ensure_field_index(result->type, "size", 0);
16427 TypeTableEntry *type_info_pointer_size_type = ir_type_info_get_type(ira, "Size", type_info_pointer_type);
16428 ensure_complete_type(ira->codegen, type_info_pointer_size_type);
16429 assert(!type_is_invalid(type_info_pointer_size_type));
16252 fields[0].special = ConstValSpecialStatic;16430 fields[0].special = ConstValSpecialStatic;
16253 fields[0].type = ira->codegen->builtin_types.entry_bool;16431 fields[0].type = type_info_pointer_size_type;
16254 fields[0].data.x_bool = ptr_type_entry->data.pointer.is_const;16432 bigint_init_unsigned(&fields[0].data.x_enum_tag, size_enum_index);
16255 // is_volatile: bool16433
16256 ensure_field_index(result->type, "is_volatile", 1);16434 // is_const: bool
16435 ensure_field_index(result->type, "is_const", 1);
16257 fields[1].special = ConstValSpecialStatic;16436 fields[1].special = ConstValSpecialStatic;
16258 fields[1].type = ira->codegen->builtin_types.entry_bool;16437 fields[1].type = ira->codegen->builtin_types.entry_bool;
16259 fields[1].data.x_bool = ptr_type_entry->data.pointer.is_volatile;16438 fields[1].data.x_bool = attrs_type->data.pointer.is_const;
16260 // alignment: u3216439 // is_volatile: bool
16261 ensure_field_index(result->type, "alignment", 2);16440 ensure_field_index(result->type, "is_volatile", 2);
16262 fields[2].special = ConstValSpecialStatic;16441 fields[2].special = ConstValSpecialStatic;
16263 fields[2].type = ira->codegen->builtin_types.entry_u32;16442 fields[2].type = ira->codegen->builtin_types.entry_bool;
16264 bigint_init_unsigned(&fields[2].data.x_bigint, ptr_type_entry->data.pointer.alignment);16443 fields[2].data.x_bool = attrs_type->data.pointer.is_volatile;
16265 // child: type16444 // alignment: u32
16266 ensure_field_index(result->type, "child", 3);16445 ensure_field_index(result->type, "alignment", 3);
16267 fields[3].special = ConstValSpecialStatic;16446 fields[3].special = ConstValSpecialStatic;
16268 fields[3].type = ira->codegen->builtin_types.entry_type;16447 fields[3].type = ira->codegen->builtin_types.entry_u32;
16269 fields[3].data.x_type = ptr_type_entry->data.pointer.child_type;16448 bigint_init_unsigned(&fields[3].data.x_bigint, attrs_type->data.pointer.alignment);
16449 // child: type
16450 ensure_field_index(result->type, "child", 4);
16451 fields[4].special = ConstValSpecialStatic;
16452 fields[4].type = ira->codegen->builtin_types.entry_type;
16453 fields[4].data.x_type = attrs_type->data.pointer.child_type;
1627016454
16271 return result;16455 return result;
16272 };16456 };
1627316457
16458 if (type_entry == ira->codegen->builtin_types.entry_global_error_set) {
16459 zig_panic("TODO implement @typeInfo for global error set");
16460 }
16461
16274 ConstExprValue *result = nullptr;16462 ConstExprValue *result = nullptr;
16275 switch (type_entry->id)16463 switch (type_entry->id)
16276 {16464 {
...@@ -16339,7 +16527,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -16339,7 +16527,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
16339 }16527 }
16340 case TypeTableEntryIdPointer:16528 case TypeTableEntryIdPointer:
16341 {16529 {
16342 result = create_ptr_like_type_info("Pointer", type_entry);16530 result = create_ptr_like_type_info(type_entry);
16343 break;16531 break;
16344 }16532 }
16345 case TypeTableEntryIdArray:16533 case TypeTableEntryIdArray:
...@@ -16364,11 +16552,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -16364,11 +16552,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1636416552
16365 break;16553 break;
16366 }16554 }
16367 case TypeTableEntryIdMaybe:16555 case TypeTableEntryIdOptional:
16368 {16556 {
16369 result = create_const_vals(1);16557 result = create_const_vals(1);
16370 result->special = ConstValSpecialStatic;16558 result->special = ConstValSpecialStatic;
16371 result->type = ir_type_info_get_type(ira, "Nullable");16559 result->type = ir_type_info_get_type(ira, "Optional");
1637216560
16373 ConstExprValue *fields = create_const_vals(1);16561 ConstExprValue *fields = create_const_vals(1);
16374 result->data.x_struct.fields = fields;16562 result->data.x_struct.fields = fields;
...@@ -16570,8 +16758,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -16570,8 +16758,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1657016758
16571 TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField");16759 TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField");
1657216760
16573 for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++)16761 for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) {
16574 {
16575 TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];16762 TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];
16576 ConstExprValue *union_field_val = &union_field_array->data.x_array.s_none.elements[union_field_index];16763 ConstExprValue *union_field_val = &union_field_array->data.x_array.s_none.elements[union_field_index];
1657716764
...@@ -16582,12 +16769,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -16582,12 +16769,11 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
16582 inner_fields[1].special = ConstValSpecialStatic;16769 inner_fields[1].special = ConstValSpecialStatic;
16583 inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type);16770 inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type);
1658416771
16585 if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef)16772 if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef) {
16586 inner_fields[1].data.x_maybe = nullptr;16773 inner_fields[1].data.x_optional = nullptr;
16587 else16774 } else {
16588 {16775 inner_fields[1].data.x_optional = create_const_vals(1);
16589 inner_fields[1].data.x_maybe = create_const_vals(1);16776 make_enum_field_val(inner_fields[1].data.x_optional, union_field->enum_field, type_info_enum_field_type);
16590 make_enum_field_val(inner_fields[1].data.x_maybe, union_field->enum_field, type_info_enum_field_type);
16591 }16777 }
1659216778
16593 inner_fields[2].special = ConstValSpecialStatic;16779 inner_fields[2].special = ConstValSpecialStatic;
...@@ -16612,15 +16798,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -16612,15 +16798,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
16612 case TypeTableEntryIdStruct:16798 case TypeTableEntryIdStruct:
16613 {16799 {
16614 if (type_entry->data.structure.is_slice) {16800 if (type_entry->data.structure.is_slice) {
16615 Buf ptr_field_name = BUF_INIT;16801 result = create_ptr_like_type_info(type_entry);
16616 buf_init_from_str(&ptr_field_name, "ptr");
16617 TypeTableEntry *ptr_type = type_entry->data.structure.fields_by_name.get(&ptr_field_name)->type_entry;
16618 ensure_complete_type(ira->codegen, ptr_type);
16619 if (type_is_invalid(ptr_type))
16620 return nullptr;
16621 buf_deinit(&ptr_field_name);
16622
16623 result = create_ptr_like_type_info("Slice", ptr_type);
16624 break;16802 break;
16625 }16803 }
1662616804
...@@ -16651,8 +16829,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -16651,8 +16829,7 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
1665116829
16652 init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false);16830 init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false);
1665316831
16654 for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++)16832 for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) {
16655 {
16656 TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index];16833 TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index];
16657 ConstExprValue *struct_field_val = &struct_field_array->data.x_array.s_none.elements[struct_field_index];16834 ConstExprValue *struct_field_val = &struct_field_array->data.x_array.s_none.elements[struct_field_index];
1665816835
...@@ -16663,15 +16840,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t...@@ -16663,15 +16840,14 @@ static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *t
16663 inner_fields[1].special = ConstValSpecialStatic;16840 inner_fields[1].special = ConstValSpecialStatic;
16664 inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize);16841 inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize);
1666516842
16666 if (!type_has_bits(struct_field->type_entry))16843 if (!type_has_bits(struct_field->type_entry)) {
16667 inner_fields[1].data.x_maybe = nullptr;16844 inner_fields[1].data.x_optional = nullptr;
16668 else16845 } else {
16669 {
16670 size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index);16846 size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index);
16671 inner_fields[1].data.x_maybe = create_const_vals(1);16847 inner_fields[1].data.x_optional = create_const_vals(1);
16672 inner_fields[1].data.x_maybe->special = ConstValSpecialStatic;16848 inner_fields[1].data.x_optional->special = ConstValSpecialStatic;
16673 inner_fields[1].data.x_maybe->type = ira->codegen->builtin_types.entry_usize;16849 inner_fields[1].data.x_optional->type = ira->codegen->builtin_types.entry_usize;
16674 bigint_init_unsigned(&inner_fields[1].data.x_maybe->data.x_bigint, byte_offset);16850 bigint_init_unsigned(&inner_fields[1].data.x_optional->data.x_bigint, byte_offset);
16675 }16851 }
1667616852
16677 inner_fields[2].special = ConstValSpecialStatic;16853 inner_fields[2].special = ConstValSpecialStatic;
...@@ -17896,7 +18072,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc...@@ -17896,7 +18072,7 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc
17896 case TypeTableEntryIdPromise:18072 case TypeTableEntryIdPromise:
17897 case TypeTableEntryIdArray:18073 case TypeTableEntryIdArray:
17898 case TypeTableEntryIdStruct:18074 case TypeTableEntryIdStruct:
17899 case TypeTableEntryIdMaybe:18075 case TypeTableEntryIdOptional:
17900 case TypeTableEntryIdErrorUnion:18076 case TypeTableEntryIdErrorUnion:
17901 case TypeTableEntryIdErrorSet:18077 case TypeTableEntryIdErrorSet:
17902 case TypeTableEntryIdEnum:18078 case TypeTableEntryIdEnum:
...@@ -18422,30 +18598,6 @@ static TypeTableEntry *ir_analyze_instruction_check_statement_is_void(IrAnalyze...@@ -18422,30 +18598,6 @@ static TypeTableEntry *ir_analyze_instruction_check_statement_is_void(IrAnalyze
18422 return ira->codegen->builtin_types.entry_void;18598 return ira->codegen->builtin_types.entry_void;
18423}18599}
1842418600
18425static TypeTableEntry *ir_analyze_instruction_can_implicit_cast(IrAnalyze *ira,
18426 IrInstructionCanImplicitCast *instruction)
18427{
18428 IrInstruction *type_value = instruction->type_value->other;
18429 TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
18430 if (type_is_invalid(type_entry))
18431 return ira->codegen->builtin_types.entry_invalid;
18432
18433 IrInstruction *target_value = instruction->target_value->other;
18434 if (type_is_invalid(target_value->value.type))
18435 return ira->codegen->builtin_types.entry_invalid;
18436
18437 ImplicitCastMatchResult result = ir_types_match_with_implicit_cast(ira, type_entry, target_value->value.type,
18438 target_value);
18439
18440 if (result == ImplicitCastMatchResultReportedError) {
18441 zig_panic("TODO refactor implicit cast tester to return bool without reporting errors");
18442 }
18443
18444 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
18445 out_val->data.x_bool = (result == ImplicitCastMatchResultYes);
18446 return ira->codegen->builtin_types.entry_bool;
18447}
18448
18449static TypeTableEntry *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic *instruction) {18601static TypeTableEntry *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic *instruction) {
18450 IrInstruction *msg = instruction->msg->other;18602 IrInstruction *msg = instruction->msg->other;
18451 if (type_is_invalid(msg->value.type))18603 if (type_is_invalid(msg->value.type))
...@@ -18484,7 +18636,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3...@@ -18484,7 +18636,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
18484 old_align_bytes = fn_type_id.alignment;18636 old_align_bytes = fn_type_id.alignment;
18485 fn_type_id.alignment = align_bytes;18637 fn_type_id.alignment = align_bytes;
18486 result_type = get_fn_type(ira->codegen, &fn_type_id);18638 result_type = get_fn_type(ira->codegen, &fn_type_id);
18487 } else if (target_type->id == TypeTableEntryIdMaybe &&18639 } else if (target_type->id == TypeTableEntryIdOptional &&
18488 target_type->data.maybe.child_type->id == TypeTableEntryIdPointer)18640 target_type->data.maybe.child_type->id == TypeTableEntryIdPointer)
18489 {18641 {
18490 TypeTableEntry *ptr_type = target_type->data.maybe.child_type;18642 TypeTableEntry *ptr_type = target_type->data.maybe.child_type;
...@@ -18492,7 +18644,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3...@@ -18492,7 +18644,7 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
18492 TypeTableEntry *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes);18644 TypeTableEntry *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes);
1849318645
18494 result_type = get_maybe_type(ira->codegen, better_ptr_type);18646 result_type = get_maybe_type(ira->codegen, better_ptr_type);
18495 } else if (target_type->id == TypeTableEntryIdMaybe &&18647 } else if (target_type->id == TypeTableEntryIdOptional &&
18496 target_type->data.maybe.child_type->id == TypeTableEntryIdFn)18648 target_type->data.maybe.child_type->id == TypeTableEntryIdFn)
18497 {18649 {
18498 FnTypeId fn_type_id = target_type->data.maybe.child_type->data.fn.fn_type_id;18650 FnTypeId fn_type_id = target_type->data.maybe.child_type->data.fn.fn_type_id;
...@@ -18650,7 +18802,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue...@@ -18650,7 +18802,7 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
18650 return;18802 return;
18651 case TypeTableEntryIdStruct:18803 case TypeTableEntryIdStruct:
18652 zig_panic("TODO buf_write_value_bytes struct type");18804 zig_panic("TODO buf_write_value_bytes struct type");
18653 case TypeTableEntryIdMaybe:18805 case TypeTableEntryIdOptional:
18654 zig_panic("TODO buf_write_value_bytes maybe type");18806 zig_panic("TODO buf_write_value_bytes maybe type");
18655 case TypeTableEntryIdErrorUnion:18807 case TypeTableEntryIdErrorUnion:
18656 zig_panic("TODO buf_write_value_bytes error union");18808 zig_panic("TODO buf_write_value_bytes error union");
...@@ -18708,7 +18860,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue...@@ -18708,7 +18860,7 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue
18708 zig_panic("TODO buf_read_value_bytes array type");18860 zig_panic("TODO buf_read_value_bytes array type");
18709 case TypeTableEntryIdStruct:18861 case TypeTableEntryIdStruct:
18710 zig_panic("TODO buf_read_value_bytes struct type");18862 zig_panic("TODO buf_read_value_bytes struct type");
18711 case TypeTableEntryIdMaybe:18863 case TypeTableEntryIdOptional:
18712 zig_panic("TODO buf_read_value_bytes maybe type");18864 zig_panic("TODO buf_read_value_bytes maybe type");
18713 case TypeTableEntryIdErrorUnion:18865 case TypeTableEntryIdErrorUnion:
18714 zig_panic("TODO buf_read_value_bytes error union");18866 zig_panic("TODO buf_read_value_bytes error union");
...@@ -18946,9 +19098,6 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr...@@ -18946,9 +19098,6 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr
18946 ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);19098 ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
18947 if (!val)19099 if (!val)
18948 return ira->codegen->builtin_types.entry_invalid;19100 return ira->codegen->builtin_types.entry_invalid;
18949 if (target->value.type->id == TypeTableEntryIdMaybe) {
18950 val = val->data.x_maybe;
18951 }
18952 if (val->type->id == TypeTableEntryIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {19101 if (val->type->id == TypeTableEntryIdPointer && val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) {
18953 IrInstruction *result = ir_create_const(&ira->new_irb, instruction->base.scope,19102 IrInstruction *result = ir_create_const(&ira->new_irb, instruction->base.scope,
18954 instruction->base.source_node, usize);19103 instruction->base.source_node, usize);
...@@ -18973,6 +19122,9 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruc...@@ -18973,6 +19122,9 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruc
18973 if (child_type->id == TypeTableEntryIdUnreachable) {19122 if (child_type->id == TypeTableEntryIdUnreachable) {
18974 ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));19123 ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));
18975 return ira->codegen->builtin_types.entry_invalid;19124 return ira->codegen->builtin_types.entry_invalid;
19125 } else if (child_type->id == TypeTableEntryIdOpaque && instruction->ptr_len == PtrLenUnknown) {
19126 ir_add_error(ira, &instruction->base, buf_sprintf("unknown-length pointer to opaque"));
19127 return ira->codegen->builtin_types.entry_invalid;
18976 }19128 }
1897719129
18978 uint32_t align_bytes;19130 uint32_t align_bytes;
...@@ -19624,7 +19776,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -19624,7 +19776,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
19624 case IrInstructionIdUnionInit:19776 case IrInstructionIdUnionInit:
19625 case IrInstructionIdStructFieldPtr:19777 case IrInstructionIdStructFieldPtr:
19626 case IrInstructionIdUnionFieldPtr:19778 case IrInstructionIdUnionFieldPtr:
19627 case IrInstructionIdMaybeWrap:19779 case IrInstructionIdOptionalWrap:
19628 case IrInstructionIdErrWrapCode:19780 case IrInstructionIdErrWrapCode:
19629 case IrInstructionIdErrWrapPayload:19781 case IrInstructionIdErrWrapPayload:
19630 case IrInstructionIdCast:19782 case IrInstructionIdCast:
...@@ -19684,8 +19836,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -19684,8 +19836,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
19684 return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction);19836 return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction);
19685 case IrInstructionIdTestNonNull:19837 case IrInstructionIdTestNonNull:
19686 return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction);19838 return ir_analyze_instruction_test_non_null(ira, (IrInstructionTestNonNull *)instruction);
19687 case IrInstructionIdUnwrapMaybe:19839 case IrInstructionIdUnwrapOptional:
19688 return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapMaybe *)instruction);19840 return ir_analyze_instruction_unwrap_maybe(ira, (IrInstructionUnwrapOptional *)instruction);
19689 case IrInstructionIdClz:19841 case IrInstructionIdClz:
19690 return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction);19842 return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction);
19691 case IrInstructionIdCtz:19843 case IrInstructionIdCtz:
...@@ -19776,8 +19928,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -19776,8 +19928,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
19776 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction);19928 return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction);
19777 case IrInstructionIdCheckStatementIsVoid:19929 case IrInstructionIdCheckStatementIsVoid:
19778 return ir_analyze_instruction_check_statement_is_void(ira, (IrInstructionCheckStatementIsVoid *)instruction);19930 return ir_analyze_instruction_check_statement_is_void(ira, (IrInstructionCheckStatementIsVoid *)instruction);
19779 case IrInstructionIdCanImplicitCast:
19780 return ir_analyze_instruction_can_implicit_cast(ira, (IrInstructionCanImplicitCast *)instruction);
19781 case IrInstructionIdDeclRef:19931 case IrInstructionIdDeclRef:
19782 return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction);19932 return ir_analyze_instruction_decl_ref(ira, (IrInstructionDeclRef *)instruction);
19783 case IrInstructionIdPanic:19933 case IrInstructionIdPanic:
...@@ -19873,6 +20023,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -19873,6 +20023,7 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
19873static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) {20023static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) {
19874 TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction);20024 TypeTableEntry *instruction_type = ir_analyze_instruction_nocast(ira, instruction);
19875 instruction->value.type = instruction_type;20025 instruction->value.type = instruction_type;
20026
19876 if (instruction->other) {20027 if (instruction->other) {
19877 instruction->other->value.type = instruction_type;20028 instruction->other->value.type = instruction_type;
19878 } else {20029 } else {
...@@ -20022,7 +20173,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -20022,7 +20173,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
20022 case IrInstructionIdSliceType:20173 case IrInstructionIdSliceType:
20023 case IrInstructionIdSizeOf:20174 case IrInstructionIdSizeOf:
20024 case IrInstructionIdTestNonNull:20175 case IrInstructionIdTestNonNull:
20025 case IrInstructionIdUnwrapMaybe:20176 case IrInstructionIdUnwrapOptional:
20026 case IrInstructionIdClz:20177 case IrInstructionIdClz:
20027 case IrInstructionIdCtz:20178 case IrInstructionIdCtz:
20028 case IrInstructionIdSwitchVar:20179 case IrInstructionIdSwitchVar:
...@@ -20044,7 +20195,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -20044,7 +20195,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
20044 case IrInstructionIdFrameAddress:20195 case IrInstructionIdFrameAddress:
20045 case IrInstructionIdTestErr:20196 case IrInstructionIdTestErr:
20046 case IrInstructionIdUnwrapErrCode:20197 case IrInstructionIdUnwrapErrCode:
20047 case IrInstructionIdMaybeWrap:20198 case IrInstructionIdOptionalWrap:
20048 case IrInstructionIdErrWrapCode:20199 case IrInstructionIdErrWrapCode:
20049 case IrInstructionIdErrWrapPayload:20200 case IrInstructionIdErrWrapPayload:
20050 case IrInstructionIdFnProto:20201 case IrInstructionIdFnProto:
...@@ -20057,7 +20208,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -20057,7 +20208,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
20057 case IrInstructionIdIntToEnum:20208 case IrInstructionIdIntToEnum:
20058 case IrInstructionIdIntToErr:20209 case IrInstructionIdIntToErr:
20059 case IrInstructionIdErrToInt:20210 case IrInstructionIdErrToInt:
20060 case IrInstructionIdCanImplicitCast:
20061 case IrInstructionIdDeclRef:20211 case IrInstructionIdDeclRef:
20062 case IrInstructionIdErrName:20212 case IrInstructionIdErrName:
20063 case IrInstructionIdTypeName:20213 case IrInstructionIdTypeName:
src/ir_print.cpp+8-19
...@@ -148,7 +148,7 @@ static const char *ir_un_op_id_str(IrUnOp op_id) {...@@ -148,7 +148,7 @@ static const char *ir_un_op_id_str(IrUnOp op_id) {
148 return "-%";148 return "-%";
149 case IrUnOpDereference:149 case IrUnOpDereference:
150 return "*";150 return "*";
151 case IrUnOpMaybe:151 case IrUnOpOptional:
152 return "?";152 return "?";
153 }153 }
154 zig_unreachable();154 zig_unreachable();
...@@ -481,7 +481,7 @@ static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instructi...@@ -481,7 +481,7 @@ static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instructi
481 fprintf(irp->f, " != null");481 fprintf(irp->f, " != null");
482}482}
483483
484static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapMaybe *instruction) {484static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapOptional *instruction) {
485 fprintf(irp->f, "&??*");485 fprintf(irp->f, "&??*");
486 ir_print_other_instruction(irp, instruction->value);486 ir_print_other_instruction(irp, instruction->value);
487 if (!instruction->safety_check_on) {487 if (!instruction->safety_check_on) {
...@@ -777,7 +777,7 @@ static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayl...@@ -777,7 +777,7 @@ static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayl
777 }777 }
778}778}
779779
780static void ir_print_maybe_wrap(IrPrint *irp, IrInstructionMaybeWrap *instruction) {780static void ir_print_maybe_wrap(IrPrint *irp, IrInstructionOptionalWrap *instruction) {
781 fprintf(irp->f, "@maybeWrap(");781 fprintf(irp->f, "@maybeWrap(");
782 ir_print_other_instruction(irp, instruction->value);782 ir_print_other_instruction(irp, instruction->value);
783 fprintf(irp->f, ")");783 fprintf(irp->f, ")");
...@@ -913,14 +913,6 @@ static void ir_print_tag_name(IrPrint *irp, IrInstructionTagName *instruction) {...@@ -913,14 +913,6 @@ static void ir_print_tag_name(IrPrint *irp, IrInstructionTagName *instruction) {
913 ir_print_other_instruction(irp, instruction->target);913 ir_print_other_instruction(irp, instruction->target);
914}914}
915915
916static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCast *instruction) {
917 fprintf(irp->f, "@canImplicitCast(");
918 ir_print_other_instruction(irp, instruction->type_value);
919 fprintf(irp->f, ",");
920 ir_print_other_instruction(irp, instruction->target_value);
921 fprintf(irp->f, ")");
922}
923
924static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) {916static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) {
925 fprintf(irp->f, "&");917 fprintf(irp->f, "&");
926 if (instruction->align_value != nullptr) {918 if (instruction->align_value != nullptr) {
...@@ -1040,7 +1032,7 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {...@@ -1040,7 +1032,7 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {
10401032
1041static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) {1033static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) {
1042 fprintf(irp->f, "@errorReturnTrace(");1034 fprintf(irp->f, "@errorReturnTrace(");
1043 switch (instruction->nullable) {1035 switch (instruction->optional) {
1044 case IrInstructionErrorReturnTrace::Null:1036 case IrInstructionErrorReturnTrace::Null:
1045 fprintf(irp->f, "Null");1037 fprintf(irp->f, "Null");
1046 break;1038 break;
...@@ -1356,8 +1348,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1356,8 +1348,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1356 case IrInstructionIdTestNonNull:1348 case IrInstructionIdTestNonNull:
1357 ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction);1349 ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction);
1358 break;1350 break;
1359 case IrInstructionIdUnwrapMaybe:1351 case IrInstructionIdUnwrapOptional:
1360 ir_print_unwrap_maybe(irp, (IrInstructionUnwrapMaybe *)instruction);1352 ir_print_unwrap_maybe(irp, (IrInstructionUnwrapOptional *)instruction);
1361 break;1353 break;
1362 case IrInstructionIdCtz:1354 case IrInstructionIdCtz:
1363 ir_print_ctz(irp, (IrInstructionCtz *)instruction);1355 ir_print_ctz(irp, (IrInstructionCtz *)instruction);
...@@ -1473,8 +1465,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1473,8 +1465,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1473 case IrInstructionIdUnwrapErrPayload:1465 case IrInstructionIdUnwrapErrPayload:
1474 ir_print_unwrap_err_payload(irp, (IrInstructionUnwrapErrPayload *)instruction);1466 ir_print_unwrap_err_payload(irp, (IrInstructionUnwrapErrPayload *)instruction);
1475 break;1467 break;
1476 case IrInstructionIdMaybeWrap:1468 case IrInstructionIdOptionalWrap:
1477 ir_print_maybe_wrap(irp, (IrInstructionMaybeWrap *)instruction);1469 ir_print_maybe_wrap(irp, (IrInstructionOptionalWrap *)instruction);
1478 break;1470 break;
1479 case IrInstructionIdErrWrapCode:1471 case IrInstructionIdErrWrapCode:
1480 ir_print_err_wrap_code(irp, (IrInstructionErrWrapCode *)instruction);1472 ir_print_err_wrap_code(irp, (IrInstructionErrWrapCode *)instruction);
...@@ -1524,9 +1516,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1524,9 +1516,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1524 case IrInstructionIdTagName:1516 case IrInstructionIdTagName:
1525 ir_print_tag_name(irp, (IrInstructionTagName *)instruction);1517 ir_print_tag_name(irp, (IrInstructionTagName *)instruction);
1526 break;1518 break;
1527 case IrInstructionIdCanImplicitCast:
1528 ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction);
1529 break;
1530 case IrInstructionIdPtrType:1519 case IrInstructionIdPtrType:
1531 ir_print_ptr_type(irp, (IrInstructionPtrType *)instruction);1520 ir_print_ptr_type(irp, (IrInstructionPtrType *)instruction);
1532 break;1521 break;
src/link.cpp+14-1
...@@ -391,6 +391,19 @@ static void construct_linker_job_elf(LinkJob *lj) {...@@ -391,6 +391,19 @@ static void construct_linker_job_elf(LinkJob *lj) {
391 }391 }
392}392}
393393
394static void construct_linker_job_wasm(LinkJob *lj) {
395 CodeGen *g = lj->codegen;
396
397 lj->args.append("--relocatable"); // So lld doesn't look for _start.
398 lj->args.append("-o");
399 lj->args.append(buf_ptr(&lj->out_file));
400
401 // .o files
402 for (size_t i = 0; i < g->link_objects.length; i += 1) {
403 lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
404 }
405}
406
394//static bool is_target_cyg_mingw(const ZigTarget *target) {407//static bool is_target_cyg_mingw(const ZigTarget *target) {
395// return (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_Cygnus) ||408// return (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_Cygnus) ||
396// (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU);409// (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU);
...@@ -924,7 +937,7 @@ static void construct_linker_job(LinkJob *lj) {...@@ -924,7 +937,7 @@ static void construct_linker_job(LinkJob *lj) {
924 case ZigLLVM_MachO:937 case ZigLLVM_MachO:
925 return construct_linker_job_macho(lj);938 return construct_linker_job_macho(lj);
926 case ZigLLVM_Wasm:939 case ZigLLVM_Wasm:
927 zig_panic("TODO link wasm");940 return construct_linker_job_wasm(lj);
928 }941 }
929}942}
930943
src/main.cpp+15
...@@ -23,6 +23,7 @@ static int usage(const char *arg0) {...@@ -23,6 +23,7 @@ static int usage(const char *arg0) {
23 " build-exe [source] create executable from source or object files\n"23 " build-exe [source] create executable from source or object files\n"
24 " build-lib [source] create library from source or object files\n"24 " build-lib [source] create library from source or object files\n"
25 " build-obj [source] create object from source or assembly\n"25 " build-obj [source] create object from source or assembly\n"
26 " builtin show the source code of that @import(\"builtin\")\n"
26 " run [source] create executable and run immediately\n"27 " run [source] create executable and run immediately\n"
27 " translate-c [source] convert c code to zig code\n"28 " translate-c [source] convert c code to zig code\n"
28 " targets list available compilation targets\n"29 " targets list available compilation targets\n"
...@@ -214,6 +215,7 @@ static Buf *resolve_zig_lib_dir(void) {...@@ -214,6 +215,7 @@ static Buf *resolve_zig_lib_dir(void) {
214enum Cmd {215enum Cmd {
215 CmdInvalid,216 CmdInvalid,
216 CmdBuild,217 CmdBuild,
218 CmdBuiltin,
217 CmdRun,219 CmdRun,
218 CmdTest,220 CmdTest,
219 CmdVersion,221 CmdVersion,
...@@ -664,6 +666,8 @@ int main(int argc, char **argv) {...@@ -664,6 +666,8 @@ int main(int argc, char **argv) {
664 out_type = OutTypeExe;666 out_type = OutTypeExe;
665 } else if (strcmp(arg, "targets") == 0) {667 } else if (strcmp(arg, "targets") == 0) {
666 cmd = CmdTargets;668 cmd = CmdTargets;
669 } else if (strcmp(arg, "builtin") == 0) {
670 cmd = CmdBuiltin;
667 } else {671 } else {
668 fprintf(stderr, "Unrecognized command: %s\n", arg);672 fprintf(stderr, "Unrecognized command: %s\n", arg);
669 return usage(arg0);673 return usage(arg0);
...@@ -681,6 +685,7 @@ int main(int argc, char **argv) {...@@ -681,6 +685,7 @@ int main(int argc, char **argv) {
681 return usage(arg0);685 return usage(arg0);
682 }686 }
683 break;687 break;
688 case CmdBuiltin:
684 case CmdVersion:689 case CmdVersion:
685 case CmdZen:690 case CmdZen:
686 case CmdTargets:691 case CmdTargets:
...@@ -727,6 +732,16 @@ int main(int argc, char **argv) {...@@ -727,6 +732,16 @@ int main(int argc, char **argv) {
727 }732 }
728733
729 switch (cmd) {734 switch (cmd) {
735 case CmdBuiltin: {
736 Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
737 CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, zig_lib_dir_buf);
738 Buf *builtin_source = codegen_generate_builtin_source(g);
739 if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
740 fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
741 return EXIT_FAILURE;
742 }
743 return EXIT_SUCCESS;
744 }
730 case CmdRun:745 case CmdRun:
731 case CmdBuild:746 case CmdBuild:
732 case CmdTranslateC:747 case CmdTranslateC:
src/parser.cpp+16-8
...@@ -1046,12 +1046,11 @@ static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index...@@ -1046,12 +1046,11 @@ static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index
1046}1046}
10471047
1048/*1048/*
1049SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | PtrDerefExpression | SliceExpression)1049SuffixOpExpression = ("async" option("&lt;" SuffixOpExpression "&gt;") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ".*" | ".?")
1050FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)1050FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
1051ArrayAccessExpression : token(LBracket) Expression token(RBracket)1051ArrayAccessExpression : token(LBracket) Expression token(RBracket)
1052SliceExpression = "[" Expression ".." option(Expression) "]"1052SliceExpression = "[" Expression ".." option(Expression) "]"
1053FieldAccessExpression : token(Dot) token(Symbol)1053FieldAccessExpression : token(Dot) token(Symbol)
1054PtrDerefExpression = ".*"
1055StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression1054StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression
1056*/1055*/
1057static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) {1056static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
...@@ -1148,6 +1147,13 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,...@@ -1148,6 +1147,13 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,
1148 AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token);1147 AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token);
1149 node->data.ptr_deref_expr.target = primary_expr;1148 node->data.ptr_deref_expr.target = primary_expr;
11501149
1150 primary_expr = node;
1151 } else if (token->id == TokenIdQuestion) {
1152 *token_index += 1;
1153
1154 AstNode *node = ast_create_node(pc, NodeTypeUnwrapOptional, first_token);
1155 node->data.unwrap_optional.expr = primary_expr;
1156
1151 primary_expr = node;1157 primary_expr = node;
1152 } else {1158 } else {
1153 ast_invalid_token_error(pc, token);1159 ast_invalid_token_error(pc, token);
...@@ -1165,8 +1171,7 @@ static PrefixOp tok_to_prefix_op(Token *token) {...@@ -1165,8 +1171,7 @@ static PrefixOp tok_to_prefix_op(Token *token) {
1165 case TokenIdDash: return PrefixOpNegation;1171 case TokenIdDash: return PrefixOpNegation;
1166 case TokenIdMinusPercent: return PrefixOpNegationWrap;1172 case TokenIdMinusPercent: return PrefixOpNegationWrap;
1167 case TokenIdTilde: return PrefixOpBinNot;1173 case TokenIdTilde: return PrefixOpBinNot;
1168 case TokenIdMaybe: return PrefixOpMaybe;1174 case TokenIdQuestion: return PrefixOpOptional;
1169 case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe;
1170 case TokenIdAmpersand: return PrefixOpAddrOf;1175 case TokenIdAmpersand: return PrefixOpAddrOf;
1171 default: return PrefixOpInvalid;1176 default: return PrefixOpInvalid;
1172 }1177 }
...@@ -2304,8 +2309,8 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, size_t *token_index, bool ma...@@ -2304,8 +2309,8 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, size_t *token_index, bool ma
2304}2309}
23052310
2306/*2311/*
2307UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression2312UnwrapExpression : BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression
2308UnwrapMaybe : "??" BoolOrExpression2313UnwrapOptional = "orelse" Expression
2309UnwrapError = "catch" option("|" Symbol "|") Expression2314UnwrapError = "catch" option("|" Symbol "|") Expression
2310*/2315*/
2311static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) {2316static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
...@@ -2315,14 +2320,14 @@ static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, boo...@@ -2315,14 +2320,14 @@ static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, boo
23152320
2316 Token *token = &pc->tokens->at(*token_index);2321 Token *token = &pc->tokens->at(*token_index);
23172322
2318 if (token->id == TokenIdDoubleQuestion) {2323 if (token->id == TokenIdKeywordOrElse) {
2319 *token_index += 1;2324 *token_index += 1;
23202325
2321 AstNode *rhs = ast_parse_expression(pc, token_index, true);2326 AstNode *rhs = ast_parse_expression(pc, token_index, true);
23222327
2323 AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);2328 AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
2324 node->data.bin_op_expr.op1 = lhs;2329 node->data.bin_op_expr.op1 = lhs;
2325 node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe;2330 node->data.bin_op_expr.bin_op = BinOpTypeUnwrapOptional;
2326 node->data.bin_op_expr.op2 = rhs;2331 node->data.bin_op_expr.op2 = rhs;
23272332
2328 return node;2333 return node;
...@@ -3028,6 +3033,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont...@@ -3028,6 +3033,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
3028 case NodeTypePtrDeref:3033 case NodeTypePtrDeref:
3029 visit_field(&node->data.ptr_deref_expr.target, visit, context);3034 visit_field(&node->data.ptr_deref_expr.target, visit, context);
3030 break;3035 break;
3036 case NodeTypeUnwrapOptional:
3037 visit_field(&node->data.unwrap_optional.expr, visit, context);
3038 break;
3031 case NodeTypeUse:3039 case NodeTypeUse:
3032 visit_field(&node->data.use.expr, visit, context);3040 visit_field(&node->data.use.expr, visit, context);
3033 break;3041 break;
src/target.cpp+5-2
...@@ -596,12 +596,15 @@ void resolve_target_object_format(ZigTarget *target) {...@@ -596,12 +596,15 @@ void resolve_target_object_format(ZigTarget *target) {
596 case ZigLLVM_tce:596 case ZigLLVM_tce:
597 case ZigLLVM_tcele:597 case ZigLLVM_tcele:
598 case ZigLLVM_thumbeb:598 case ZigLLVM_thumbeb:
599 case ZigLLVM_wasm32:
600 case ZigLLVM_wasm64:
601 case ZigLLVM_xcore:599 case ZigLLVM_xcore:
602 target->oformat= ZigLLVM_ELF;600 target->oformat= ZigLLVM_ELF;
603 return;601 return;
604602
603 case ZigLLVM_wasm32:
604 case ZigLLVM_wasm64:
605 target->oformat = ZigLLVM_Wasm;
606 return;
607
605 case ZigLLVM_ppc:608 case ZigLLVM_ppc:
606 case ZigLLVM_ppc64:609 case ZigLLVM_ppc64:
607 if (is_os_darwin(target)) {610 if (is_os_darwin(target)) {
src/tokenizer.cpp+7-28
...@@ -134,6 +134,7 @@ static const struct ZigKeyword zig_keywords[] = {...@@ -134,6 +134,7 @@ static const struct ZigKeyword zig_keywords[] = {
134 {"noalias", TokenIdKeywordNoAlias},134 {"noalias", TokenIdKeywordNoAlias},
135 {"null", TokenIdKeywordNull},135 {"null", TokenIdKeywordNull},
136 {"or", TokenIdKeywordOr},136 {"or", TokenIdKeywordOr},
137 {"orelse", TokenIdKeywordOrElse},
137 {"packed", TokenIdKeywordPacked},138 {"packed", TokenIdKeywordPacked},
138 {"promise", TokenIdKeywordPromise},139 {"promise", TokenIdKeywordPromise},
139 {"pub", TokenIdKeywordPub},140 {"pub", TokenIdKeywordPub},
...@@ -215,7 +216,6 @@ enum TokenizeState {...@@ -215,7 +216,6 @@ enum TokenizeState {
215 TokenizeStateSawGreaterThanGreaterThan,216 TokenizeStateSawGreaterThanGreaterThan,
216 TokenizeStateSawDot,217 TokenizeStateSawDot,
217 TokenizeStateSawDotDot,218 TokenizeStateSawDotDot,
218 TokenizeStateSawQuestionMark,
219 TokenizeStateSawAtSign,219 TokenizeStateSawAtSign,
220 TokenizeStateCharCode,220 TokenizeStateCharCode,
221 TokenizeStateError,221 TokenizeStateError,
...@@ -532,6 +532,10 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -532,6 +532,10 @@ void tokenize(Buf *buf, Tokenization *out) {
532 begin_token(&t, TokenIdComma);532 begin_token(&t, TokenIdComma);
533 end_token(&t);533 end_token(&t);
534 break;534 break;
535 case '?':
536 begin_token(&t, TokenIdQuestion);
537 end_token(&t);
538 break;
535 case '{':539 case '{':
536 begin_token(&t, TokenIdLBrace);540 begin_token(&t, TokenIdLBrace);
537 end_token(&t);541 end_token(&t);
...@@ -624,33 +628,10 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -624,33 +628,10 @@ void tokenize(Buf *buf, Tokenization *out) {
624 begin_token(&t, TokenIdDot);628 begin_token(&t, TokenIdDot);
625 t.state = TokenizeStateSawDot;629 t.state = TokenizeStateSawDot;
626 break;630 break;
627 case '?':
628 begin_token(&t, TokenIdMaybe);
629 t.state = TokenizeStateSawQuestionMark;
630 break;
631 default:631 default:
632 invalid_char_error(&t, c);632 invalid_char_error(&t, c);
633 }633 }
634 break;634 break;
635 case TokenizeStateSawQuestionMark:
636 switch (c) {
637 case '?':
638 set_token_id(&t, t.cur_tok, TokenIdDoubleQuestion);
639 end_token(&t);
640 t.state = TokenizeStateStart;
641 break;
642 case '=':
643 set_token_id(&t, t.cur_tok, TokenIdMaybeAssign);
644 end_token(&t);
645 t.state = TokenizeStateStart;
646 break;
647 default:
648 t.pos -= 1;
649 end_token(&t);
650 t.state = TokenizeStateStart;
651 continue;
652 }
653 break;
654 case TokenizeStateSawDot:635 case TokenizeStateSawDot:
655 switch (c) {636 switch (c) {
656 case '.':637 case '.':
...@@ -1485,7 +1466,6 @@ void tokenize(Buf *buf, Tokenization *out) {...@@ -1485,7 +1466,6 @@ void tokenize(Buf *buf, Tokenization *out) {
1485 case TokenizeStateSawGreaterThan:1466 case TokenizeStateSawGreaterThan:
1486 case TokenizeStateSawGreaterThanGreaterThan:1467 case TokenizeStateSawGreaterThanGreaterThan:
1487 case TokenizeStateSawDot:1468 case TokenizeStateSawDot:
1488 case TokenizeStateSawQuestionMark:
1489 case TokenizeStateSawAtSign:1469 case TokenizeStateSawAtSign:
1490 case TokenizeStateSawStarPercent:1470 case TokenizeStateSawStarPercent:
1491 case TokenizeStateSawPlusPercent:1471 case TokenizeStateSawPlusPercent:
...@@ -1550,7 +1530,6 @@ const char * token_name(TokenId id) {...@@ -1550,7 +1530,6 @@ const char * token_name(TokenId id) {
1550 case TokenIdDash: return "-";1530 case TokenIdDash: return "-";
1551 case TokenIdDivEq: return "/=";1531 case TokenIdDivEq: return "/=";
1552 case TokenIdDot: return ".";1532 case TokenIdDot: return ".";
1553 case TokenIdDoubleQuestion: return "??";
1554 case TokenIdEllipsis2: return "..";1533 case TokenIdEllipsis2: return "..";
1555 case TokenIdEllipsis3: return "...";1534 case TokenIdEllipsis3: return "...";
1556 case TokenIdEof: return "EOF";1535 case TokenIdEof: return "EOF";
...@@ -1587,6 +1566,7 @@ const char * token_name(TokenId id) {...@@ -1587,6 +1566,7 @@ const char * token_name(TokenId id) {
1587 case TokenIdKeywordNoAlias: return "noalias";1566 case TokenIdKeywordNoAlias: return "noalias";
1588 case TokenIdKeywordNull: return "null";1567 case TokenIdKeywordNull: return "null";
1589 case TokenIdKeywordOr: return "or";1568 case TokenIdKeywordOr: return "or";
1569 case TokenIdKeywordOrElse: return "orelse";
1590 case TokenIdKeywordPacked: return "packed";1570 case TokenIdKeywordPacked: return "packed";
1591 case TokenIdKeywordPromise: return "promise";1571 case TokenIdKeywordPromise: return "promise";
1592 case TokenIdKeywordPub: return "pub";1572 case TokenIdKeywordPub: return "pub";
...@@ -1609,8 +1589,7 @@ const char * token_name(TokenId id) {...@@ -1609,8 +1589,7 @@ const char * token_name(TokenId id) {
1609 case TokenIdLBrace: return "{";1589 case TokenIdLBrace: return "{";
1610 case TokenIdLBracket: return "[";1590 case TokenIdLBracket: return "[";
1611 case TokenIdLParen: return "(";1591 case TokenIdLParen: return "(";
1612 case TokenIdMaybe: return "?";1592 case TokenIdQuestion: return "?";
1613 case TokenIdMaybeAssign: return "?=";
1614 case TokenIdMinusEq: return "-=";1593 case TokenIdMinusEq: return "-=";
1615 case TokenIdMinusPercent: return "-%";1594 case TokenIdMinusPercent: return "-%";
1616 case TokenIdMinusPercentEq: return "-%=";1595 case TokenIdMinusPercentEq: return "-%=";
src/tokenizer.hpp+4-3
...@@ -41,7 +41,6 @@ enum TokenId {...@@ -41,7 +41,6 @@ enum TokenId {
41 TokenIdDash,41 TokenIdDash,
42 TokenIdDivEq,42 TokenIdDivEq,
43 TokenIdDot,43 TokenIdDot,
44 TokenIdDoubleQuestion,
45 TokenIdEllipsis2,44 TokenIdEllipsis2,
46 TokenIdEllipsis3,45 TokenIdEllipsis3,
47 TokenIdEof,46 TokenIdEof,
...@@ -76,6 +75,7 @@ enum TokenId {...@@ -76,6 +75,7 @@ enum TokenId {
76 TokenIdKeywordNoAlias,75 TokenIdKeywordNoAlias,
77 TokenIdKeywordNull,76 TokenIdKeywordNull,
78 TokenIdKeywordOr,77 TokenIdKeywordOr,
78 TokenIdKeywordOrElse,
79 TokenIdKeywordPacked,79 TokenIdKeywordPacked,
80 TokenIdKeywordPromise,80 TokenIdKeywordPromise,
81 TokenIdKeywordPub,81 TokenIdKeywordPub,
...@@ -100,8 +100,7 @@ enum TokenId {...@@ -100,8 +100,7 @@ enum TokenId {
100 TokenIdLBrace,100 TokenIdLBrace,
101 TokenIdLBracket,101 TokenIdLBracket,
102 TokenIdLParen,102 TokenIdLParen,
103 TokenIdMaybe,103 TokenIdQuestion,
104 TokenIdMaybeAssign,
105 TokenIdMinusEq,104 TokenIdMinusEq,
106 TokenIdMinusPercent,105 TokenIdMinusPercent,
107 TokenIdMinusPercentEq,106 TokenIdMinusPercentEq,
...@@ -170,6 +169,8 @@ struct Token {...@@ -170,6 +169,8 @@ struct Token {
170 TokenCharLit char_lit;169 TokenCharLit char_lit;
171 } data;170 } data;
172};171};
172// work around conflicting name Token which is also found in libclang
173typedef Token ZigToken;
173174
174struct Tokenization {175struct Tokenization {
175 ZigList<Token> *tokens;176 ZigList<Token> *tokens;
src/translate_c.cpp+45-14
...@@ -260,6 +260,12 @@ static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *ch...@@ -260,6 +260,12 @@ static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *ch
260 return node;260 return node;
261}261}
262262
263static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child_node) {
264 AstNode *node = trans_create_node(c, NodeTypeUnwrapOptional);
265 node->data.unwrap_optional.expr = child_node;
266 return node;
267}
268
263static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpType op, AstNode *rhs_node) {269static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpType op, AstNode *rhs_node) {
264 AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);270 AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
265 node->data.bin_op_expr.op1 = lhs_node;271 node->data.bin_op_expr.op1 = lhs_node;
...@@ -276,8 +282,11 @@ static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNod...@@ -276,8 +282,11 @@ static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNod
276 node);282 node);
277}283}
278284
279static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node) {285static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node, PtrLen ptr_len) {
280 AstNode *node = trans_create_node(c, NodeTypePointerType);286 AstNode *node = trans_create_node(c, NodeTypePointerType);
287 node->data.pointer_type.star_token = allocate<ZigToken>(1);
288 node->data.pointer_type.star_token->id = (ptr_len == PtrLenSingle) ? TokenIdStar: TokenIdBracketStarBracket;
289 node->data.pointer_type.is_const = is_const;
281 node->data.pointer_type.is_const = is_const;290 node->data.pointer_type.is_const = is_const;
282 node->data.pointer_type.is_volatile = is_volatile;291 node->data.pointer_type.is_volatile = is_volatile;
283 node->data.pointer_type.op_expr = child_node;292 node->data.pointer_type.op_expr = child_node;
...@@ -379,7 +388,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r...@@ -379,7 +388,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r
379 fn_def->data.fn_def.fn_proto = fn_proto;388 fn_def->data.fn_def.fn_proto = fn_proto;
380 fn_proto->data.fn_proto.fn_def_node = fn_def;389 fn_proto->data.fn_proto.fn_def_node = fn_def;
381390
382 AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, ref_node);391 AstNode *unwrap_node = trans_create_node_unwrap_null(c, ref_node);
383 AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr);392 AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr);
384 fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node;393 fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node;
385394
...@@ -406,10 +415,6 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r...@@ -406,10 +415,6 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r
406 return fn_def;415 return fn_def;
407}416}
408417
409static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child) {
410 return trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, child);
411}
412
413static AstNode *get_global(Context *c, Buf *name) {418static AstNode *get_global(Context *c, Buf *name) {
414 {419 {
415 auto entry = c->global_table.maybe_get(name);420 auto entry = c->global_table.maybe_get(name);
...@@ -731,6 +736,30 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) {...@@ -731,6 +736,30 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) {
731 }736 }
732}737}
733738
739static bool type_is_opaque(Context *c, const Type *ty, const SourceLocation &source_loc) {
740 switch (ty->getTypeClass()) {
741 case Type::Builtin: {
742 const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(ty);
743 return builtin_ty->getKind() == BuiltinType::Void;
744 }
745 case Type::Record: {
746 const RecordType *record_ty = static_cast<const RecordType*>(ty);
747 return record_ty->getDecl()->getDefinition() == nullptr;
748 }
749 case Type::Elaborated: {
750 const ElaboratedType *elaborated_ty = static_cast<const ElaboratedType*>(ty);
751 return type_is_opaque(c, elaborated_ty->getNamedType().getTypePtr(), source_loc);
752 }
753 case Type::Typedef: {
754 const TypedefType *typedef_ty = static_cast<const TypedefType*>(ty);
755 const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
756 return type_is_opaque(c, typedef_decl->getUnderlyingType().getTypePtr(), source_loc);
757 }
758 default:
759 return false;
760 }
761}
762
734static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) {763static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) {
735 switch (ty->getTypeClass()) {764 switch (ty->getTypeClass()) {
736 case Type::Builtin:765 case Type::Builtin:
...@@ -859,12 +888,14 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou...@@ -859,12 +888,14 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
859 }888 }
860889
861 if (qual_type_child_is_fn_proto(child_qt)) {890 if (qual_type_child_is_fn_proto(child_qt)) {
862 return trans_create_node_prefix_op(c, PrefixOpMaybe, child_node);891 return trans_create_node_prefix_op(c, PrefixOpOptional, child_node);
863 }892 }
864893
894 PtrLen ptr_len = type_is_opaque(c, child_qt.getTypePtr(), source_loc) ? PtrLenSingle : PtrLenUnknown;
895
865 AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),896 AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
866 child_qt.isVolatileQualified(), child_node);897 child_qt.isVolatileQualified(), child_node, ptr_len);
867 return trans_create_node_prefix_op(c, PrefixOpMaybe, pointer_node);898 return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node);
868 }899 }
869 case Type::Typedef:900 case Type::Typedef:
870 {901 {
...@@ -1048,7 +1079,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou...@@ -1048,7 +1079,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
1048 return nullptr;1079 return nullptr;
1049 }1080 }
1050 AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),1081 AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
1051 child_qt.isVolatileQualified(), child_type_node);1082 child_qt.isVolatileQualified(), child_type_node, PtrLenUnknown);
1052 return pointer_node;1083 return pointer_node;
1053 }1084 }
1054 case Type::BlockPointer:1085 case Type::BlockPointer:
...@@ -1941,7 +1972,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc...@@ -1941,7 +1972,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc
1941 bool is_fn_ptr = qual_type_is_fn_ptr(stmt->getSubExpr()->getType());1972 bool is_fn_ptr = qual_type_is_fn_ptr(stmt->getSubExpr()->getType());
1942 if (is_fn_ptr)1973 if (is_fn_ptr)
1943 return value_node;1974 return value_node;
1944 AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node);1975 AstNode *unwrapped = trans_create_node_unwrap_null(c, value_node);
1945 return trans_create_node_ptr_deref(c, unwrapped);1976 return trans_create_node_ptr_deref(c, unwrapped);
1946 }1977 }
1947 case UO_Plus:1978 case UO_Plus:
...@@ -2572,7 +2603,7 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *...@@ -2572,7 +2603,7 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *
2572 }2603 }
2573 }2604 }
2574 if (callee_node == nullptr) {2605 if (callee_node == nullptr) {
2575 callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, callee_raw_node);2606 callee_node = trans_create_node_unwrap_null(c, callee_raw_node);
2576 }2607 }
2577 } else {2608 } else {
2578 callee_node = callee_raw_node;2609 callee_node = callee_raw_node;
...@@ -4286,7 +4317,7 @@ static AstNode *trans_lookup_ast_maybe_fn(Context *c, AstNode *ref_node) {...@@ -4286,7 +4317,7 @@ static AstNode *trans_lookup_ast_maybe_fn(Context *c, AstNode *ref_node) {
4286 return nullptr;4317 return nullptr;
4287 if (prefix_node->type != NodeTypePrefixOpExpr)4318 if (prefix_node->type != NodeTypePrefixOpExpr)
4288 return nullptr;4319 return nullptr;
4289 if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpMaybe)4320 if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpOptional)
4290 return nullptr;4321 return nullptr;
42914322
4292 AstNode *fn_proto_node = prefix_node->data.prefix_op_expr.primary_expr;4323 AstNode *fn_proto_node = prefix_node->data.prefix_op_expr.primary_expr;
...@@ -4462,7 +4493,7 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t...@@ -4462,7 +4493,7 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t
4462 } else if (first_tok->id == CTokIdAsterisk) {4493 } else if (first_tok->id == CTokIdAsterisk) {
4463 *tok_i += 1;4494 *tok_i += 1;
44644495
4465 node = trans_create_node_ptr_type(c, false, false, node);4496 node = trans_create_node_ptr_type(c, false, false, node, PtrLenUnknown);
4466 } else {4497 } else {
4467 return node;4498 return node;
4468 }4499 }
src/zig_llvm.cpp+1-1
...@@ -853,7 +853,7 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_...@@ -853,7 +853,7 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_
853 return lld::mach_o::link(array_ref_args, diag);853 return lld::mach_o::link(array_ref_args, diag);
854854
855 case ZigLLVM_Wasm:855 case ZigLLVM_Wasm:
856 assert(false); // TODO ZigLLDLink for Wasm856 return lld::wasm::link(array_ref_args, false, diag);
857 }857 }
858 assert(false); // unreachable858 assert(false); // unreachable
859 abort();859 abort();
std/array_list.zig+70-40
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("index.zig");1const std = @import("index.zig");
2const debug = std.debug;2const debug = std.debug;
3const assert = debug.assert;3const assert = debug.assert;
4const assertError = debug.assertError;
4const mem = std.mem;5const mem = std.mem;
5const Allocator = mem.Allocator;6const Allocator = mem.Allocator;
67
...@@ -28,20 +29,33 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {...@@ -28,20 +29,33 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
28 };29 };
29 }30 }
3031
31 pub fn deinit(l: *const Self) void {32 pub fn deinit(self: *const Self) void {
32 l.allocator.free(l.items);33 self.allocator.free(self.items);
33 }34 }
3435
35 pub fn toSlice(l: *const Self) []align(A) T {36 pub fn toSlice(self: *const Self) []align(A) T {
36 return l.items[0..l.len];37 return self.items[0..self.len];
37 }38 }
3839
39 pub fn toSliceConst(l: *const Self) []align(A) const T {40 pub fn toSliceConst(self: *const Self) []align(A) const T {
40 return l.items[0..l.len];41 return self.items[0..self.len];
41 }42 }
4243
43 pub fn at(l: *const Self, n: usize) T {44 pub fn at(self: *const Self, n: usize) T {
44 return l.toSliceConst()[n];45 return self.toSliceConst()[n];
46 }
47
48 /// Sets the value at index `i`, or returns `error.OutOfBounds` if
49 /// the index is not in range.
50 pub fn setOrError(self: *const Self, i: usize, item: *const T) !void {
51 if (i >= self.len) return error.OutOfBounds;
52 self.items[i] = item.*;
53 }
54
55 /// Sets the value at index `i`, asserting that the value is in range.
56 pub fn set(self: *const Self, i: usize, item: *const T) void {
57 assert(i < self.len);
58 self.items[i] = item.*;
45 }59 }
4660
47 pub fn count(self: *const Self) usize {61 pub fn count(self: *const Self) usize {
...@@ -67,58 +81,58 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {...@@ -67,58 +81,58 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
67 return result;81 return result;
68 }82 }
6983
70 pub fn insert(l: *Self, n: usize, item: *const T) !void {84 pub fn insert(self: *Self, n: usize, item: *const T) !void {
71 try l.ensureCapacity(l.len + 1);85 try self.ensureCapacity(self.len + 1);
72 l.len += 1;86 self.len += 1;
7387
74 mem.copy(T, l.items[n + 1 .. l.len], l.items[n .. l.len - 1]);88 mem.copy(T, self.items[n + 1 .. self.len], self.items[n .. self.len - 1]);
75 l.items[n] = item.*;89 self.items[n] = item.*;
76 }90 }
7791
78 pub fn insertSlice(l: *Self, n: usize, items: []align(A) const T) !void {92 pub fn insertSlice(self: *Self, n: usize, items: []align(A) const T) !void {
79 try l.ensureCapacity(l.len + items.len);93 try self.ensureCapacity(self.len + items.len);
80 l.len += items.len;94 self.len += items.len;
8195
82 mem.copy(T, l.items[n + items.len .. l.len], l.items[n .. l.len - items.len]);96 mem.copy(T, self.items[n + items.len .. self.len], self.items[n .. self.len - items.len]);
83 mem.copy(T, l.items[n .. n + items.len], items);97 mem.copy(T, self.items[n .. n + items.len], items);
84 }98 }
8599
86 pub fn append(l: *Self, item: *const T) !void {100 pub fn append(self: *Self, item: *const T) !void {
87 const new_item_ptr = try l.addOne();101 const new_item_ptr = try self.addOne();
88 new_item_ptr.* = item.*;102 new_item_ptr.* = item.*;
89 }103 }
90104
91 pub fn appendSlice(l: *Self, items: []align(A) const T) !void {105 pub fn appendSlice(self: *Self, items: []align(A) const T) !void {
92 try l.ensureCapacity(l.len + items.len);106 try self.ensureCapacity(self.len + items.len);
93 mem.copy(T, l.items[l.len..], items);107 mem.copy(T, self.items[self.len..], items);
94 l.len += items.len;108 self.len += items.len;
95 }109 }
96110
97 pub fn resize(l: *Self, new_len: usize) !void {111 pub fn resize(self: *Self, new_len: usize) !void {
98 try l.ensureCapacity(new_len);112 try self.ensureCapacity(new_len);
99 l.len = new_len;113 self.len = new_len;
100 }114 }
101115
102 pub fn shrink(l: *Self, new_len: usize) void {116 pub fn shrink(self: *Self, new_len: usize) void {
103 assert(new_len <= l.len);117 assert(new_len <= self.len);
104 l.len = new_len;118 self.len = new_len;
105 }119 }
106120
107 pub fn ensureCapacity(l: *Self, new_capacity: usize) !void {121 pub fn ensureCapacity(self: *Self, new_capacity: usize) !void {
108 var better_capacity = l.items.len;122 var better_capacity = self.items.len;
109 if (better_capacity >= new_capacity) return;123 if (better_capacity >= new_capacity) return;
110 while (true) {124 while (true) {
111 better_capacity += better_capacity / 2 + 8;125 better_capacity += better_capacity / 2 + 8;
112 if (better_capacity >= new_capacity) break;126 if (better_capacity >= new_capacity) break;
113 }127 }
114 l.items = try l.allocator.alignedRealloc(T, A, l.items, better_capacity);128 self.items = try self.allocator.alignedRealloc(T, A, self.items, better_capacity);
115 }129 }
116130
117 pub fn addOne(l: *Self) !*T {131 pub fn addOne(self: *Self) !*T {
118 const new_length = l.len + 1;132 const new_length = self.len + 1;
119 try l.ensureCapacity(new_length);133 try self.ensureCapacity(new_length);
120 const result = &l.items[l.len];134 const result = &self.items[self.len];
121 l.len = new_length;135 self.len = new_length;
122 return result;136 return result;
123 }137 }
124138
...@@ -159,9 +173,15 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {...@@ -159,9 +173,15 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
159}173}
160174
161test "basic ArrayList test" {175test "basic ArrayList test" {
162 var list = ArrayList(i32).init(debug.global_allocator);176 var bytes: [1024]u8 = undefined;
177 const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
178
179 var list = ArrayList(i32).init(allocator);
163 defer list.deinit();180 defer list.deinit();
164181
182 // setting on empty list is out of bounds
183 assertError(list.setOrError(0, 1), error.OutOfBounds);
184
165 {185 {
166 var i: usize = 0;186 var i: usize = 0;
167 while (i < 10) : (i += 1) {187 while (i < 10) : (i += 1) {
...@@ -200,6 +220,16 @@ test "basic ArrayList test" {...@@ -200,6 +220,16 @@ test "basic ArrayList test" {
200220
201 list.appendSlice([]const i32{}) catch unreachable;221 list.appendSlice([]const i32{}) catch unreachable;
202 assert(list.len == 9);222 assert(list.len == 9);
223
224 // can only set on indices < self.len
225 list.set(7, 33);
226 list.set(8, 42);
227
228 assertError(list.setOrError(9, 99), error.OutOfBounds);
229 assertError(list.setOrError(10, 123), error.OutOfBounds);
230
231 assert(list.pop() == 42);
232 assert(list.pop() == 33);
203}233}
204234
205test "iterator ArrayList test" {235test "iterator ArrayList test" {
...@@ -228,7 +258,7 @@ test "iterator ArrayList test" {...@@ -228,7 +258,7 @@ test "iterator ArrayList test" {
228 }258 }
229259
230 it.reset();260 it.reset();
231 assert(??it.next() == 1);261 assert(it.next().? == 1);
232}262}
233263
234test "insert ArrayList test" {264test "insert ArrayList test" {
std/atomic/queue.zig+17-8
...@@ -33,8 +33,8 @@ pub fn Queue(comptime T: type) type {...@@ -33,8 +33,8 @@ pub fn Queue(comptime T: type) type {
33 pub fn get(self: *Self) ?*Node {33 pub fn get(self: *Self) ?*Node {
34 var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst);34 var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst);
35 while (true) {35 while (true) {
36 const node = head.next ?? return null;36 const node = head.next orelse return null;
37 head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return node;37 head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return node;
38 }38 }
39 }39 }
40 };40 };
...@@ -94,8 +94,18 @@ test "std.atomic.queue" {...@@ -94,8 +94,18 @@ test "std.atomic.queue" {
94 for (getters) |t|94 for (getters) |t|
95 t.wait();95 t.wait();
9696
97 std.debug.assert(context.put_sum == context.get_sum);97 if (context.put_sum != context.get_sum) {
98 std.debug.assert(context.get_count == puts_per_thread * put_thread_count);98 std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
99 }
100
101 if (context.get_count != puts_per_thread * put_thread_count) {
102 std.debug.panic(
103 "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}",
104 context.get_count,
105 u32(puts_per_thread),
106 u32(put_thread_count),
107 );
108 }
99}109}
100110
101fn startPuts(ctx: *Context) u8 {111fn startPuts(ctx: *Context) u8 {
...@@ -114,15 +124,14 @@ fn startPuts(ctx: *Context) u8 {...@@ -114,15 +124,14 @@ fn startPuts(ctx: *Context) u8 {
114124
115fn startGets(ctx: *Context) u8 {125fn startGets(ctx: *Context) u8 {
116 while (true) {126 while (true) {
127 const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1;
128
117 while (ctx.queue.get()) |node| {129 while (ctx.queue.get()) |node| {
118 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz130 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
119 _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);131 _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
120 _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);132 _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
121 }133 }
122134
123 if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {135 if (last) return 0;
124 break;
125 }
126 }136 }
127 return 0;
128}137}
std/atomic/stack.zig+17-8
...@@ -28,14 +28,14 @@ pub fn Stack(comptime T: type) type {...@@ -28,14 +28,14 @@ pub fn Stack(comptime T: type) type {
28 var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst);28 var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst);
29 while (true) {29 while (true) {
30 node.next = root;30 node.next = root;
31 root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break;31 root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse break;
32 }32 }
33 }33 }
3434
35 pub fn pop(self: *Self) ?*Node {35 pub fn pop(self: *Self) ?*Node {
36 var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst);36 var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst);
37 while (true) {37 while (true) {
38 root = @cmpxchgWeak(?*Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root;38 root = @cmpxchgWeak(?*Node, &self.root, root, (root orelse return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return root;
39 }39 }
40 }40 }
4141
...@@ -97,8 +97,18 @@ test "std.atomic.stack" {...@@ -97,8 +97,18 @@ test "std.atomic.stack" {
97 for (getters) |t|97 for (getters) |t|
98 t.wait();98 t.wait();
9999
100 std.debug.assert(context.put_sum == context.get_sum);100 if (context.put_sum != context.get_sum) {
101 std.debug.assert(context.get_count == puts_per_thread * put_thread_count);101 std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
102 }
103
104 if (context.get_count != puts_per_thread * put_thread_count) {
105 std.debug.panic(
106 "failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}",
107 context.get_count,
108 u32(puts_per_thread),
109 u32(put_thread_count),
110 );
111 }
102}112}
103113
104fn startPuts(ctx: *Context) u8 {114fn startPuts(ctx: *Context) u8 {
...@@ -117,15 +127,14 @@ fn startPuts(ctx: *Context) u8 {...@@ -117,15 +127,14 @@ fn startPuts(ctx: *Context) u8 {
117127
118fn startGets(ctx: *Context) u8 {128fn startGets(ctx: *Context) u8 {
119 while (true) {129 while (true) {
130 const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1;
131
120 while (ctx.stack.pop()) |node| {132 while (ctx.stack.pop()) |node| {
121 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz133 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
122 _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);134 _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
123 _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);135 _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
124 }136 }
125137
126 if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {138 if (last) return 0;
127 break;
128 }
129 }139 }
130 return 0;
131}140}
std/buf_map.zig+6-6
...@@ -19,7 +19,7 @@ pub const BufMap = struct {...@@ -19,7 +19,7 @@ pub const BufMap = struct {
19 pub fn deinit(self: *const BufMap) void {19 pub fn deinit(self: *const BufMap) void {
20 var it = self.hash_map.iterator();20 var it = self.hash_map.iterator();
21 while (true) {21 while (true) {
22 const entry = it.next() ?? break;22 const entry = it.next() orelse break;
23 self.free(entry.key);23 self.free(entry.key);
24 self.free(entry.value);24 self.free(entry.value);
25 }25 }
...@@ -37,12 +37,12 @@ pub const BufMap = struct {...@@ -37,12 +37,12 @@ pub const BufMap = struct {
37 }37 }
3838
39 pub fn get(self: *const BufMap, key: []const u8) ?[]const u8 {39 pub fn get(self: *const BufMap, key: []const u8) ?[]const u8 {
40 const entry = self.hash_map.get(key) ?? return null;40 const entry = self.hash_map.get(key) orelse return null;
41 return entry.value;41 return entry.value;
42 }42 }
4343
44 pub fn delete(self: *BufMap, key: []const u8) void {44 pub fn delete(self: *BufMap, key: []const u8) void {
45 const entry = self.hash_map.remove(key) ?? return;45 const entry = self.hash_map.remove(key) orelse return;
46 self.free(entry.key);46 self.free(entry.key);
47 self.free(entry.value);47 self.free(entry.value);
48 }48 }
...@@ -72,15 +72,15 @@ test "BufMap" {...@@ -72,15 +72,15 @@ test "BufMap" {
72 defer bufmap.deinit();72 defer bufmap.deinit();
7373
74 try bufmap.set("x", "1");74 try bufmap.set("x", "1");
75 assert(mem.eql(u8, ??bufmap.get("x"), "1"));75 assert(mem.eql(u8, bufmap.get("x").?, "1"));
76 assert(1 == bufmap.count());76 assert(1 == bufmap.count());
7777
78 try bufmap.set("x", "2");78 try bufmap.set("x", "2");
79 assert(mem.eql(u8, ??bufmap.get("x"), "2"));79 assert(mem.eql(u8, bufmap.get("x").?, "2"));
80 assert(1 == bufmap.count());80 assert(1 == bufmap.count());
8181
82 try bufmap.set("x", "3");82 try bufmap.set("x", "3");
83 assert(mem.eql(u8, ??bufmap.get("x"), "3"));83 assert(mem.eql(u8, bufmap.get("x").?, "3"));
84 assert(1 == bufmap.count());84 assert(1 == bufmap.count());
8585
86 bufmap.delete("x");86 bufmap.delete("x");
std/buf_set.zig+2-2
...@@ -17,7 +17,7 @@ pub const BufSet = struct {...@@ -17,7 +17,7 @@ pub const BufSet = struct {
17 pub fn deinit(self: *const BufSet) void {17 pub fn deinit(self: *const BufSet) void {
18 var it = self.hash_map.iterator();18 var it = self.hash_map.iterator();
19 while (true) {19 while (true) {
20 const entry = it.next() ?? break;20 const entry = it.next() orelse break;
21 self.free(entry.key);21 self.free(entry.key);
22 }22 }
2323
...@@ -33,7 +33,7 @@ pub const BufSet = struct {...@@ -33,7 +33,7 @@ pub const BufSet = struct {
33 }33 }
3434
35 pub fn delete(self: *BufSet, key: []const u8) void {35 pub fn delete(self: *BufSet, key: []const u8) void {
36 const entry = self.hash_map.remove(key) ?? return;36 const entry = self.hash_map.remove(key) orelse return;
37 self.free(entry.key);37 self.free(entry.key);
38 }38 }
3939
std/buffer.zig+3-4
...@@ -28,7 +28,6 @@ pub const Buffer = struct {...@@ -28,7 +28,6 @@ pub const Buffer = struct {
28 /// Must deinitialize with deinit.28 /// Must deinitialize with deinit.
29 /// None of the other operations are valid until you do one of these:29 /// None of the other operations are valid until you do one of these:
30 /// * ::replaceContents30 /// * ::replaceContents
31 /// * ::replaceContentsBuffer
32 /// * ::resize31 /// * ::resize
33 pub fn initNull(allocator: *Allocator) Buffer {32 pub fn initNull(allocator: *Allocator) Buffer {
34 return Buffer{ .list = ArrayList(u8).init(allocator) };33 return Buffer{ .list = ArrayList(u8).init(allocator) };
...@@ -42,9 +41,9 @@ pub const Buffer = struct {...@@ -42,9 +41,9 @@ pub const Buffer = struct {
42 /// Buffer takes ownership of the passed in slice. The slice must have been41 /// Buffer takes ownership of the passed in slice. The slice must have been
43 /// allocated with `allocator`.42 /// allocated with `allocator`.
44 /// Must deinitialize with deinit.43 /// Must deinitialize with deinit.
45 pub fn fromOwnedSlice(allocator: *Allocator, slice: []u8) Buffer {44 pub fn fromOwnedSlice(allocator: *Allocator, slice: []u8) !Buffer {
46 var self = Buffer{ .list = ArrayList(u8).fromOwnedSlice(allocator, slice) };45 var self = Buffer{ .list = ArrayList(u8).fromOwnedSlice(allocator, slice) };
47 self.list.append(0);46 try self.list.append(0);
48 return self;47 return self;
49 }48 }
5049
...@@ -116,7 +115,7 @@ pub const Buffer = struct {...@@ -116,7 +115,7 @@ pub const Buffer = struct {
116 return mem.eql(u8, self.list.items[start..l], m);115 return mem.eql(u8, self.list.items[start..l], m);
117 }116 }
118117
119 pub fn replaceContents(self: *const Buffer, m: []const u8) !void {118 pub fn replaceContents(self: *Buffer, m: []const u8) !void {
120 try self.resize(m.len);119 try self.resize(m.len);
121 mem.copy(u8, self.list.toSlice(), m);120 mem.copy(u8, self.list.toSlice(), m);
122 }121 }
std/build.zig+21-19
...@@ -136,7 +136,7 @@ pub const Builder = struct {...@@ -136,7 +136,7 @@ pub const Builder = struct {
136 }136 }
137137
138 pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void {138 pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void {
139 self.prefix = maybe_prefix ?? "/usr/local"; // TODO better default139 self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default
140 self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable;140 self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable;
141 self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable;141 self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable;
142 }142 }
...@@ -312,9 +312,9 @@ pub const Builder = struct {...@@ -312,9 +312,9 @@ pub const Builder = struct {
312 if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {312 if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
313 var it = mem.split(nix_cflags_compile, " ");313 var it = mem.split(nix_cflags_compile, " ");
314 while (true) {314 while (true) {
315 const word = it.next() ?? break;315 const word = it.next() orelse break;
316 if (mem.eql(u8, word, "-isystem")) {316 if (mem.eql(u8, word, "-isystem")) {
317 const include_path = it.next() ?? {317 const include_path = it.next() orelse {
318 warn("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n");318 warn("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n");
319 break;319 break;
320 };320 };
...@@ -330,9 +330,9 @@ pub const Builder = struct {...@@ -330,9 +330,9 @@ pub const Builder = struct {
330 if (os.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| {330 if (os.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| {
331 var it = mem.split(nix_ldflags, " ");331 var it = mem.split(nix_ldflags, " ");
332 while (true) {332 while (true) {
333 const word = it.next() ?? break;333 const word = it.next() orelse break;
334 if (mem.eql(u8, word, "-rpath")) {334 if (mem.eql(u8, word, "-rpath")) {
335 const rpath = it.next() ?? {335 const rpath = it.next() orelse {
336 warn("Expected argument after -rpath in NIX_LDFLAGS\n");336 warn("Expected argument after -rpath in NIX_LDFLAGS\n");
337 break;337 break;
338 };338 };
...@@ -362,7 +362,7 @@ pub const Builder = struct {...@@ -362,7 +362,7 @@ pub const Builder = struct {
362 }362 }
363 self.available_options_list.append(available_option) catch unreachable;363 self.available_options_list.append(available_option) catch unreachable;
364364
365 const entry = self.user_input_options.get(name) ?? return null;365 const entry = self.user_input_options.get(name) orelse return null;
366 entry.value.used = true;366 entry.value.used = true;
367 switch (type_id) {367 switch (type_id) {
368 TypeId.Bool => switch (entry.value.value) {368 TypeId.Bool => switch (entry.value.value) {
...@@ -416,9 +416,9 @@ pub const Builder = struct {...@@ -416,9 +416,9 @@ pub const Builder = struct {
416 pub fn standardReleaseOptions(self: *Builder) builtin.Mode {416 pub fn standardReleaseOptions(self: *Builder) builtin.Mode {
417 if (self.release_mode) |mode| return mode;417 if (self.release_mode) |mode| return mode;
418418
419 const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false;419 const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") orelse false;
420 const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false;420 const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") orelse false;
421 const release_small = self.option(bool, "release-small", "size optimizations on and safety off") ?? false;421 const release_small = self.option(bool, "release-small", "size optimizations on and safety off") orelse false;
422422
423 const mode = if (release_safe and !release_fast and !release_small) builtin.Mode.ReleaseSafe else if (release_fast and !release_safe and !release_small) builtin.Mode.ReleaseFast else if (release_small and !release_fast and !release_safe) builtin.Mode.ReleaseSmall else if (!release_fast and !release_safe and !release_small) builtin.Mode.Debug else x: {423 const mode = if (release_safe and !release_fast and !release_small) builtin.Mode.ReleaseSafe else if (release_fast and !release_safe and !release_small) builtin.Mode.ReleaseFast else if (release_small and !release_fast and !release_safe) builtin.Mode.ReleaseSmall else if (!release_fast and !release_safe and !release_small) builtin.Mode.Debug else x: {
424 warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)");424 warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)");
...@@ -518,7 +518,7 @@ pub const Builder = struct {...@@ -518,7 +518,7 @@ pub const Builder = struct {
518 // make sure all args are used518 // make sure all args are used
519 var it = self.user_input_options.iterator();519 var it = self.user_input_options.iterator();
520 while (true) {520 while (true) {
521 const entry = it.next() ?? break;521 const entry = it.next() orelse break;
522 if (!entry.value.used) {522 if (!entry.value.used) {
523 warn("Invalid option: -D{}\n\n", entry.key);523 warn("Invalid option: -D{}\n\n", entry.key);
524 self.markInvalidUserInput();524 self.markInvalidUserInput();
...@@ -617,7 +617,7 @@ pub const Builder = struct {...@@ -617,7 +617,7 @@ pub const Builder = struct {
617 warn("cp {} {}\n", source_path, dest_path);617 warn("cp {} {}\n", source_path, dest_path);
618 }618 }
619619
620 const dirname = os.path.dirname(dest_path);620 const dirname = os.path.dirname(dest_path) orelse ".";
621 const abs_source_path = self.pathFromRoot(source_path);621 const abs_source_path = self.pathFromRoot(source_path);
622 os.makePath(self.allocator, dirname) catch |err| {622 os.makePath(self.allocator, dirname) catch |err| {
623 warn("Unable to create path {}: {}\n", dirname, @errorName(err));623 warn("Unable to create path {}: {}\n", dirname, @errorName(err));
...@@ -1246,7 +1246,7 @@ pub const LibExeObjStep = struct {...@@ -1246,7 +1246,7 @@ pub const LibExeObjStep = struct {
1246 {1246 {
1247 var it = self.link_libs.iterator();1247 var it = self.link_libs.iterator();
1248 while (true) {1248 while (true) {
1249 const entry = it.next() ?? break;1249 const entry = it.next() orelse break;
1250 zig_args.append("--library") catch unreachable;1250 zig_args.append("--library") catch unreachable;
1251 zig_args.append(entry.key) catch unreachable;1251 zig_args.append(entry.key) catch unreachable;
1252 }1252 }
...@@ -1395,8 +1395,9 @@ pub const LibExeObjStep = struct {...@@ -1395,8 +1395,9 @@ pub const LibExeObjStep = struct {
1395 cc_args.append(abs_source_file) catch unreachable;1395 cc_args.append(abs_source_file) catch unreachable;
13961396
1397 const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable;1397 const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable;
1398 const cache_o_dir = os.path.dirname(cache_o_src);1398 if (os.path.dirname(cache_o_src)) |cache_o_dir| {
1399 try builder.makePath(cache_o_dir);1399 try builder.makePath(cache_o_dir);
1400 }
1400 const cache_o_file = builder.fmt("{}{}", cache_o_src, self.target.oFileExt());1401 const cache_o_file = builder.fmt("{}{}", cache_o_src, self.target.oFileExt());
1401 cc_args.append("-o") catch unreachable;1402 cc_args.append("-o") catch unreachable;
1402 cc_args.append(builder.pathFromRoot(cache_o_file)) catch unreachable;1403 cc_args.append(builder.pathFromRoot(cache_o_file)) catch unreachable;
...@@ -1509,8 +1510,9 @@ pub const LibExeObjStep = struct {...@@ -1509,8 +1510,9 @@ pub const LibExeObjStep = struct {
1509 cc_args.append(abs_source_file) catch unreachable;1510 cc_args.append(abs_source_file) catch unreachable;
15101511
1511 const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable;1512 const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable;
1512 const cache_o_dir = os.path.dirname(cache_o_src);1513 if (os.path.dirname(cache_o_src)) |cache_o_dir| {
1513 try builder.makePath(cache_o_dir);1514 try builder.makePath(cache_o_dir);
1515 }
1514 const cache_o_file = builder.fmt("{}{}", cache_o_src, self.target.oFileExt());1516 const cache_o_file = builder.fmt("{}{}", cache_o_src, self.target.oFileExt());
1515 cc_args.append("-o") catch unreachable;1517 cc_args.append("-o") catch unreachable;
1516 cc_args.append(builder.pathFromRoot(cache_o_file)) catch unreachable;1518 cc_args.append(builder.pathFromRoot(cache_o_file)) catch unreachable;
...@@ -1696,7 +1698,7 @@ pub const TestStep = struct {...@@ -1696,7 +1698,7 @@ pub const TestStep = struct {
1696 {1698 {
1697 var it = self.link_libs.iterator();1699 var it = self.link_libs.iterator();
1698 while (true) {1700 while (true) {
1699 const entry = it.next() ?? break;1701 const entry = it.next() orelse break;
1700 try zig_args.append("--library");1702 try zig_args.append("--library");
1701 try zig_args.append(entry.key);1703 try zig_args.append(entry.key);
1702 }1704 }
...@@ -1855,7 +1857,7 @@ pub const WriteFileStep = struct {...@@ -1855,7 +1857,7 @@ pub const WriteFileStep = struct {
1855 fn make(step: *Step) !void {1857 fn make(step: *Step) !void {
1856 const self = @fieldParentPtr(WriteFileStep, "step", step);1858 const self = @fieldParentPtr(WriteFileStep, "step", step);
1857 const full_path = self.builder.pathFromRoot(self.file_path);1859 const full_path = self.builder.pathFromRoot(self.file_path);
1858 const full_path_dir = os.path.dirname(full_path);1860 const full_path_dir = os.path.dirname(full_path) orelse ".";
1859 os.makePath(self.builder.allocator, full_path_dir) catch |err| {1861 os.makePath(self.builder.allocator, full_path_dir) catch |err| {
1860 warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));1862 warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));
1861 return err;1863 return err;
...@@ -1945,7 +1947,7 @@ pub const Step = struct {...@@ -1945,7 +1947,7 @@ pub const Step = struct {
1945};1947};
19461948
1947fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {1949fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
1948 const out_dir = os.path.dirname(output_path);1950 const out_dir = os.path.dirname(output_path) orelse ".";
1949 const out_basename = os.path.basename(output_path);1951 const out_basename = os.path.basename(output_path);
1950 // sym link for libfoo.so.1 to libfoo.so.1.2.31952 // sym link for libfoo.so.1 to libfoo.so.1.2.3
1951 const major_only_path = os.path.join(allocator, out_dir, filename_major_only) catch unreachable;1953 const major_only_path = os.path.join(allocator, out_dir, filename_major_only) catch unreachable;
std/c/index.zig+10-10
...@@ -20,11 +20,11 @@ pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int;...@@ -20,11 +20,11 @@ pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int;
20pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize;20pub 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 stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;24pub 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;25pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize;
26pub extern "c" fn mmap(addr: ?[*]c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?[*]c_void;26pub 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;27pub extern "c" fn munmap(addr: *c_void, len: usize) c_int;
28pub extern "c" fn unlink(path: [*]const u8) c_int;28pub extern "c" fn unlink(path: [*]const u8) c_int;
29pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;29pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;
30pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int;30pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int;
...@@ -48,15 +48,15 @@ pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;...@@ -48,15 +48,15 @@ pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
48pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int;48pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int;
49pub extern "c" fn rmdir(path: [*]const u8) c_int;49pub extern "c" fn rmdir(path: [*]const u8) c_int;
5050
51pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?[*]c_void;51pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void;
52pub extern "c" fn malloc(usize) ?[*]c_void;52pub extern "c" fn malloc(usize) ?*c_void;
53pub extern "c" fn realloc([*]c_void, usize) ?[*]c_void;53pub extern "c" fn realloc(*c_void, usize) ?*c_void;
54pub extern "c" fn free([*]c_void) void;54pub extern "c" fn free(*c_void) void;
55pub extern "c" fn posix_memalign(memptr: *[*]c_void, alignment: usize, size: usize) c_int;55pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int;
5656
57pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int;57pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int;
58pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int;58pub 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;59pub 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;60pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;
61pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int;61pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int;
6262
std/debug/index.zig+20-10
...@@ -88,6 +88,16 @@ pub fn assert(ok: bool) void {...@@ -88,6 +88,16 @@ pub fn assert(ok: bool) void {
88 }88 }
89}89}
9090
91/// TODO: add `==` operator for `error_union == error_set`, and then
92/// remove this function
93pub fn assertError(value: var, expected_error: error) void {
94 if (value) {
95 @panic("expected error");
96 } else |actual_error| {
97 assert(actual_error == expected_error);
98 }
99}
100
91/// Call this function when you want to panic if the condition is not true.101/// Call this function when you want to panic if the condition is not true.
92/// If `ok` is `false`, this function will panic in every release mode.102/// If `ok` is `false`, this function will panic in every release mode.
93pub fn assertOrPanic(ok: bool) void {103pub fn assertOrPanic(ok: bool) void {
...@@ -198,7 +208,7 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us...@@ -198,7 +208,7 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us
198 .name = "???",208 .name = "???",
199 .address = address,209 .address = address,
200 };210 };
201 const symbol = debug_info.symbol_table.search(address) ?? &unknown;211 const symbol = debug_info.symbol_table.search(address) orelse &unknown;
202 try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address);212 try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address);
203 },213 },
204 else => {214 else => {
...@@ -258,10 +268,10 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace {...@@ -258,10 +268,10 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace {
258 try st.elf.openFile(allocator, &st.self_exe_file);268 try st.elf.openFile(allocator, &st.self_exe_file);
259 errdefer st.elf.close();269 errdefer st.elf.close();
260270
261 st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;271 st.debug_info = (try st.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo;
262 st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;272 st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) orelse return error.MissingDebugInfo;
263 st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo;273 st.debug_str = (try st.elf.findSection(".debug_str")) orelse return error.MissingDebugInfo;
264 st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;274 st.debug_line = (try st.elf.findSection(".debug_line")) orelse return error.MissingDebugInfo;
265 st.debug_ranges = (try st.elf.findSection(".debug_ranges"));275 st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
266 try scanAllCompileUnits(st);276 try scanAllCompileUnits(st);
267 return st;277 return st;
...@@ -433,7 +443,7 @@ const Die = struct {...@@ -433,7 +443,7 @@ const Die = struct {
433 }443 }
434444
435 fn getAttrAddr(self: *const Die, id: u64) !u64 {445 fn getAttrAddr(self: *const Die, id: u64) !u64 {
436 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;446 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
437 return switch (form_value.*) {447 return switch (form_value.*) {
438 FormValue.Address => |value| value,448 FormValue.Address => |value| value,
439 else => error.InvalidDebugInfo,449 else => error.InvalidDebugInfo,
...@@ -441,7 +451,7 @@ const Die = struct {...@@ -441,7 +451,7 @@ const Die = struct {
441 }451 }
442452
443 fn getAttrSecOffset(self: *const Die, id: u64) !u64 {453 fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
444 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;454 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
445 return switch (form_value.*) {455 return switch (form_value.*) {
446 FormValue.Const => |value| value.asUnsignedLe(),456 FormValue.Const => |value| value.asUnsignedLe(),
447 FormValue.SecOffset => |value| value,457 FormValue.SecOffset => |value| value,
...@@ -450,7 +460,7 @@ const Die = struct {...@@ -450,7 +460,7 @@ const Die = struct {
450 }460 }
451461
452 fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {462 fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
453 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;463 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
454 return switch (form_value.*) {464 return switch (form_value.*) {
455 FormValue.Const => |value| value.asUnsignedLe(),465 FormValue.Const => |value| value.asUnsignedLe(),
456 else => error.InvalidDebugInfo,466 else => error.InvalidDebugInfo,
...@@ -458,7 +468,7 @@ const Die = struct {...@@ -458,7 +468,7 @@ const Die = struct {
458 }468 }
459469
460 fn getAttrString(self: *const Die, st: *ElfStackTrace, id: u64) ![]u8 {470 fn getAttrString(self: *const Die, st: *ElfStackTrace, id: u64) ![]u8 {
461 const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;471 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
462 return switch (form_value.*) {472 return switch (form_value.*) {
463 FormValue.String => |value| value,473 FormValue.String => |value| value,
464 FormValue.StrPtr => |offset| getString(st, offset),474 FormValue.StrPtr => |offset| getString(st, offset),
...@@ -738,7 +748,7 @@ fn parseDie(st: *ElfStackTrace, abbrev_table: *const AbbrevTable, is_64: bool) !...@@ -738,7 +748,7 @@ fn parseDie(st: *ElfStackTrace, abbrev_table: *const AbbrevTable, is_64: bool) !
738 var in_file_stream = io.FileInStream.init(in_file);748 var in_file_stream = io.FileInStream.init(in_file);
739 const in_stream = &in_file_stream.stream;749 const in_stream = &in_file_stream.stream;
740 const abbrev_code = try readULeb128(in_stream);750 const abbrev_code = try readULeb128(in_stream);
741 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo;751 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
742752
743 var result = Die{753 var result = Die{
744 .tag_id = table_entry.tag_id,754 .tag_id = table_entry.tag_id,
std/event.zig+2-2
...@@ -40,9 +40,9 @@ pub const TcpServer = struct {...@@ -40,9 +40,9 @@ pub const TcpServer = struct {
40 self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd));40 self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd));
4141
42 self.accept_coro = try async<self.loop.allocator> TcpServer.handler(self);42 self.accept_coro = try async<self.loop.allocator> TcpServer.handler(self);
43 errdefer cancel ??self.accept_coro;43 errdefer cancel self.accept_coro.?;
4444
45 try self.loop.addFd(self.sockfd, ??self.accept_coro);45 try self.loop.addFd(self.sockfd, self.accept_coro.?);
46 errdefer self.loop.removeFd(self.sockfd);46 errdefer self.loop.removeFd(self.sockfd);
47 }47 }
4848
std/fmt/index.zig+44-20
...@@ -97,7 +97,11 @@ pub fn formatType(...@@ -97,7 +97,11 @@ pub fn formatType(
97 output: fn (@typeOf(context), []const u8) Errors!void,97 output: fn (@typeOf(context), []const u8) Errors!void,
98) Errors!void {98) Errors!void {
99 const T = @typeOf(value);99 const T = @typeOf(value);
100 switch (@typeId(T)) {100 if (T == error) {
101 try output(context, "error.");
102 return output(context, @errorName(value));
103 }
104 switch (@typeInfo(T)) {
101 builtin.TypeId.Int, builtin.TypeId.Float => {105 builtin.TypeId.Int, builtin.TypeId.Float => {
102 return formatValue(value, fmt, context, Errors, output);106 return formatValue(value, fmt, context, Errors, output);
103 },107 },
...@@ -107,7 +111,7 @@ pub fn formatType(...@@ -107,7 +111,7 @@ pub fn formatType(
107 builtin.TypeId.Bool => {111 builtin.TypeId.Bool => {
108 return output(context, if (value) "true" else "false");112 return output(context, if (value) "true" else "false");
109 },113 },
110 builtin.TypeId.Nullable => {114 builtin.TypeId.Optional => {
111 if (value) |payload| {115 if (value) |payload| {
112 return formatType(payload, fmt, context, Errors, output);116 return formatType(payload, fmt, context, Errors, output);
113 } else {117 } else {
...@@ -125,12 +129,13 @@ pub fn formatType(...@@ -125,12 +129,13 @@ pub fn formatType(
125 try output(context, "error.");129 try output(context, "error.");
126 return output(context, @errorName(value));130 return output(context, @errorName(value));
127 },131 },
128 builtin.TypeId.Pointer => {132 builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) {
129 switch (@typeId(T.Child)) {133 builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) {
130 builtin.TypeId.Array => {134 builtin.TypeId.Array => |info| {
131 if (T.Child.Child == u8) {135 if (info.child == u8) {
132 return formatText(value, fmt, context, Errors, output);136 return formatText(value, fmt, context, Errors, output);
133 }137 }
138 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
134 },139 },
135 builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {140 builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
136 const has_cust_fmt = comptime cf: {141 const has_cust_fmt = comptime cf: {
...@@ -154,14 +159,24 @@ pub fn formatType(...@@ -154,14 +159,24 @@ pub fn formatType(
154 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));159 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
155 },160 },
156 else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),161 else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
157 }162 },
158 },163 builtin.TypeInfo.Pointer.Size.Many => {
159 else => if (@canImplicitCast([]const u8, value)) {164 if (ptr_info.child == u8) {
160 const casted_value = ([]const u8)(value);165 //This is a bit of a hack, but it made more sense to
161 return output(context, casted_value);166 // do this check here than have formatText do it
162 } else {167 if (fmt[0] == 's') {
163 @compileError("Unable to format type '" ++ @typeName(T) ++ "'");168 const len = std.cstr.len(value);
169 return formatText(value[0..len], fmt, context, Errors, output);
170 }
171 }
172 return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
173 },
174 builtin.TypeInfo.Pointer.Size.Slice => {
175 const casted_value = ([]const u8)(value);
176 return output(context, casted_value);
177 },
164 },178 },
179 else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"),
165 }180 }
166}181}
167182
...@@ -293,7 +308,7 @@ pub fn formatBuf(...@@ -293,7 +308,7 @@ pub fn formatBuf(
293 var leftover_padding = if (width > buf.len) (width - buf.len) else return;308 var leftover_padding = if (width > buf.len) (width - buf.len) else return;
294 const pad_byte: u8 = ' ';309 const pad_byte: u8 = ' ';
295 while (leftover_padding > 0) : (leftover_padding -= 1) {310 while (leftover_padding > 0) : (leftover_padding -= 1) {
296 try output(context, (&pad_byte)[0..1]);311 try output(context, (*[1]u8)(&pad_byte)[0..1]);
297 }312 }
298}313}
299314
...@@ -552,14 +567,19 @@ pub fn formatBytes(...@@ -552,14 +567,19 @@ pub fn formatBytes(
552 return output(context, "0B");567 return output(context, "0B");
553 }568 }
554569
555 const mags = " KMGTPEZY";570 const mags_si = " kMGTPEZY";
571 const mags_iec = " KMGTPEZY";
556 const magnitude = switch (radix) {572 const magnitude = switch (radix) {
557 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags.len - 1),573 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags_si.len - 1),
558 1024 => math.min(math.log2(value) / 10, mags.len - 1),574 1024 => math.min(math.log2(value) / 10, mags_iec.len - 1),
559 else => unreachable,575 else => unreachable,
560 };576 };
561 const new_value = f64(value) / math.pow(f64, f64(radix), f64(magnitude));577 const new_value = f64(value) / math.pow(f64, f64(radix), f64(magnitude));
562 const suffix = mags[magnitude];578 const suffix = switch (radix) {
579 1000 => mags_si[magnitude],
580 1024 => mags_iec[magnitude],
581 else => unreachable,
582 };
563583
564 try formatFloatDecimal(new_value, width, context, Errors, output);584 try formatFloatDecimal(new_value, width, context, Errors, output);
565585
...@@ -807,11 +827,11 @@ test "parse unsigned comptime" {...@@ -807,11 +827,11 @@ test "parse unsigned comptime" {
807test "fmt.format" {827test "fmt.format" {
808 {828 {
809 const value: ?i32 = 1234;829 const value: ?i32 = 1234;
810 try testFmt("nullable: 1234\n", "nullable: {}\n", value);830 try testFmt("optional: 1234\n", "optional: {}\n", value);
811 }831 }
812 {832 {
813 const value: ?i32 = null;833 const value: ?i32 = null;
814 try testFmt("nullable: null\n", "nullable: {}\n", value);834 try testFmt("optional: null\n", "optional: {}\n", value);
815 }835 }
816 {836 {
817 const value: error!i32 = 1234;837 const value: error!i32 = 1234;
...@@ -829,6 +849,10 @@ test "fmt.format" {...@@ -829,6 +849,10 @@ test "fmt.format" {
829 const value: u8 = 'a';849 const value: u8 = 'a';
830 try testFmt("u8: a\n", "u8: {c}\n", value);850 try testFmt("u8: a\n", "u8: {c}\n", value);
831 }851 }
852 try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
853 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
854 try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C");
855 try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C");
832 try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));856 try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
833 try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));857 try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));
834 {858 {
std/hash_map.zig+4-4
...@@ -265,11 +265,11 @@ test "basic hash map usage" {...@@ -265,11 +265,11 @@ test "basic hash map usage" {
265 assert((map.put(4, 44) catch unreachable) == null);265 assert((map.put(4, 44) catch unreachable) == null);
266 assert((map.put(5, 55) catch unreachable) == null);266 assert((map.put(5, 55) catch unreachable) == null);
267267
268 assert(??(map.put(5, 66) catch unreachable) == 55);268 assert((map.put(5, 66) catch unreachable).? == 55);
269 assert(??(map.put(5, 55) catch unreachable) == 66);269 assert((map.put(5, 55) catch unreachable).? == 66);
270270
271 assert(map.contains(2));271 assert(map.contains(2));
272 assert((??map.get(2)).value == 22);272 assert(map.get(2).?.value == 22);
273 _ = map.remove(2);273 _ = map.remove(2);
274 assert(map.remove(2) == null);274 assert(map.remove(2) == null);
275 assert(map.get(2) == null);275 assert(map.get(2) == null);
...@@ -317,7 +317,7 @@ test "iterator hash map" {...@@ -317,7 +317,7 @@ test "iterator hash map" {
317 }317 }
318318
319 it.reset();319 it.reset();
320 var entry = ??it.next();320 var entry = it.next().?;
321 assert(entry.key == keys[0]);321 assert(entry.key == keys[0]);
322 assert(entry.value == values[0]);322 assert(entry.value == values[0]);
323}323}
std/heap.zig+10-10
...@@ -22,7 +22,7 @@ fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 {...@@ -22,7 +22,7 @@ fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 {
22}22}
2323
24fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {24fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
25 const old_ptr = @ptrCast([*]c_void, old_mem.ptr);25 const old_ptr = @ptrCast(*c_void, old_mem.ptr);
26 if (c.realloc(old_ptr, new_size)) |buf| {26 if (c.realloc(old_ptr, new_size)) |buf| {
27 return @ptrCast([*]u8, buf)[0..new_size];27 return @ptrCast([*]u8, buf)[0..new_size];
28 } else if (new_size <= old_mem.len) {28 } else if (new_size <= old_mem.len) {
...@@ -33,7 +33,7 @@ fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![...@@ -33,7 +33,7 @@ fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![
33}33}
3434
35fn cFree(self: *Allocator, old_mem: []u8) void {35fn cFree(self: *Allocator, old_mem: []u8) void {
36 const old_ptr = @ptrCast([*]c_void, old_mem.ptr);36 const old_ptr = @ptrCast(*c_void, old_mem.ptr);
37 c.free(old_ptr);37 c.free(old_ptr);
38}38}
3939
...@@ -97,12 +97,12 @@ pub const DirectAllocator = struct {...@@ -97,12 +97,12 @@ pub const DirectAllocator = struct {
97 },97 },
98 Os.windows => {98 Os.windows => {
99 const amt = n + alignment + @sizeOf(usize);99 const amt = n + alignment + @sizeOf(usize);
100 const heap_handle = self.heap_handle ?? blk: {100 const heap_handle = self.heap_handle orelse blk: {
101 const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) ?? return error.OutOfMemory;101 const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) orelse return error.OutOfMemory;
102 self.heap_handle = hh;102 self.heap_handle = hh;
103 break :blk hh;103 break :blk hh;
104 };104 };
105 const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) ?? return error.OutOfMemory;105 const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
106 const root_addr = @ptrToInt(ptr);106 const root_addr = @ptrToInt(ptr);
107 const rem = @rem(root_addr, alignment);107 const rem = @rem(root_addr, alignment);
108 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);108 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
...@@ -140,9 +140,9 @@ pub const DirectAllocator = struct {...@@ -140,9 +140,9 @@ pub const DirectAllocator = struct {
140 const old_adjusted_addr = @ptrToInt(old_mem.ptr);140 const old_adjusted_addr = @ptrToInt(old_mem.ptr);
141 const old_record_addr = old_adjusted_addr + old_mem.len;141 const old_record_addr = old_adjusted_addr + old_mem.len;
142 const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;142 const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
143 const old_ptr = @intToPtr([*]c_void, root_addr);143 const old_ptr = @intToPtr(*c_void, root_addr);
144 const amt = new_size + alignment + @sizeOf(usize);144 const amt = new_size + alignment + @sizeOf(usize);
145 const new_ptr = os.windows.HeapReAlloc(??self.heap_handle, 0, old_ptr, amt) ?? blk: {145 const new_ptr = os.windows.HeapReAlloc(self.heap_handle.?, 0, old_ptr, amt) orelse blk: {
146 if (new_size > old_mem.len) return error.OutOfMemory;146 if (new_size > old_mem.len) return error.OutOfMemory;
147 const new_record_addr = old_record_addr - new_size + old_mem.len;147 const new_record_addr = old_record_addr - new_size + old_mem.len;
148 @intToPtr(*align(1) usize, new_record_addr).* = root_addr;148 @intToPtr(*align(1) usize, new_record_addr).* = root_addr;
...@@ -170,8 +170,8 @@ pub const DirectAllocator = struct {...@@ -170,8 +170,8 @@ pub const DirectAllocator = struct {
170 Os.windows => {170 Os.windows => {
171 const record_addr = @ptrToInt(bytes.ptr) + bytes.len;171 const record_addr = @ptrToInt(bytes.ptr) + bytes.len;
172 const root_addr = @intToPtr(*align(1) usize, record_addr).*;172 const root_addr = @intToPtr(*align(1) usize, record_addr).*;
173 const ptr = @intToPtr([*]c_void, root_addr);173 const ptr = @intToPtr(*c_void, root_addr);
174 _ = os.windows.HeapFree(??self.heap_handle, 0, ptr);174 _ = os.windows.HeapFree(self.heap_handle.?, 0, ptr);
175 },175 },
176 else => @compileError("Unsupported OS"),176 else => @compileError("Unsupported OS"),
177 }177 }
...@@ -343,7 +343,7 @@ pub const ThreadSafeFixedBufferAllocator = struct {...@@ -343,7 +343,7 @@ pub const ThreadSafeFixedBufferAllocator = struct {
343 if (new_end_index > self.buffer.len) {343 if (new_end_index > self.buffer.len) {
344 return error.OutOfMemory;344 return error.OutOfMemory;
345 }345 }
346 end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index..new_end_index];346 end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse return self.buffer[adjusted_index..new_end_index];
347 }347 }
348 }348 }
349349
std/json.zig+177-88
...@@ -3,6 +3,7 @@...@@ -3,6 +3,7 @@
3// https://tools.ietf.org/html/rfc82593// https://tools.ietf.org/html/rfc8259
44
5const std = @import("index.zig");5const std = @import("index.zig");
6const debug = std.debug;
6const mem = std.mem;7const mem = std.mem;
78
8const u1 = @IntType(false, 1);9const u1 = @IntType(false, 1);
...@@ -86,7 +87,9 @@ pub const Token = struct {...@@ -86,7 +87,9 @@ pub const Token = struct {
86// parsing state requires ~40-50 bytes of stack space.87// parsing state requires ~40-50 bytes of stack space.
87//88//
88// Conforms strictly to RFC8529.89// Conforms strictly to RFC8529.
89pub const StreamingJsonParser = struct {90//
91// For a non-byte based wrapper, consider using TokenStream instead.
92pub const StreamingParser = struct {
90 // Current state93 // Current state
91 state: State,94 state: State,
92 // How many bytes we have counted for the current token95 // How many bytes we have counted for the current token
...@@ -109,13 +112,13 @@ pub const StreamingJsonParser = struct {...@@ -109,13 +112,13 @@ pub const StreamingJsonParser = struct {
109 const array_bit = 1;112 const array_bit = 1;
110 const max_stack_size = @maxValue(u8);113 const max_stack_size = @maxValue(u8);
111114
112 pub fn init() StreamingJsonParser {115 pub fn init() StreamingParser {
113 var p: StreamingJsonParser = undefined;116 var p: StreamingParser = undefined;
114 p.reset();117 p.reset();
115 return p;118 return p;
116 }119 }
117120
118 pub fn reset(p: *StreamingJsonParser) void {121 pub fn reset(p: *StreamingParser) void {
119 p.state = State.TopLevelBegin;122 p.state = State.TopLevelBegin;
120 p.count = 0;123 p.count = 0;
121 // Set before ever read in main transition function124 // Set before ever read in main transition function
...@@ -175,7 +178,7 @@ pub const StreamingJsonParser = struct {...@@ -175,7 +178,7 @@ pub const StreamingJsonParser = struct {
175178
176 // Only call this function to generate array/object final state.179 // Only call this function to generate array/object final state.
177 pub fn fromInt(x: var) State {180 pub fn fromInt(x: var) State {
178 std.debug.assert(x == 0 or x == 1);181 debug.assert(x == 0 or x == 1);
179 const T = @TagType(State);182 const T = @TagType(State);
180 return State(T(x));183 return State(T(x));
181 }184 }
...@@ -205,7 +208,7 @@ pub const StreamingJsonParser = struct {...@@ -205,7 +208,7 @@ pub const StreamingJsonParser = struct {
205 // tokens. token2 is always null if token1 is null.208 // tokens. token2 is always null if token1 is null.
206 //209 //
207 // There is currently no error recovery on a bad stream.210 // There is currently no error recovery on a bad stream.
208 pub fn feed(p: *StreamingJsonParser, c: u8, token1: *?Token, token2: *?Token) Error!void {211 pub fn feed(p: *StreamingParser, c: u8, token1: *?Token, token2: *?Token) Error!void {
209 token1.* = null;212 token1.* = null;
210 token2.* = null;213 token2.* = null;
211 p.count += 1;214 p.count += 1;
...@@ -217,7 +220,7 @@ pub const StreamingJsonParser = struct {...@@ -217,7 +220,7 @@ pub const StreamingJsonParser = struct {
217 }220 }
218221
219 // Perform a single transition on the state machine and return any possible token.222 // Perform a single transition on the state machine and return any possible token.
220 fn transition(p: *StreamingJsonParser, c: u8, token: *?Token) Error!bool {223 fn transition(p: *StreamingParser, c: u8, token: *?Token) Error!bool {
221 switch (p.state) {224 switch (p.state) {
222 State.TopLevelBegin => switch (c) {225 State.TopLevelBegin => switch (c) {
223 '{' => {226 '{' => {
...@@ -321,7 +324,9 @@ pub const StreamingJsonParser = struct {...@@ -321,7 +324,9 @@ pub const StreamingJsonParser = struct {
321 p.complete = true;324 p.complete = true;
322 p.state = State.TopLevelEnd;325 p.state = State.TopLevelEnd;
323 },326 },
324 else => {},327 else => {
328 p.state = State.ValueEnd;
329 },
325 }330 }
326331
327 token.* = Token.initMarker(Token.Id.ObjectEnd);332 token.* = Token.initMarker(Token.Id.ObjectEnd);
...@@ -345,7 +350,9 @@ pub const StreamingJsonParser = struct {...@@ -345,7 +350,9 @@ pub const StreamingJsonParser = struct {
345 p.complete = true;350 p.complete = true;
346 p.state = State.TopLevelEnd;351 p.state = State.TopLevelEnd;
347 },352 },
348 else => {},353 else => {
354 p.state = State.ValueEnd;
355 },
349 }356 }
350357
351 token.* = Token.initMarker(Token.Id.ArrayEnd);358 token.* = Token.initMarker(Token.Id.ArrayEnd);
...@@ -852,16 +859,122 @@ pub const StreamingJsonParser = struct {...@@ -852,16 +859,122 @@ pub const StreamingJsonParser = struct {
852 }859 }
853};860};
854861
862// A small wrapper over a StreamingParser for full slices. Returns a stream of json Tokens.
863pub const TokenStream = struct {
864 i: usize,
865 slice: []const u8,
866 parser: StreamingParser,
867 token: ?Token,
868
869 pub fn init(slice: []const u8) TokenStream {
870 return TokenStream{
871 .i = 0,
872 .slice = slice,
873 .parser = StreamingParser.init(),
874 .token = null,
875 };
876 }
877
878 pub fn next(self: *TokenStream) !?Token {
879 if (self.token) |token| {
880 self.token = null;
881 return token;
882 }
883
884 var t1: ?Token = undefined;
885 var t2: ?Token = undefined;
886
887 while (self.i < self.slice.len) {
888 try self.parser.feed(self.slice[self.i], &t1, &t2);
889 self.i += 1;
890
891 if (t1) |token| {
892 self.token = t2;
893 return token;
894 }
895 }
896
897 if (self.i > self.slice.len) {
898 try self.parser.feed(' ', &t1, &t2);
899 self.i += 1;
900
901 if (t1) |token| {
902 return token;
903 }
904 }
905
906 return null;
907 }
908};
909
910fn checkNext(p: *TokenStream, id: Token.Id) void {
911 const token = (p.next() catch unreachable).?;
912 debug.assert(token.id == id);
913}
914
915test "token" {
916 const s =
917 \\{
918 \\ "Image": {
919 \\ "Width": 800,
920 \\ "Height": 600,
921 \\ "Title": "View from 15th Floor",
922 \\ "Thumbnail": {
923 \\ "Url": "http://www.example.com/image/481989943",
924 \\ "Height": 125,
925 \\ "Width": 100
926 \\ },
927 \\ "Animated" : false,
928 \\ "IDs": [116, 943, 234, 38793]
929 \\ }
930 \\}
931 ;
932
933 var p = TokenStream.init(s);
934
935 checkNext(&p, Token.Id.ObjectBegin);
936 checkNext(&p, Token.Id.String); // Image
937 checkNext(&p, Token.Id.ObjectBegin);
938 checkNext(&p, Token.Id.String); // Width
939 checkNext(&p, Token.Id.Number);
940 checkNext(&p, Token.Id.String); // Height
941 checkNext(&p, Token.Id.Number);
942 checkNext(&p, Token.Id.String); // Title
943 checkNext(&p, Token.Id.String);
944 checkNext(&p, Token.Id.String); // Thumbnail
945 checkNext(&p, Token.Id.ObjectBegin);
946 checkNext(&p, Token.Id.String); // Url
947 checkNext(&p, Token.Id.String);
948 checkNext(&p, Token.Id.String); // Height
949 checkNext(&p, Token.Id.Number);
950 checkNext(&p, Token.Id.String); // Width
951 checkNext(&p, Token.Id.Number);
952 checkNext(&p, Token.Id.ObjectEnd);
953 checkNext(&p, Token.Id.String); // Animated
954 checkNext(&p, Token.Id.False);
955 checkNext(&p, Token.Id.String); // IDs
956 checkNext(&p, Token.Id.ArrayBegin);
957 checkNext(&p, Token.Id.Number);
958 checkNext(&p, Token.Id.Number);
959 checkNext(&p, Token.Id.Number);
960 checkNext(&p, Token.Id.Number);
961 checkNext(&p, Token.Id.ArrayEnd);
962 checkNext(&p, Token.Id.ObjectEnd);
963 checkNext(&p, Token.Id.ObjectEnd);
964
965 debug.assert((try p.next()) == null);
966}
967
855// Validate a JSON string. This does not limit number precision so a decoder may not necessarily968// Validate a JSON string. This does not limit number precision so a decoder may not necessarily
856// be able to decode the string even if this returns true.969// be able to decode the string even if this returns true.
857pub fn validate(s: []const u8) bool {970pub fn validate(s: []const u8) bool {
858 var p = StreamingJsonParser.init();971 var p = StreamingParser.init();
859972
860 for (s) |c, i| {973 for (s) |c, i| {
861 var token1: ?Token = undefined;974 var token1: ?Token = undefined;
862 var token2: ?Token = undefined;975 var token2: ?Token = undefined;
863976
864 p.feed(c, *token1, *token2) catch |err| {977 p.feed(c, &token1, &token2) catch |err| {
865 return false;978 return false;
866 };979 };
867 }980 }
...@@ -869,6 +982,10 @@ pub fn validate(s: []const u8) bool {...@@ -869,6 +982,10 @@ pub fn validate(s: []const u8) bool {
869 return p.complete;982 return p.complete;
870}983}
871984
985test "json validate" {
986 debug.assert(validate("{}"));
987}
988
872const Allocator = std.mem.Allocator;989const Allocator = std.mem.Allocator;
873const ArenaAllocator = std.heap.ArenaAllocator;990const ArenaAllocator = std.heap.ArenaAllocator;
874const ArrayList = std.ArrayList;991const ArrayList = std.ArrayList;
...@@ -897,46 +1014,46 @@ pub const Value = union(enum) {...@@ -897,46 +1014,46 @@ pub const Value = union(enum) {
897 pub fn dump(self: *const Value) void {1014 pub fn dump(self: *const Value) void {
898 switch (self.*) {1015 switch (self.*) {
899 Value.Null => {1016 Value.Null => {
900 std.debug.warn("null");1017 debug.warn("null");
901 },1018 },
902 Value.Bool => |inner| {1019 Value.Bool => |inner| {
903 std.debug.warn("{}", inner);1020 debug.warn("{}", inner);
904 },1021 },
905 Value.Integer => |inner| {1022 Value.Integer => |inner| {
906 std.debug.warn("{}", inner);1023 debug.warn("{}", inner);
907 },1024 },
908 Value.Float => |inner| {1025 Value.Float => |inner| {
909 std.debug.warn("{.5}", inner);1026 debug.warn("{.5}", inner);
910 },1027 },
911 Value.String => |inner| {1028 Value.String => |inner| {
912 std.debug.warn("\"{}\"", inner);1029 debug.warn("\"{}\"", inner);
913 },1030 },
914 Value.Array => |inner| {1031 Value.Array => |inner| {
915 var not_first = false;1032 var not_first = false;
916 std.debug.warn("[");1033 debug.warn("[");
917 for (inner.toSliceConst()) |value| {1034 for (inner.toSliceConst()) |value| {
918 if (not_first) {1035 if (not_first) {
919 std.debug.warn(",");1036 debug.warn(",");
920 }1037 }
921 not_first = true;1038 not_first = true;
922 value.dump();1039 value.dump();
923 }1040 }
924 std.debug.warn("]");1041 debug.warn("]");
925 },1042 },
926 Value.Object => |inner| {1043 Value.Object => |inner| {
927 var not_first = false;1044 var not_first = false;
928 std.debug.warn("{{");1045 debug.warn("{{");
929 var it = inner.iterator();1046 var it = inner.iterator();
9301047
931 while (it.next()) |entry| {1048 while (it.next()) |entry| {
932 if (not_first) {1049 if (not_first) {
933 std.debug.warn(",");1050 debug.warn(",");
934 }1051 }
935 not_first = true;1052 not_first = true;
936 std.debug.warn("\"{}\":", entry.key);1053 debug.warn("\"{}\":", entry.key);
937 entry.value.dump();1054 entry.value.dump();
938 }1055 }
939 std.debug.warn("}}");1056 debug.warn("}}");
940 },1057 },
941 }1058 }
942 }1059 }
...@@ -952,53 +1069,53 @@ pub const Value = union(enum) {...@@ -952,53 +1069,53 @@ pub const Value = union(enum) {
952 fn dumpIndentLevel(self: *const Value, indent: usize, level: usize) void {1069 fn dumpIndentLevel(self: *const Value, indent: usize, level: usize) void {
953 switch (self.*) {1070 switch (self.*) {
954 Value.Null => {1071 Value.Null => {
955 std.debug.warn("null");1072 debug.warn("null");
956 },1073 },
957 Value.Bool => |inner| {1074 Value.Bool => |inner| {
958 std.debug.warn("{}", inner);1075 debug.warn("{}", inner);
959 },1076 },
960 Value.Integer => |inner| {1077 Value.Integer => |inner| {
961 std.debug.warn("{}", inner);1078 debug.warn("{}", inner);
962 },1079 },
963 Value.Float => |inner| {1080 Value.Float => |inner| {
964 std.debug.warn("{.5}", inner);1081 debug.warn("{.5}", inner);
965 },1082 },
966 Value.String => |inner| {1083 Value.String => |inner| {
967 std.debug.warn("\"{}\"", inner);1084 debug.warn("\"{}\"", inner);
968 },1085 },
969 Value.Array => |inner| {1086 Value.Array => |inner| {
970 var not_first = false;1087 var not_first = false;
971 std.debug.warn("[\n");1088 debug.warn("[\n");
9721089
973 for (inner.toSliceConst()) |value| {1090 for (inner.toSliceConst()) |value| {
974 if (not_first) {1091 if (not_first) {
975 std.debug.warn(",\n");1092 debug.warn(",\n");
976 }1093 }
977 not_first = true;1094 not_first = true;
978 padSpace(level + indent);1095 padSpace(level + indent);
979 value.dumpIndentLevel(indent, level + indent);1096 value.dumpIndentLevel(indent, level + indent);
980 }1097 }
981 std.debug.warn("\n");1098 debug.warn("\n");
982 padSpace(level);1099 padSpace(level);
983 std.debug.warn("]");1100 debug.warn("]");
984 },1101 },
985 Value.Object => |inner| {1102 Value.Object => |inner| {
986 var not_first = false;1103 var not_first = false;
987 std.debug.warn("{{\n");1104 debug.warn("{{\n");
988 var it = inner.iterator();1105 var it = inner.iterator();
9891106
990 while (it.next()) |entry| {1107 while (it.next()) |entry| {
991 if (not_first) {1108 if (not_first) {
992 std.debug.warn(",\n");1109 debug.warn(",\n");
993 }1110 }
994 not_first = true;1111 not_first = true;
995 padSpace(level + indent);1112 padSpace(level + indent);
996 std.debug.warn("\"{}\": ", entry.key);1113 debug.warn("\"{}\": ", entry.key);
997 entry.value.dumpIndentLevel(indent, level + indent);1114 entry.value.dumpIndentLevel(indent, level + indent);
998 }1115 }
999 std.debug.warn("\n");1116 debug.warn("\n");
1000 padSpace(level);1117 padSpace(level);
1001 std.debug.warn("}}");1118 debug.warn("}}");
1002 },1119 },
1003 }1120 }
1004 }1121 }
...@@ -1006,13 +1123,13 @@ pub const Value = union(enum) {...@@ -1006,13 +1123,13 @@ pub const Value = union(enum) {
1006 fn padSpace(indent: usize) void {1123 fn padSpace(indent: usize) void {
1007 var i: usize = 0;1124 var i: usize = 0;
1008 while (i < indent) : (i += 1) {1125 while (i < indent) : (i += 1) {
1009 std.debug.warn(" ");1126 debug.warn(" ");
1010 }1127 }
1011 }1128 }
1012};1129};
10131130
1014// A non-stream JSON parser which constructs a tree of Value's.1131// A non-stream JSON parser which constructs a tree of Value's.
1015pub const JsonParser = struct {1132pub const Parser = struct {
1016 allocator: *Allocator,1133 allocator: *Allocator,
1017 state: State,1134 state: State,
1018 copy_strings: bool,1135 copy_strings: bool,
...@@ -1026,8 +1143,8 @@ pub const JsonParser = struct {...@@ -1026,8 +1143,8 @@ pub const JsonParser = struct {
1026 Simple,1143 Simple,
1027 };1144 };
10281145
1029 pub fn init(allocator: *Allocator, copy_strings: bool) JsonParser {1146 pub fn init(allocator: *Allocator, copy_strings: bool) Parser {
1030 return JsonParser{1147 return Parser{
1031 .allocator = allocator,1148 .allocator = allocator,
1032 .state = State.Simple,1149 .state = State.Simple,
1033 .copy_strings = copy_strings,1150 .copy_strings = copy_strings,
...@@ -1035,52 +1152,26 @@ pub const JsonParser = struct {...@@ -1035,52 +1152,26 @@ pub const JsonParser = struct {
1035 };1152 };
1036 }1153 }
10371154
1038 pub fn deinit(p: *JsonParser) void {1155 pub fn deinit(p: *Parser) void {
1039 p.stack.deinit();1156 p.stack.deinit();
1040 }1157 }
10411158
1042 pub fn reset(p: *JsonParser) void {1159 pub fn reset(p: *Parser) void {
1043 p.state = State.Simple;1160 p.state = State.Simple;
1044 p.stack.shrink(0);1161 p.stack.shrink(0);
1045 }1162 }
10461163
1047 pub fn parse(p: *JsonParser, input: []const u8) !ValueTree {1164 pub fn parse(p: *Parser, input: []const u8) !ValueTree {
1048 var mp = StreamingJsonParser.init();1165 var s = TokenStream.init(input);
10491166
1050 var arena = ArenaAllocator.init(p.allocator);1167 var arena = ArenaAllocator.init(p.allocator);
1051 errdefer arena.deinit();1168 errdefer arena.deinit();
10521169
1053 for (input) |c, i| {1170 while (try s.next()) |token| {
1054 var mt1: ?Token = undefined;1171 try p.transition(&arena.allocator, input, s.i - 1, token);
1055 var mt2: ?Token = undefined;
1056
1057 try mp.feed(c, &mt1, &mt2);
1058 if (mt1) |t1| {
1059 try p.transition(&arena.allocator, input, i, t1);
1060
1061 if (mt2) |t2| {
1062 try p.transition(&arena.allocator, input, i, t2);
1063 }
1064 }
1065 }
1066
1067 // Handle top-level lonely number values.
1068 {
1069 const i = input.len;
1070 var mt1: ?Token = undefined;
1071 var mt2: ?Token = undefined;
1072
1073 try mp.feed(' ', &mt1, &mt2);
1074 if (mt1) |t1| {
1075 try p.transition(&arena.allocator, input, i, t1);
1076 }
1077 }
1078
1079 if (!mp.complete) {
1080 return error.IncompleteJsonInput;
1081 }1172 }
10821173
1083 std.debug.assert(p.stack.len == 1);1174 debug.assert(p.stack.len == 1);
10841175
1085 return ValueTree{1176 return ValueTree{
1086 .arena = arena,1177 .arena = arena,
...@@ -1090,7 +1181,7 @@ pub const JsonParser = struct {...@@ -1090,7 +1181,7 @@ pub const JsonParser = struct {
10901181
1091 // Even though p.allocator exists, we take an explicit allocator so that allocation state1182 // Even though p.allocator exists, we take an explicit allocator so that allocation state
1092 // can be cleaned up on error correctly during a `parse` on call.1183 // can be cleaned up on error correctly during a `parse` on call.
1093 fn transition(p: *JsonParser, allocator: *Allocator, input: []const u8, i: usize, token: *const Token) !void {1184 fn transition(p: *Parser, allocator: *Allocator, input: []const u8, i: usize, token: *const Token) !void {
1094 switch (p.state) {1185 switch (p.state) {
1095 State.ObjectKey => switch (token.id) {1186 State.ObjectKey => switch (token.id) {
1096 Token.Id.ObjectEnd => {1187 Token.Id.ObjectEnd => {
...@@ -1147,7 +1238,7 @@ pub const JsonParser = struct {...@@ -1147,7 +1238,7 @@ pub const JsonParser = struct {
1147 _ = p.stack.pop();1238 _ = p.stack.pop();
1148 p.state = State.ObjectKey;1239 p.state = State.ObjectKey;
1149 },1240 },
1150 else => {1241 Token.Id.ObjectEnd, Token.Id.ArrayEnd => {
1151 unreachable;1242 unreachable;
1152 },1243 },
1153 }1244 }
...@@ -1187,7 +1278,7 @@ pub const JsonParser = struct {...@@ -1187,7 +1278,7 @@ pub const JsonParser = struct {
1187 Token.Id.Null => {1278 Token.Id.Null => {
1188 try array.append(Value.Null);1279 try array.append(Value.Null);
1189 },1280 },
1190 else => {1281 Token.Id.ObjectEnd => {
1191 unreachable;1282 unreachable;
1192 },1283 },
1193 }1284 }
...@@ -1223,7 +1314,7 @@ pub const JsonParser = struct {...@@ -1223,7 +1314,7 @@ pub const JsonParser = struct {
1223 }1314 }
1224 }1315 }
12251316
1226 fn pushToParent(p: *JsonParser, value: *const Value) !void {1317 fn pushToParent(p: *Parser, value: *const Value) !void {
1227 switch (p.stack.at(p.stack.len - 1)) {1318 switch (p.stack.at(p.stack.len - 1)) {
1228 // Object Parent -> [ ..., object, <key>, value ]1319 // Object Parent -> [ ..., object, <key>, value ]
1229 Value.String => |key| {1320 Value.String => |key| {
...@@ -1244,14 +1335,14 @@ pub const JsonParser = struct {...@@ -1244,14 +1335,14 @@ pub const JsonParser = struct {
1244 }1335 }
1245 }1336 }
12461337
1247 fn parseString(p: *JsonParser, allocator: *Allocator, token: *const Token, input: []const u8, i: usize) !Value {1338 fn parseString(p: *Parser, allocator: *Allocator, token: *const Token, input: []const u8, i: usize) !Value {
1248 // TODO: We don't strictly have to copy values which do not contain any escape1339 // TODO: We don't strictly have to copy values which do not contain any escape
1249 // characters if flagged with the option.1340 // characters if flagged with the option.
1250 const slice = token.slice(input, i);1341 const slice = token.slice(input, i);
1251 return Value{ .String = try mem.dupe(p.allocator, u8, slice) };1342 return Value{ .String = try mem.dupe(p.allocator, u8, slice) };
1252 }1343 }
12531344
1254 fn parseNumber(p: *JsonParser, token: *const Token, input: []const u8, i: usize) !Value {1345 fn parseNumber(p: *Parser, token: *const Token, input: []const u8, i: usize) !Value {
1255 return if (token.number_is_integer)1346 return if (token.number_is_integer)
1256 Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) }1347 Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) }
1257 else1348 else
...@@ -1259,10 +1350,8 @@ pub const JsonParser = struct {...@@ -1259,10 +1350,8 @@ pub const JsonParser = struct {
1259 }1350 }
1260};1351};
12611352
1262const debug = std.debug;
1263
1264test "json parser dynamic" {1353test "json parser dynamic" {
1265 var p = JsonParser.init(std.debug.global_allocator, false);1354 var p = Parser.init(debug.global_allocator, false);
1266 defer p.deinit();1355 defer p.deinit();
12671356
1268 const s =1357 const s =
...@@ -1287,17 +1376,17 @@ test "json parser dynamic" {...@@ -1287,17 +1376,17 @@ test "json parser dynamic" {
12871376
1288 var root = tree.root;1377 var root = tree.root;
12891378
1290 var image = (??root.Object.get("Image")).value;1379 var image = root.Object.get("Image").?.value;
12911380
1292 const width = (??image.Object.get("Width")).value;1381 const width = image.Object.get("Width").?.value;
1293 debug.assert(width.Integer == 800);1382 debug.assert(width.Integer == 800);
12941383
1295 const height = (??image.Object.get("Height")).value;1384 const height = image.Object.get("Height").?.value;
1296 debug.assert(height.Integer == 600);1385 debug.assert(height.Integer == 600);
12971386
1298 const title = (??image.Object.get("Title")).value;1387 const title = image.Object.get("Title").?.value;
1299 debug.assert(mem.eql(u8, title.String, "View from 15th Floor"));1388 debug.assert(mem.eql(u8, title.String, "View from 15th Floor"));
13001389
1301 const animated = (??image.Object.get("Animated")).value;1390 const animated = image.Object.get("Animated").?.value;
1302 debug.assert(animated.Bool == false);1391 debug.assert(animated.Bool == false);
1303}1392}
std/json_test.zig+10
...@@ -17,6 +17,16 @@ fn any(comptime s: []const u8) void {...@@ -17,6 +17,16 @@ fn any(comptime s: []const u8) void {
17 std.debug.assert(true);17 std.debug.assert(true);
18}18}
1919
20////////////////////////////////////////////////////////////////////////////////////////////////////
21//
22// Additional tests not part of test JSONTestSuite.
23
24test "y_trailing_comma_after_empty" {
25 ok(
26 \\{"1":[],"2":{},"3":"4"}
27 );
28}
29
20////////////////////////////////////////////////////////////////////////////////////////////////////30////////////////////////////////////////////////////////////////////////////////////////////////////
2131
22test "y_array_arraysWithSpaces" {32test "y_array_arraysWithSpaces" {
std/linked_list.zig+6-6
...@@ -169,7 +169,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -169,7 +169,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
169 /// Returns:169 /// Returns:
170 /// A pointer to the last node in the list.170 /// A pointer to the last node in the list.
171 pub fn pop(list: *Self) ?*Node {171 pub fn pop(list: *Self) ?*Node {
172 const last = list.last ?? return null;172 const last = list.last orelse return null;
173 list.remove(last);173 list.remove(last);
174 return last;174 return last;
175 }175 }
...@@ -179,7 +179,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -179,7 +179,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
179 /// Returns:179 /// Returns:
180 /// A pointer to the first node in the list.180 /// A pointer to the first node in the list.
181 pub fn popFirst(list: *Self) ?*Node {181 pub fn popFirst(list: *Self) ?*Node {
182 const first = list.first ?? return null;182 const first = list.first orelse return null;
183 list.remove(first);183 list.remove(first);
184 return first;184 return first;
185 }185 }
...@@ -270,8 +270,8 @@ test "basic linked list test" {...@@ -270,8 +270,8 @@ test "basic linked list test" {
270 var last = list.pop(); // {2, 3, 4}270 var last = list.pop(); // {2, 3, 4}
271 list.remove(three); // {2, 4}271 list.remove(three); // {2, 4}
272272
273 assert((??list.first).data == 2);273 assert(list.first.?.data == 2);
274 assert((??list.last).data == 4);274 assert(list.last.?.data == 4);
275 assert(list.len == 2);275 assert(list.len == 2);
276}276}
277277
...@@ -336,7 +336,7 @@ test "basic intrusive linked list test" {...@@ -336,7 +336,7 @@ test "basic intrusive linked list test" {
336 var last = list.pop(); // {2, 3, 4}336 var last = list.pop(); // {2, 3, 4}
337 list.remove(&three.link); // {2, 4}337 list.remove(&three.link); // {2, 4}
338338
339 assert((??list.first).toData().value == 2);339 assert(list.first.?.toData().value == 2);
340 assert((??list.last).toData().value == 4);340 assert(list.last.?.toData().value == 4);
341 assert(list.len == 2);341 assert(list.len == 2);
342}342}
std/macho.zig+1-1
...@@ -130,7 +130,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable...@@ -130,7 +130,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable
130 for (syms) |sym| {130 for (syms) |sym| {
131 if (!isSymbol(sym)) continue;131 if (!isSymbol(sym)) continue;
132 const start = sym.n_strx;132 const start = sym.n_strx;
133 const end = ??mem.indexOfScalarPos(u8, strings, start, 0);133 const end = mem.indexOfScalarPos(u8, strings, start, 0).?;
134 const name = strings[start..end];134 const name = strings[start..end];
135 const address = sym.n_value;135 const address = sym.n_value;
136 symbols[nsym] = Symbol{ .name = name, .address = address };136 symbols[nsym] = Symbol{ .name = name, .address = address };
std/math/big/index.zig created+5
...@@ -0,0 +1,5 @@
1pub use @import("int.zig");
2
3test "math.big" {
4 _ = @import("int.zig");
5}
std/math/big/int.zig created+2023
...@@ -0,0 +1,2023 @@
1const std = @import("../../index.zig");
2const builtin = @import("builtin");
3const debug = std.debug;
4const math = std.math;
5const mem = std.mem;
6const Allocator = mem.Allocator;
7const ArrayList = std.ArrayList;
8
9const TypeId = builtin.TypeId;
10
11pub const Limb = usize;
12pub const DoubleLimb = @IntType(false, 2 * Limb.bit_count);
13pub const Log2Limb = math.Log2Int(Limb);
14
15comptime {
16 debug.assert(math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count);
17 debug.assert(Limb.bit_count <= 64); // u128 set is unsupported
18 debug.assert(Limb.is_signed == false);
19}
20
21const wrapped_buffer_size = 512;
22
23// Converts primitive integer values onto a stack-based big integer, or passes through existing
24// Int types with no modifications. This can fail at runtime if using a very large dynamic
25// integer but it is very unlikely and is considered a user error.
26fn wrapInt(allocator: *Allocator, bn: var) *const Int {
27 const T = @typeOf(bn);
28 switch (@typeInfo(T)) {
29 TypeId.Pointer => |info| {
30 if (info.child == Int) {
31 return bn;
32 } else {
33 @compileError("cannot set Int using type " ++ @typeName(T));
34 }
35 },
36 else => {
37 var s = allocator.create(Int) catch unreachable;
38 s.* = Int{
39 .allocator = allocator,
40 .positive = false,
41 .limbs = block: {
42 var limbs = allocator.alloc(Limb, Int.default_capacity) catch unreachable;
43 limbs[0] = 0;
44 break :block limbs;
45 },
46 .len = 1,
47 };
48 s.set(bn) catch unreachable;
49 return s;
50 },
51 }
52}
53
54pub const Int = struct {
55 allocator: *Allocator,
56 positive: bool,
57 // - little-endian ordered
58 // - len >= 1 always
59 // - zero value -> len == 1 with limbs[0] == 0
60 limbs: []Limb,
61 len: usize,
62
63 const default_capacity = 4;
64
65 pub fn init(allocator: *Allocator) !Int {
66 return try Int.initCapacity(allocator, default_capacity);
67 }
68
69 pub fn initSet(allocator: *Allocator, value: var) !Int {
70 var s = try Int.init(allocator);
71 try s.set(value);
72 return s;
73 }
74
75 pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int {
76 return Int{
77 .allocator = allocator,
78 .positive = true,
79 .limbs = block: {
80 var limbs = try allocator.alloc(Limb, math.max(default_capacity, capacity));
81 limbs[0] = 0;
82 break :block limbs;
83 },
84 .len = 1,
85 };
86 }
87
88 pub fn ensureCapacity(self: *Int, capacity: usize) !void {
89 if (capacity <= self.limbs.len) {
90 return;
91 }
92
93 self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity);
94 }
95
96 pub fn deinit(self: *const Int) void {
97 self.allocator.free(self.limbs);
98 }
99
100 pub fn clone(other: *const Int) !Int {
101 return Int{
102 .allocator = other.allocator,
103 .positive = other.positive,
104 .limbs = block: {
105 var limbs = try other.allocator.alloc(Limb, other.len);
106 mem.copy(Limb, limbs[0..], other.limbs[0..other.len]);
107 break :block limbs;
108 },
109 .len = other.len,
110 };
111 }
112
113 pub fn copy(self: *Int, other: *const Int) !void {
114 if (self == other) {
115 return;
116 }
117
118 self.positive = other.positive;
119 try self.ensureCapacity(other.len);
120 mem.copy(Limb, self.limbs[0..], other.limbs[0..other.len]);
121 self.len = other.len;
122 }
123
124 pub fn swap(self: *Int, other: *Int) void {
125 mem.swap(Int, self, other);
126 }
127
128 pub fn dump(self: *const Int) void {
129 for (self.limbs) |limb| {
130 debug.warn("{x} ", limb);
131 }
132 debug.warn("\n");
133 }
134
135 pub fn negate(r: *Int) void {
136 r.positive = !r.positive;
137 }
138
139 pub fn abs(r: *Int) void {
140 r.positive = true;
141 }
142
143 pub fn isOdd(r: *const Int) bool {
144 return r.limbs[0] & 1 != 0;
145 }
146
147 pub fn isEven(r: *const Int) bool {
148 return !r.isOdd();
149 }
150
151 fn bitcount(self: *const Int) usize {
152 const u_bit_count = (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1]));
153 return usize(!self.positive) + u_bit_count;
154 }
155
156 pub fn sizeInBase(self: *const Int, base: usize) usize {
157 return (self.bitcount() / math.log2(base)) + 1;
158 }
159
160 pub fn set(self: *Int, value: var) Allocator.Error!void {
161 const T = @typeOf(value);
162
163 switch (@typeInfo(T)) {
164 TypeId.Int => |info| {
165 const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T;
166
167 try self.ensureCapacity(@sizeOf(UT) / @sizeOf(Limb));
168 self.positive = value >= 0;
169 self.len = 0;
170
171 var w_value: UT = if (value < 0) UT(-value) else UT(value);
172
173 if (info.bits <= Limb.bit_count) {
174 self.limbs[0] = Limb(w_value);
175 self.len = 1;
176 } else {
177 var i: usize = 0;
178 while (w_value != 0) : (i += 1) {
179 self.limbs[i] = @truncate(Limb, w_value);
180 self.len += 1;
181
182 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
183 w_value >>= Limb.bit_count / 2;
184 w_value >>= Limb.bit_count / 2;
185 }
186 }
187 },
188 TypeId.ComptimeInt => {
189 comptime var w_value = if (value < 0) -value else value;
190
191 const req_limbs = @divFloor(math.log2(w_value), Limb.bit_count) + 1;
192 try self.ensureCapacity(req_limbs);
193
194 self.positive = value >= 0;
195 self.len = req_limbs;
196
197 if (w_value <= @maxValue(Limb)) {
198 self.limbs[0] = w_value;
199 } else {
200 const mask = (1 << Limb.bit_count) - 1;
201
202 comptime var i = 0;
203 inline while (w_value != 0) : (i += 1) {
204 self.limbs[i] = w_value & mask;
205
206 w_value >>= Limb.bit_count / 2;
207 w_value >>= Limb.bit_count / 2;
208 }
209 }
210 },
211 else => {
212 @compileError("cannot set Int using type " ++ @typeName(T));
213 },
214 }
215 }
216
217 pub const ConvertError = error{
218 NegativeIntoUnsigned,
219 TargetTooSmall,
220 };
221
222 pub fn to(self: *const Int, comptime T: type) ConvertError!T {
223 switch (@typeId(T)) {
224 TypeId.Int => {
225 const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T;
226
227 if (self.bitcount() > 8 * @sizeOf(UT)) {
228 return error.TargetTooSmall;
229 }
230
231 var r: UT = 0;
232
233 if (@sizeOf(UT) <= @sizeOf(Limb)) {
234 r = UT(self.limbs[0]);
235 } else {
236 for (self.limbs[0..self.len]) |_, ri| {
237 const limb = self.limbs[self.len - ri - 1];
238 r <<= Limb.bit_count;
239 r |= limb;
240 }
241 }
242
243 if (!T.is_signed) {
244 return if (self.positive) r else error.NegativeIntoUnsigned;
245 } else {
246 return if (self.positive) T(r) else -T(r);
247 }
248 },
249 else => {
250 @compileError("cannot convert Int to type " ++ @typeName(T));
251 },
252 }
253 }
254
255 fn charToDigit(ch: u8, base: u8) !u8 {
256 const d = switch (ch) {
257 '0'...'9' => ch - '0',
258 'a'...'f' => (ch - 'a') + 0xa,
259 else => return error.InvalidCharForDigit,
260 };
261
262 return if (d < base) d else return error.DigitTooLargeForBase;
263 }
264
265 fn digitToChar(d: u8, base: u8) !u8 {
266 if (d >= base) {
267 return error.DigitTooLargeForBase;
268 }
269
270 return switch (d) {
271 0...9 => '0' + d,
272 0xa...0xf => ('a' - 0xa) + d,
273 else => unreachable,
274 };
275 }
276
277 pub fn setString(self: *Int, base: u8, value: []const u8) !void {
278 if (base < 2 or base > 16) {
279 return error.InvalidBase;
280 }
281
282 var i: usize = 0;
283 var positive = true;
284 if (value.len > 0 and value[0] == '-') {
285 positive = false;
286 i += 1;
287 }
288
289 try self.set(0);
290 for (value[i..]) |ch| {
291 const d = try charToDigit(ch, base);
292 try self.mul(self, base);
293 try self.add(self, d);
294 }
295 self.positive = positive;
296 }
297
298 pub fn toString(self: *const Int, allocator: *Allocator, base: u8) ![]const u8 {
299 if (base < 2 or base > 16) {
300 return error.InvalidBase;
301 }
302
303 var digits = ArrayList(u8).init(allocator);
304 try digits.ensureCapacity(self.sizeInBase(base) + 1);
305 defer digits.deinit();
306
307 if (self.eqZero()) {
308 try digits.append('0');
309 return digits.toOwnedSlice();
310 }
311
312 // Power of two: can do a single pass and use masks to extract digits.
313 if (base & (base - 1) == 0) {
314 const base_shift = math.log2_int(Limb, base);
315
316 for (self.limbs[0..self.len]) |limb| {
317 var shift: usize = 0;
318 while (shift < Limb.bit_count) : (shift += base_shift) {
319 const r = u8((limb >> Log2Limb(shift)) & Limb(base - 1));
320 const ch = try digitToChar(r, base);
321 try digits.append(ch);
322 }
323 }
324
325 while (true) {
326 // always will have a non-zero digit somewhere
327 const c = digits.pop();
328 if (c != '0') {
329 digits.append(c) catch unreachable;
330 break;
331 }
332 }
333 } // Non power-of-two: batch divisions per word size.
334 else {
335 const digits_per_limb = math.log(Limb, base, @maxValue(Limb));
336 var limb_base: Limb = 1;
337 var j: usize = 0;
338 while (j < digits_per_limb) : (j += 1) {
339 limb_base *= base;
340 }
341
342 var q = try self.clone();
343 q.positive = true;
344 var r = try Int.init(allocator);
345 var b = try Int.initSet(allocator, limb_base);
346
347 while (q.len >= 2) {
348 try Int.divTrunc(&q, &r, &q, &b);
349
350 var r_word = r.limbs[0];
351 var i: usize = 0;
352 while (i < digits_per_limb) : (i += 1) {
353 const ch = try digitToChar(u8(r_word % base), base);
354 r_word /= base;
355 try digits.append(ch);
356 }
357 }
358
359 {
360 debug.assert(q.len == 1);
361
362 var r_word = q.limbs[0];
363 while (r_word != 0) {
364 const ch = try digitToChar(u8(r_word % base), base);
365 r_word /= base;
366 try digits.append(ch);
367 }
368 }
369 }
370
371 if (!self.positive) {
372 try digits.append('-');
373 }
374
375 var s = digits.toOwnedSlice();
376 mem.reverse(u8, s);
377 return s;
378 }
379
380 // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
381 pub fn cmpAbs(a: *const Int, bv: var) i8 {
382 // TODO: Thread-local buffer.
383 var buffer: [wrapped_buffer_size]u8 = undefined;
384 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
385 var b = wrapInt(&stack.allocator, bv);
386
387 if (a.len < b.len) {
388 return -1;
389 }
390 if (a.len > b.len) {
391 return 1;
392 }
393
394 var i: usize = a.len - 1;
395 while (i != 0) : (i -= 1) {
396 if (a.limbs[i] != b.limbs[i]) {
397 break;
398 }
399 }
400
401 if (a.limbs[i] < b.limbs[i]) {
402 return -1;
403 } else if (a.limbs[i] > b.limbs[i]) {
404 return 1;
405 } else {
406 return 0;
407 }
408 }
409
410 // returns -1, 0, 1 if a < b, a == b or a > b respectively.
411 pub fn cmp(a: *const Int, bv: var) i8 {
412 var buffer: [wrapped_buffer_size]u8 = undefined;
413 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
414 var b = wrapInt(&stack.allocator, bv);
415
416 if (a.positive != b.positive) {
417 return if (a.positive) i8(1) else -1;
418 } else {
419 const r = cmpAbs(a, b);
420 return if (a.positive) r else -r;
421 }
422 }
423
424 // if a == 0
425 pub fn eqZero(a: *const Int) bool {
426 return a.len == 1 and a.limbs[0] == 0;
427 }
428
429 // if |a| == |b|
430 pub fn eqAbs(a: *const Int, b: var) bool {
431 return cmpAbs(a, b) == 0;
432 }
433
434 // if a == b
435 pub fn eq(a: *const Int, b: var) bool {
436 return cmp(a, b) == 0;
437 }
438
439 // Normalize for a possible single carry digit.
440 //
441 // [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
442 // [1, 2, 3, 4, 5] -> [1, 2, 3, 4, 5]
443 // [0] -> [0]
444 fn norm1(r: *Int, length: usize) void {
445 debug.assert(length > 0);
446 debug.assert(length <= r.limbs.len);
447
448 if (r.limbs[length - 1] == 0) {
449 r.len = if (length > 1) length - 1 else 1;
450 } else {
451 r.len = length;
452 }
453 }
454
455 // Normalize a possible sequence of leading zeros.
456 //
457 // [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
458 // [1, 2, 0, 0, 0] -> [1, 2]
459 // [0, 0, 0, 0, 0] -> [0]
460 fn normN(r: *Int, length: usize) void {
461 debug.assert(length > 0);
462 debug.assert(length <= r.limbs.len);
463
464 var j = length;
465 while (j > 0) : (j -= 1) {
466 if (r.limbs[j - 1] != 0) {
467 break;
468 }
469 }
470
471 // Handle zero
472 r.len = if (j != 0) j else 1;
473 }
474
475 // r = a + b
476 pub fn add(r: *Int, av: var, bv: var) Allocator.Error!void {
477 var buffer: [2 * wrapped_buffer_size]u8 = undefined;
478 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
479 var a = wrapInt(&stack.allocator, av);
480 var b = wrapInt(&stack.allocator, bv);
481
482 if (a.eqZero()) {
483 try r.copy(b);
484 return;
485 } else if (b.eqZero()) {
486 try r.copy(a);
487 return;
488 }
489
490 if (a.positive != b.positive) {
491 if (a.positive) {
492 // (a) + (-b) => a - b
493 const bp = Int{
494 .allocator = undefined,
495 .positive = true,
496 .limbs = b.limbs,
497 .len = b.len,
498 };
499 try r.sub(a, bp);
500 } else {
501 // (-a) + (b) => b - a
502 const ap = Int{
503 .allocator = undefined,
504 .positive = true,
505 .limbs = a.limbs,
506 .len = a.len,
507 };
508 try r.sub(b, ap);
509 }
510 } else {
511 if (a.len >= b.len) {
512 try r.ensureCapacity(a.len + 1);
513 lladd(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
514 r.norm1(a.len + 1);
515 } else {
516 try r.ensureCapacity(b.len + 1);
517 lladd(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
518 r.norm1(b.len + 1);
519 }
520
521 r.positive = a.positive;
522 }
523 }
524
525 // Knuth 4.3.1, Algorithm A.
526 fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void {
527 @setRuntimeSafety(false);
528 debug.assert(a.len != 0 and b.len != 0);
529 debug.assert(a.len >= b.len);
530 debug.assert(r.len >= a.len + 1);
531
532 var i: usize = 0;
533 var carry: Limb = 0;
534
535 while (i < b.len) : (i += 1) {
536 var c: Limb = 0;
537 c += Limb(@addWithOverflow(Limb, a[i], b[i], &r[i]));
538 c += Limb(@addWithOverflow(Limb, r[i], carry, &r[i]));
539 carry = c;
540 }
541
542 while (i < a.len) : (i += 1) {
543 carry = Limb(@addWithOverflow(Limb, a[i], carry, &r[i]));
544 }
545
546 r[i] = carry;
547 }
548
549 // r = a - b
550 pub fn sub(r: *Int, av: var, bv: var) !void {
551 var buffer: [wrapped_buffer_size]u8 = undefined;
552 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
553 var a = wrapInt(&stack.allocator, av);
554 var b = wrapInt(&stack.allocator, bv);
555
556 if (a.positive != b.positive) {
557 if (a.positive) {
558 // (a) - (-b) => a + b
559 const bp = Int{
560 .allocator = undefined,
561 .positive = true,
562 .limbs = b.limbs,
563 .len = b.len,
564 };
565 try r.add(a, bp);
566 } else {
567 // (-a) - (b) => -(a + b)
568 const ap = Int{
569 .allocator = undefined,
570 .positive = true,
571 .limbs = a.limbs,
572 .len = a.len,
573 };
574 try r.add(ap, b);
575 r.positive = false;
576 }
577 } else {
578 if (a.positive) {
579 // (a) - (b) => a - b
580 if (a.cmp(b) >= 0) {
581 try r.ensureCapacity(a.len + 1);
582 llsub(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
583 r.normN(a.len);
584 r.positive = true;
585 } else {
586 try r.ensureCapacity(b.len + 1);
587 llsub(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
588 r.normN(b.len);
589 r.positive = false;
590 }
591 } else {
592 // (-a) - (-b) => -(a - b)
593 if (a.cmp(b) < 0) {
594 try r.ensureCapacity(a.len + 1);
595 llsub(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
596 r.normN(a.len);
597 r.positive = false;
598 } else {
599 try r.ensureCapacity(b.len + 1);
600 llsub(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
601 r.normN(b.len);
602 r.positive = true;
603 }
604 }
605 }
606 }
607
608 // Knuth 4.3.1, Algorithm S.
609 fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void {
610 @setRuntimeSafety(false);
611 debug.assert(a.len != 0 and b.len != 0);
612 debug.assert(a.len > b.len or (a.len == b.len and a[a.len - 1] >= b[b.len - 1]));
613 debug.assert(r.len >= a.len);
614
615 var i: usize = 0;
616 var borrow: Limb = 0;
617
618 while (i < b.len) : (i += 1) {
619 var c: Limb = 0;
620 c += Limb(@subWithOverflow(Limb, a[i], b[i], &r[i]));
621 c += Limb(@subWithOverflow(Limb, r[i], borrow, &r[i]));
622 borrow = c;
623 }
624
625 while (i < a.len) : (i += 1) {
626 borrow = Limb(@subWithOverflow(Limb, a[i], borrow, &r[i]));
627 }
628
629 debug.assert(borrow == 0);
630 }
631
632 // rma = a * b
633 //
634 // For greatest efficiency, ensure rma does not alias a or b.
635 pub fn mul(rma: *Int, av: var, bv: var) !void {
636 var buffer: [2 * wrapped_buffer_size]u8 = undefined;
637 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
638 var a = wrapInt(&stack.allocator, av);
639 var b = wrapInt(&stack.allocator, bv);
640
641 var r = rma;
642 var aliased = rma == a or rma == b;
643
644 var sr: Int = undefined;
645 if (aliased) {
646 sr = try Int.initCapacity(rma.allocator, a.len + b.len);
647 r = &sr;
648 aliased = true;
649 }
650 defer if (aliased) {
651 rma.swap(r);
652 r.deinit();
653 };
654
655 try r.ensureCapacity(a.len + b.len);
656
657 if (a.len >= b.len) {
658 llmul(r.limbs, a.limbs[0..a.len], b.limbs[0..b.len]);
659 } else {
660 llmul(r.limbs, b.limbs[0..b.len], a.limbs[0..a.len]);
661 }
662
663 r.positive = a.positive == b.positive;
664 r.normN(a.len + b.len);
665 }
666
667 // a + b * c + *carry, sets carry to the overflow bits
668 pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb {
669 var r1: Limb = undefined;
670
671 // r1 = a + *carry
672 const c1 = Limb(@addWithOverflow(Limb, a, carry.*, &r1));
673
674 // r2 = b * c
675 //
676 // We still use a DoubleLimb here since the @mulWithOverflow builtin does not
677 // return the carry and lower bits separately so we would need to perform this
678 // anyway to get the carry bits. The branch on the overflow case costs more than
679 // just computing them unconditionally and splitting.
680 //
681 // This could be a single x86 mul instruction, which stores the carry/lower in rdx:rax.
682 const bc = DoubleLimb(b) * DoubleLimb(c);
683 const r2 = @truncate(Limb, bc);
684 const c2 = @truncate(Limb, bc >> Limb.bit_count);
685
686 // r1 = r1 + r2
687 const c3 = Limb(@addWithOverflow(Limb, r1, r2, &r1));
688
689 // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then
690 // c2 is at least <= @maxValue(Limb) - 2.
691 carry.* = c1 + c2 + c3;
692
693 return r1;
694 }
695
696 // Knuth 4.3.1, Algorithm M.
697 //
698 // r MUST NOT alias any of a or b.
699 fn llmul(r: []Limb, a: []const Limb, b: []const Limb) void {
700 @setRuntimeSafety(false);
701 debug.assert(a.len >= b.len);
702 debug.assert(r.len >= a.len + b.len);
703
704 mem.set(Limb, r[0 .. a.len + b.len], 0);
705
706 var i: usize = 0;
707 while (i < a.len) : (i += 1) {
708 var carry: Limb = 0;
709 var j: usize = 0;
710 while (j < b.len) : (j += 1) {
711 r[i + j] = @inlineCall(addMulLimbWithCarry, r[i + j], a[i], b[j], &carry);
712 }
713 r[i + j] = carry;
714 }
715 }
716
717 pub fn divFloor(q: *Int, r: *Int, a: var, b: var) !void {
718 try div(q, r, a, b);
719
720 // Trunc -> Floor.
721 if (!q.positive) {
722 try q.sub(q, 1);
723 try r.add(q, 1);
724 }
725 r.positive = b.positive;
726 }
727
728 pub fn divTrunc(q: *Int, r: *Int, a: var, b: var) !void {
729 try div(q, r, a, b);
730 r.positive = a.positive;
731 }
732
733 // Truncates by default.
734 fn div(quo: *Int, rem: *Int, av: var, bv: var) !void {
735 var buffer: [2 * wrapped_buffer_size]u8 = undefined;
736 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
737 var a = wrapInt(&stack.allocator, av);
738 var b = wrapInt(&stack.allocator, bv);
739
740 if (b.eqZero()) {
741 @panic("division by zero");
742 }
743 if (quo == rem) {
744 @panic("quo and rem cannot be same variable");
745 }
746
747 if (a.cmpAbs(b) < 0) {
748 // quo may alias a so handle rem first
749 try rem.copy(a);
750 rem.positive = a.positive == b.positive;
751
752 quo.positive = true;
753 quo.len = 1;
754 quo.limbs[0] = 0;
755 return;
756 }
757
758 if (b.len == 1) {
759 try quo.ensureCapacity(a.len);
760
761 lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[0..a.len], b.limbs[0]);
762 quo.norm1(a.len);
763 quo.positive = a.positive == b.positive;
764
765 rem.len = 1;
766 rem.positive = true;
767 } else {
768 // x and y are modified during division
769 var x = try a.clone();
770 defer x.deinit();
771
772 var y = try b.clone();
773 defer y.deinit();
774
775 // x may grow one limb during normalization
776 try quo.ensureCapacity(a.len + y.len);
777 try divN(quo.allocator, quo, rem, &x, &y);
778
779 quo.positive = a.positive == b.positive;
780 }
781 }
782
783 // Knuth 4.3.1, Exercise 16.
784 fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void {
785 @setRuntimeSafety(false);
786 debug.assert(a.len > 1 or a[0] >= b);
787 debug.assert(quo.len >= a.len);
788
789 rem.* = 0;
790 for (a) |_, ri| {
791 const i = a.len - ri - 1;
792 const pdiv = ((DoubleLimb(rem.*) << Limb.bit_count) | a[i]);
793
794 if (pdiv == 0) {
795 quo[i] = 0;
796 rem.* = 0;
797 } else if (pdiv < b) {
798 quo[i] = 0;
799 rem.* = @truncate(Limb, pdiv);
800 } else if (pdiv == b) {
801 quo[i] = 1;
802 rem.* = 0;
803 } else {
804 quo[i] = @truncate(Limb, @divTrunc(pdiv, b));
805 rem.* = @truncate(Limb, pdiv - (quo[i] *% b));
806 }
807 }
808 }
809
810 // Handbook of Applied Cryptography, 14.20
811 //
812 // x = qy + r where 0 <= r < y
813 fn divN(allocator: *Allocator, q: *Int, r: *Int, x: *Int, y: *Int) !void {
814 debug.assert(y.len >= 2);
815 debug.assert(x.len >= y.len);
816 debug.assert(q.limbs.len >= x.len + y.len - 1);
817 debug.assert(default_capacity >= 3); // see 3.2
818
819 var tmp = try Int.init(allocator);
820 defer tmp.deinit();
821
822 // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set)
823 const norm_shift = @clz(y.limbs[y.len - 1]);
824 try x.shiftLeft(x, norm_shift);
825 try y.shiftLeft(y, norm_shift);
826
827 const n = x.len - 1;
828 const t = y.len - 1;
829
830 // 1.
831 q.len = n - t + 1;
832 mem.set(Limb, q.limbs[0..q.len], 0);
833
834 // 2.
835 try tmp.shiftLeft(y, Limb.bit_count * (n - t));
836 while (x.cmp(&tmp) >= 0) {
837 q.limbs[n - t] += 1;
838 try x.sub(x, tmp);
839 }
840
841 // 3.
842 var i = n;
843 while (i > t) : (i -= 1) {
844 // 3.1
845 if (x.limbs[i] == y.limbs[t]) {
846 q.limbs[i - t - 1] = @maxValue(Limb);
847 } else {
848 const num = (DoubleLimb(x.limbs[i]) << Limb.bit_count) | DoubleLimb(x.limbs[i - 1]);
849 const z = Limb(num / DoubleLimb(y.limbs[t]));
850 q.limbs[i - t - 1] = if (z > @maxValue(Limb)) @maxValue(Limb) else Limb(z);
851 }
852
853 // 3.2
854 tmp.limbs[0] = if (i >= 2) x.limbs[i - 2] else 0;
855 tmp.limbs[1] = if (i >= 1) x.limbs[i - 1] else 0;
856 tmp.limbs[2] = x.limbs[i];
857 tmp.normN(3);
858
859 while (true) {
860 // 2x1 limb multiplication unrolled against single-limb q[i-t-1]
861 var carry: Limb = 0;
862 r.limbs[0] = addMulLimbWithCarry(0, if (t >= 1) y.limbs[t - 1] else 0, q.limbs[i - t - 1], &carry);
863 r.limbs[1] = addMulLimbWithCarry(0, y.limbs[t], q.limbs[i - t - 1], &carry);
864 r.limbs[2] = carry;
865 r.normN(3);
866
867 if (r.cmpAbs(&tmp) <= 0) {
868 break;
869 }
870
871 q.limbs[i - t - 1] -= 1;
872 }
873
874 // 3.3
875 try tmp.set(q.limbs[i - t - 1]);
876 try tmp.mul(&tmp, y);
877 try tmp.shiftLeft(&tmp, Limb.bit_count * (i - t - 1));
878 try x.sub(x, &tmp);
879
880 if (!x.positive) {
881 try tmp.shiftLeft(y, Limb.bit_count * (i - t - 1));
882 try x.add(x, &tmp);
883 q.limbs[i - t - 1] -= 1;
884 }
885 }
886
887 // Denormalize
888 q.normN(q.len);
889
890 try r.shiftRight(x, norm_shift);
891 r.normN(r.len);
892 }
893
894 // r = a << shift, in other words, r = a * 2^shift
895 pub fn shiftLeft(r: *Int, av: var, shift: usize) !void {
896 var buffer: [wrapped_buffer_size]u8 = undefined;
897 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
898 var a = wrapInt(&stack.allocator, av);
899
900 try r.ensureCapacity(a.len + (shift / Limb.bit_count) + 1);
901 llshl(r.limbs[0..], a.limbs[0..a.len], shift);
902 r.norm1(a.len + (shift / Limb.bit_count) + 1);
903 r.positive = a.positive;
904 }
905
906 fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
907 @setRuntimeSafety(false);
908 debug.assert(a.len >= 1);
909 debug.assert(r.len >= a.len + (shift / Limb.bit_count) + 1);
910
911 const limb_shift = shift / Limb.bit_count + 1;
912 const interior_limb_shift = Log2Limb(shift % Limb.bit_count);
913
914 var carry: Limb = 0;
915 var i: usize = 0;
916 while (i < a.len) : (i += 1) {
917 const src_i = a.len - i - 1;
918 const dst_i = src_i + limb_shift;
919
920 const src_digit = a[src_i];
921 r[dst_i] = carry | @inlineCall(math.shr, Limb, src_digit, Limb.bit_count - Limb(interior_limb_shift));
922 carry = (src_digit << interior_limb_shift);
923 }
924
925 r[limb_shift - 1] = carry;
926 mem.set(Limb, r[0 .. limb_shift - 1], 0);
927 }
928
929 // r = a >> shift
930 pub fn shiftRight(r: *Int, av: var, shift: usize) !void {
931 var buffer: [wrapped_buffer_size]u8 = undefined;
932 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
933 var a = wrapInt(&stack.allocator, av);
934
935 if (a.len <= shift / Limb.bit_count) {
936 r.len = 1;
937 r.limbs[0] = 0;
938 r.positive = true;
939 return;
940 }
941
942 try r.ensureCapacity(a.len - (shift / Limb.bit_count));
943 const r_len = llshr(r.limbs[0..], a.limbs[0..a.len], shift);
944 r.len = a.len - (shift / Limb.bit_count);
945 r.positive = a.positive;
946 }
947
948 fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
949 @setRuntimeSafety(false);
950 debug.assert(a.len >= 1);
951 debug.assert(r.len >= a.len - (shift / Limb.bit_count));
952
953 const limb_shift = shift / Limb.bit_count;
954 const interior_limb_shift = Log2Limb(shift % Limb.bit_count);
955
956 var carry: Limb = 0;
957 var i: usize = 0;
958 while (i < a.len - limb_shift) : (i += 1) {
959 const src_i = a.len - i - 1;
960 const dst_i = src_i - limb_shift;
961
962 const src_digit = a[src_i];
963 r[dst_i] = carry | (src_digit >> interior_limb_shift);
964 carry = @inlineCall(math.shl, Limb, src_digit, Limb.bit_count - Limb(interior_limb_shift));
965 }
966 }
967
968 // r = a | b
969 pub fn bitOr(r: *Int, av: var, bv: var) !void {
970 var buffer: [2 * wrapped_buffer_size]u8 = undefined;
971 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
972 var a = wrapInt(&stack.allocator, av);
973 var b = wrapInt(&stack.allocator, bv);
974
975 if (a.len > b.len) {
976 try r.ensureCapacity(a.len);
977 llor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
978 r.len = a.len;
979 } else {
980 try r.ensureCapacity(b.len);
981 llor(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
982 r.len = b.len;
983 }
984 }
985
986 fn llor(r: []Limb, a: []const Limb, b: []const Limb) void {
987 @setRuntimeSafety(false);
988 debug.assert(r.len >= a.len);
989 debug.assert(a.len >= b.len);
990
991 var i: usize = 0;
992 while (i < b.len) : (i += 1) {
993 r[i] = a[i] | b[i];
994 }
995 while (i < a.len) : (i += 1) {
996 r[i] = a[i];
997 }
998 }
999
1000 // r = a & b
1001 pub fn bitAnd(r: *Int, av: var, bv: var) !void {
1002 var buffer: [2 * wrapped_buffer_size]u8 = undefined;
1003 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
1004 var a = wrapInt(&stack.allocator, av);
1005 var b = wrapInt(&stack.allocator, bv);
1006
1007 if (a.len > b.len) {
1008 try r.ensureCapacity(b.len);
1009 lland(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
1010 r.normN(b.len);
1011 } else {
1012 try r.ensureCapacity(a.len);
1013 lland(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
1014 r.normN(a.len);
1015 }
1016 }
1017
1018 fn lland(r: []Limb, a: []const Limb, b: []const Limb) void {
1019 @setRuntimeSafety(false);
1020 debug.assert(r.len >= b.len);
1021 debug.assert(a.len >= b.len);
1022
1023 var i: usize = 0;
1024 while (i < b.len) : (i += 1) {
1025 r[i] = a[i] & b[i];
1026 }
1027 }
1028
1029 // r = a ^ b
1030 pub fn bitXor(r: *Int, av: var, bv: var) !void {
1031 var buffer: [2 * wrapped_buffer_size]u8 = undefined;
1032 var stack = std.heap.FixedBufferAllocator.init(buffer[0..]);
1033 var a = wrapInt(&stack.allocator, av);
1034 var b = wrapInt(&stack.allocator, bv);
1035
1036 if (a.len > b.len) {
1037 try r.ensureCapacity(a.len);
1038 llxor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
1039 r.normN(a.len);
1040 } else {
1041 try r.ensureCapacity(b.len);
1042 llxor(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
1043 r.normN(b.len);
1044 }
1045 }
1046
1047 fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void {
1048 @setRuntimeSafety(false);
1049 debug.assert(r.len >= a.len);
1050 debug.assert(a.len >= b.len);
1051
1052 var i: usize = 0;
1053 while (i < b.len) : (i += 1) {
1054 r[i] = a[i] ^ b[i];
1055 }
1056 while (i < a.len) : (i += 1) {
1057 r[i] = a[i];
1058 }
1059 }
1060};
1061
1062// NOTE: All the following tests assume the max machine-word will be 64-bit.
1063//
1064// They will still run on larger than this and should pass, but the multi-limb code-paths
1065// may be untested in some cases.
1066
1067const u256 = @IntType(false, 256);
1068var al = debug.global_allocator;
1069
1070test "big.int comptime_int set" {
1071 comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab;
1072 var a = try Int.initSet(al, s);
1073
1074 const s_limb_count = 128 / Limb.bit_count;
1075
1076 comptime var i: usize = 0;
1077 inline while (i < s_limb_count) : (i += 1) {
1078 const result = Limb(s & @maxValue(Limb));
1079 s >>= Limb.bit_count / 2;
1080 s >>= Limb.bit_count / 2;
1081 debug.assert(a.limbs[i] == result);
1082 }
1083}
1084
1085test "big.int comptime_int set negative" {
1086 var a = try Int.initSet(al, -10);
1087
1088 debug.assert(a.limbs[0] == 10);
1089 debug.assert(a.positive == false);
1090}
1091
1092test "big.int int set unaligned small" {
1093 var a = try Int.initSet(al, u7(45));
1094
1095 debug.assert(a.limbs[0] == 45);
1096 debug.assert(a.positive == true);
1097}
1098
1099test "big.int comptime_int to" {
1100 const a = try Int.initSet(al, 0xefffffff00000001eeeeeeefaaaaaaab);
1101
1102 debug.assert((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab);
1103}
1104
1105test "big.int sub-limb to" {
1106 const a = try Int.initSet(al, 10);
1107
1108 debug.assert((try a.to(u8)) == 10);
1109}
1110
1111test "big.int to target too small error" {
1112 const a = try Int.initSet(al, 0xffffffff);
1113
1114 if (a.to(u8)) |_| {
1115 unreachable;
1116 } else |err| {
1117 debug.assert(err == error.TargetTooSmall);
1118 }
1119}
1120
1121test "big.int norm1" {
1122 var a = try Int.init(al);
1123 try a.ensureCapacity(8);
1124
1125 a.limbs[0] = 1;
1126 a.limbs[1] = 2;
1127 a.limbs[2] = 3;
1128 a.limbs[3] = 0;
1129 a.norm1(4);
1130 debug.assert(a.len == 3);
1131
1132 a.limbs[0] = 1;
1133 a.limbs[1] = 2;
1134 a.limbs[2] = 3;
1135 a.norm1(3);
1136 debug.assert(a.len == 3);
1137
1138 a.limbs[0] = 0;
1139 a.limbs[1] = 0;
1140 a.norm1(2);
1141 debug.assert(a.len == 1);
1142
1143 a.limbs[0] = 0;
1144 a.norm1(1);
1145 debug.assert(a.len == 1);
1146}
1147
1148test "big.int normN" {
1149 var a = try Int.init(al);
1150 try a.ensureCapacity(8);
1151
1152 a.limbs[0] = 1;
1153 a.limbs[1] = 2;
1154 a.limbs[2] = 0;
1155 a.limbs[3] = 0;
1156 a.normN(4);
1157 debug.assert(a.len == 2);
1158
1159 a.limbs[0] = 1;
1160 a.limbs[1] = 2;
1161 a.limbs[2] = 3;
1162 a.normN(3);
1163 debug.assert(a.len == 3);
1164
1165 a.limbs[0] = 0;
1166 a.limbs[1] = 0;
1167 a.limbs[2] = 0;
1168 a.limbs[3] = 0;
1169 a.normN(4);
1170 debug.assert(a.len == 1);
1171
1172 a.limbs[0] = 0;
1173 a.normN(1);
1174 debug.assert(a.len == 1);
1175}
1176
1177test "big.int parity" {
1178 var a = try Int.init(al);
1179 try a.set(0);
1180 debug.assert(a.isEven());
1181 debug.assert(!a.isOdd());
1182
1183 try a.set(7);
1184 debug.assert(!a.isEven());
1185 debug.assert(a.isOdd());
1186}
1187
1188test "big.int bitcount + sizeInBase" {
1189 var a = try Int.init(al);
1190
1191 try a.set(0b100);
1192 debug.assert(a.bitcount() == 3);
1193 debug.assert(a.sizeInBase(2) >= 3);
1194 debug.assert(a.sizeInBase(10) >= 1);
1195
1196 try a.set(0xffffffff);
1197 debug.assert(a.bitcount() == 32);
1198 debug.assert(a.sizeInBase(2) >= 32);
1199 debug.assert(a.sizeInBase(10) >= 10);
1200
1201 try a.shiftLeft(&a, 5000);
1202 debug.assert(a.bitcount() == 5032);
1203 debug.assert(a.sizeInBase(2) >= 5032);
1204 a.positive = false;
1205
1206 debug.assert(a.bitcount() == 5033);
1207 debug.assert(a.sizeInBase(2) >= 5033);
1208}
1209
1210test "big.int string set" {
1211 var a = try Int.init(al);
1212 try a.setString(10, "120317241209124781241290847124");
1213
1214 debug.assert((try a.to(u128)) == 120317241209124781241290847124);
1215}
1216
1217test "big.int string negative" {
1218 var a = try Int.init(al);
1219 try a.setString(10, "-1023");
1220 debug.assert((try a.to(i32)) == -1023);
1221}
1222
1223test "big.int string set bad char error" {
1224 var a = try Int.init(al);
1225 a.setString(10, "x") catch |err| debug.assert(err == error.InvalidCharForDigit);
1226}
1227
1228test "big.int string set bad base error" {
1229 var a = try Int.init(al);
1230 a.setString(45, "10") catch |err| debug.assert(err == error.InvalidBase);
1231}
1232
1233test "big.int string to" {
1234 const a = try Int.initSet(al, 120317241209124781241290847124);
1235
1236 const as = try a.toString(al, 10);
1237 const es = "120317241209124781241290847124";
1238
1239 debug.assert(mem.eql(u8, as, es));
1240}
1241
1242test "big.int string to base base error" {
1243 const a = try Int.initSet(al, 0xffffffff);
1244
1245 if (a.toString(al, 45)) |_| {
1246 unreachable;
1247 } else |err| {
1248 debug.assert(err == error.InvalidBase);
1249 }
1250}
1251
1252test "big.int string to base 2" {
1253 const a = try Int.initSet(al, -0b1011);
1254
1255 const as = try a.toString(al, 2);
1256 const es = "-1011";
1257
1258 debug.assert(mem.eql(u8, as, es));
1259}
1260
1261test "big.int string to base 16" {
1262 const a = try Int.initSet(al, 0xefffffff00000001eeeeeeefaaaaaaab);
1263
1264 const as = try a.toString(al, 16);
1265 const es = "efffffff00000001eeeeeeefaaaaaaab";
1266
1267 debug.assert(mem.eql(u8, as, es));
1268}
1269
1270test "big.int neg string to" {
1271 const a = try Int.initSet(al, -123907434);
1272
1273 const as = try a.toString(al, 10);
1274 const es = "-123907434";
1275
1276 debug.assert(mem.eql(u8, as, es));
1277}
1278
1279test "big.int zero string to" {
1280 const a = try Int.initSet(al, 0);
1281
1282 const as = try a.toString(al, 10);
1283 const es = "0";
1284
1285 debug.assert(mem.eql(u8, as, es));
1286}
1287
1288test "big.int clone" {
1289 var a = try Int.initSet(al, 1234);
1290 const b = try a.clone();
1291
1292 debug.assert((try a.to(u32)) == 1234);
1293 debug.assert((try b.to(u32)) == 1234);
1294
1295 try a.set(77);
1296 debug.assert((try a.to(u32)) == 77);
1297 debug.assert((try b.to(u32)) == 1234);
1298}
1299
1300test "big.int swap" {
1301 var a = try Int.initSet(al, 1234);
1302 var b = try Int.initSet(al, 5678);
1303
1304 debug.assert((try a.to(u32)) == 1234);
1305 debug.assert((try b.to(u32)) == 5678);
1306
1307 a.swap(&b);
1308
1309 debug.assert((try a.to(u32)) == 5678);
1310 debug.assert((try b.to(u32)) == 1234);
1311}
1312
1313test "big.int to negative" {
1314 var a = try Int.initSet(al, -10);
1315
1316 debug.assert((try a.to(i32)) == -10);
1317}
1318
1319test "big.int compare" {
1320 var a = try Int.initSet(al, -11);
1321 var b = try Int.initSet(al, 10);
1322
1323 debug.assert(a.cmpAbs(&b) == 1);
1324 debug.assert(a.cmp(&b) == -1);
1325}
1326
1327test "big.int compare similar" {
1328 var a = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeee);
1329 var b = try Int.initSet(al, 0xffffffffeeeeeeeeffffffffeeeeeeef);
1330
1331 debug.assert(a.cmpAbs(&b) == -1);
1332 debug.assert(b.cmpAbs(&a) == 1);
1333}
1334
1335test "big.int compare different limb size" {
1336 var a = try Int.initSet(al, @maxValue(Limb) + 1);
1337 var b = try Int.initSet(al, 1);
1338
1339 debug.assert(a.cmpAbs(&b) == 1);
1340 debug.assert(b.cmpAbs(&a) == -1);
1341}
1342
1343test "big.int compare multi-limb" {
1344 var a = try Int.initSet(al, -0x7777777799999999ffffeeeeffffeeeeffffeeeef);
1345 var b = try Int.initSet(al, 0x7777777799999999ffffeeeeffffeeeeffffeeeee);
1346
1347 debug.assert(a.cmpAbs(&b) == 1);
1348 debug.assert(a.cmp(&b) == -1);
1349}
1350
1351test "big.int equality" {
1352 var a = try Int.initSet(al, 0xffffffff1);
1353 var b = try Int.initSet(al, -0xffffffff1);
1354
1355 debug.assert(a.eqAbs(&b));
1356 debug.assert(!a.eq(&b));
1357}
1358
1359test "big.int abs" {
1360 var a = try Int.initSet(al, -5);
1361
1362 a.abs();
1363 debug.assert((try a.to(u32)) == 5);
1364
1365 a.abs();
1366 debug.assert((try a.to(u32)) == 5);
1367}
1368
1369test "big.int negate" {
1370 var a = try Int.initSet(al, 5);
1371
1372 a.negate();
1373 debug.assert((try a.to(i32)) == -5);
1374
1375 a.negate();
1376 debug.assert((try a.to(i32)) == 5);
1377}
1378
1379test "big.int add single-single" {
1380 var a = try Int.initSet(al, 50);
1381 var b = try Int.initSet(al, 5);
1382
1383 var c = try Int.init(al);
1384 try c.add(&a, &b);
1385
1386 debug.assert((try c.to(u32)) == 55);
1387}
1388
1389test "big.int add multi-single" {
1390 var a = try Int.initSet(al, @maxValue(Limb) + 1);
1391 var b = try Int.initSet(al, 1);
1392
1393 var c = try Int.init(al);
1394
1395 try c.add(&a, &b);
1396 debug.assert((try c.to(DoubleLimb)) == @maxValue(Limb) + 2);
1397
1398 try c.add(&b, &a);
1399 debug.assert((try c.to(DoubleLimb)) == @maxValue(Limb) + 2);
1400}
1401
1402test "big.int add multi-multi" {
1403 const op1 = 0xefefefef7f7f7f7f;
1404 const op2 = 0xfefefefe9f9f9f9f;
1405 var a = try Int.initSet(al, op1);
1406 var b = try Int.initSet(al, op2);
1407
1408 var c = try Int.init(al);
1409 try c.add(&a, &b);
1410
1411 debug.assert((try c.to(u128)) == op1 + op2);
1412}
1413
1414test "big.int add zero-zero" {
1415 var a = try Int.initSet(al, 0);
1416 var b = try Int.initSet(al, 0);
1417
1418 var c = try Int.init(al);
1419 try c.add(&a, &b);
1420
1421 debug.assert((try c.to(u32)) == 0);
1422}
1423
1424test "big.int add alias multi-limb nonzero-zero" {
1425 const op1 = 0xffffffff777777771;
1426 var a = try Int.initSet(al, op1);
1427 var b = try Int.initSet(al, 0);
1428
1429 try a.add(&a, &b);
1430
1431 debug.assert((try a.to(u128)) == op1);
1432}
1433
1434test "big.int add sign" {
1435 var a = try Int.init(al);
1436
1437 try a.add(1, 2);
1438 debug.assert((try a.to(i32)) == 3);
1439
1440 try a.add(-1, 2);
1441 debug.assert((try a.to(i32)) == 1);
1442
1443 try a.add(1, -2);
1444 debug.assert((try a.to(i32)) == -1);
1445
1446 try a.add(-1, -2);
1447 debug.assert((try a.to(i32)) == -3);
1448}
1449
1450test "big.int sub single-single" {
1451 var a = try Int.initSet(al, 50);
1452 var b = try Int.initSet(al, 5);
1453
1454 var c = try Int.init(al);
1455 try c.sub(&a, &b);
1456
1457 debug.assert((try c.to(u32)) == 45);
1458}
1459
1460test "big.int sub multi-single" {
1461 var a = try Int.initSet(al, @maxValue(Limb) + 1);
1462 var b = try Int.initSet(al, 1);
1463
1464 var c = try Int.init(al);
1465 try c.sub(&a, &b);
1466
1467 debug.assert((try c.to(Limb)) == @maxValue(Limb));
1468}
1469
1470test "big.int sub multi-multi" {
1471 const op1 = 0xefefefefefefefefefefefef;
1472 const op2 = 0xabababababababababababab;
1473
1474 var a = try Int.initSet(al, op1);
1475 var b = try Int.initSet(al, op2);
1476
1477 var c = try Int.init(al);
1478 try c.sub(&a, &b);
1479
1480 debug.assert((try c.to(u128)) == op1 - op2);
1481}
1482
1483test "big.int sub equal" {
1484 var a = try Int.initSet(al, 0x11efefefefefefefefefefefef);
1485 var b = try Int.initSet(al, 0x11efefefefefefefefefefefef);
1486
1487 var c = try Int.init(al);
1488 try c.sub(&a, &b);
1489
1490 debug.assert((try c.to(u32)) == 0);
1491}
1492
1493test "big.int sub sign" {
1494 var a = try Int.init(al);
1495
1496 try a.sub(1, 2);
1497 debug.assert((try a.to(i32)) == -1);
1498
1499 try a.sub(-1, 2);
1500 debug.assert((try a.to(i32)) == -3);
1501
1502 try a.sub(1, -2);
1503 debug.assert((try a.to(i32)) == 3);
1504
1505 try a.sub(-1, -2);
1506 debug.assert((try a.to(i32)) == 1);
1507
1508 try a.sub(-2, -1);
1509 debug.assert((try a.to(i32)) == -1);
1510}
1511
1512test "big.int mul single-single" {
1513 var a = try Int.initSet(al, 50);
1514 var b = try Int.initSet(al, 5);
1515
1516 var c = try Int.init(al);
1517 try c.mul(&a, &b);
1518
1519 debug.assert((try c.to(u64)) == 250);
1520}
1521
1522test "big.int mul multi-single" {
1523 var a = try Int.initSet(al, @maxValue(Limb));
1524 var b = try Int.initSet(al, 2);
1525
1526 var c = try Int.init(al);
1527 try c.mul(&a, &b);
1528
1529 debug.assert((try c.to(DoubleLimb)) == 2 * @maxValue(Limb));
1530}
1531
1532test "big.int mul multi-multi" {
1533 const op1 = 0x998888efefefefefefefef;
1534 const op2 = 0x333000abababababababab;
1535 var a = try Int.initSet(al, op1);
1536 var b = try Int.initSet(al, op2);
1537
1538 var c = try Int.init(al);
1539 try c.mul(&a, &b);
1540
1541 debug.assert((try c.to(u256)) == op1 * op2);
1542}
1543
1544test "big.int mul alias r with a" {
1545 var a = try Int.initSet(al, @maxValue(Limb));
1546 var b = try Int.initSet(al, 2);
1547
1548 try a.mul(&a, &b);
1549
1550 debug.assert((try a.to(DoubleLimb)) == 2 * @maxValue(Limb));
1551}
1552
1553test "big.int mul alias r with b" {
1554 var a = try Int.initSet(al, @maxValue(Limb));
1555 var b = try Int.initSet(al, 2);
1556
1557 try a.mul(&b, &a);
1558
1559 debug.assert((try a.to(DoubleLimb)) == 2 * @maxValue(Limb));
1560}
1561
1562test "big.int mul alias r with a and b" {
1563 var a = try Int.initSet(al, @maxValue(Limb));
1564
1565 try a.mul(&a, &a);
1566
1567 debug.assert((try a.to(DoubleLimb)) == @maxValue(Limb) * @maxValue(Limb));
1568}
1569
1570test "big.int mul a*0" {
1571 var a = try Int.initSet(al, 0xefefefefefefefef);
1572 var b = try Int.initSet(al, 0);
1573
1574 var c = try Int.init(al);
1575 try c.mul(&a, &b);
1576
1577 debug.assert((try c.to(u32)) == 0);
1578}
1579
1580test "big.int mul 0*0" {
1581 var a = try Int.initSet(al, 0);
1582 var b = try Int.initSet(al, 0);
1583
1584 var c = try Int.init(al);
1585 try c.mul(&a, &b);
1586
1587 debug.assert((try c.to(u32)) == 0);
1588}
1589
1590test "big.int div single-single no rem" {
1591 var a = try Int.initSet(al, 50);
1592 var b = try Int.initSet(al, 5);
1593
1594 var q = try Int.init(al);
1595 var r = try Int.init(al);
1596 try Int.divTrunc(&q, &r, &a, &b);
1597
1598 debug.assert((try q.to(u32)) == 10);
1599 debug.assert((try r.to(u32)) == 0);
1600}
1601
1602test "big.int div single-single with rem" {
1603 var a = try Int.initSet(al, 49);
1604 var b = try Int.initSet(al, 5);
1605
1606 var q = try Int.init(al);
1607 var r = try Int.init(al);
1608 try Int.divTrunc(&q, &r, &a, &b);
1609
1610 debug.assert((try q.to(u32)) == 9);
1611 debug.assert((try r.to(u32)) == 4);
1612}
1613
1614test "big.int div multi-single no rem" {
1615 const op1 = 0xffffeeeeddddcccc;
1616 const op2 = 34;
1617
1618 var a = try Int.initSet(al, op1);
1619 var b = try Int.initSet(al, op2);
1620
1621 var q = try Int.init(al);
1622 var r = try Int.init(al);
1623 try Int.divTrunc(&q, &r, &a, &b);
1624
1625 debug.assert((try q.to(u64)) == op1 / op2);
1626 debug.assert((try r.to(u64)) == 0);
1627}
1628
1629test "big.int div multi-single with rem" {
1630 const op1 = 0xffffeeeeddddcccf;
1631 const op2 = 34;
1632
1633 var a = try Int.initSet(al, op1);
1634 var b = try Int.initSet(al, op2);
1635
1636 var q = try Int.init(al);
1637 var r = try Int.init(al);
1638 try Int.divTrunc(&q, &r, &a, &b);
1639
1640 debug.assert((try q.to(u64)) == op1 / op2);
1641 debug.assert((try r.to(u64)) == 3);
1642}
1643
1644test "big.int div multi>2-single" {
1645 const op1 = 0xfefefefefefefefefefefefefefefefe;
1646 const op2 = 0xefab8;
1647
1648 var a = try Int.initSet(al, op1);
1649 var b = try Int.initSet(al, op2);
1650
1651 var q = try Int.init(al);
1652 var r = try Int.init(al);
1653 try Int.divTrunc(&q, &r, &a, &b);
1654
1655 debug.assert((try q.to(u128)) == op1 / op2);
1656 debug.assert((try r.to(u32)) == 0x3e4e);
1657}
1658
1659test "big.int div single-single q < r" {
1660 var a = try Int.initSet(al, 0x0078f432);
1661 var b = try Int.initSet(al, 0x01000000);
1662
1663 var q = try Int.init(al);
1664 var r = try Int.init(al);
1665 try Int.divTrunc(&q, &r, &a, &b);
1666
1667 debug.assert((try q.to(u64)) == 0);
1668 debug.assert((try r.to(u64)) == 0x0078f432);
1669}
1670
1671test "big.int div single-single q == r" {
1672 var a = try Int.initSet(al, 10);
1673 var b = try Int.initSet(al, 10);
1674
1675 var q = try Int.init(al);
1676 var r = try Int.init(al);
1677 try Int.divTrunc(&q, &r, &a, &b);
1678
1679 debug.assert((try q.to(u64)) == 1);
1680 debug.assert((try r.to(u64)) == 0);
1681}
1682
1683test "big.int div q=0 alias" {
1684 var a = try Int.initSet(al, 3);
1685 var b = try Int.initSet(al, 10);
1686
1687 try Int.divTrunc(&a, &b, &a, &b);
1688
1689 debug.assert((try a.to(u64)) == 0);
1690 debug.assert((try b.to(u64)) == 3);
1691}
1692
1693test "big.int div multi-multi q < r" {
1694 const op1 = 0x1ffffffff0078f432;
1695 const op2 = 0x1ffffffff01000000;
1696 var a = try Int.initSet(al, op1);
1697 var b = try Int.initSet(al, op2);
1698
1699 var q = try Int.init(al);
1700 var r = try Int.init(al);
1701 try Int.divTrunc(&q, &r, &a, &b);
1702
1703 debug.assert((try q.to(u128)) == 0);
1704 debug.assert((try r.to(u128)) == op1);
1705}
1706
1707test "big.int div trunc single-single +/+" {
1708 const u: i32 = 5;
1709 const v: i32 = 3;
1710
1711 var a = try Int.initSet(al, u);
1712 var b = try Int.initSet(al, v);
1713
1714 var q = try Int.init(al);
1715 var r = try Int.init(al);
1716 try Int.divTrunc(&q, &r, &a, &b);
1717
1718 // n = q * d + r
1719 // 5 = 1 * 3 + 2
1720 const eq = @divTrunc(u, v);
1721 const er = @mod(u, v);
1722
1723 debug.assert((try q.to(i32)) == eq);
1724 debug.assert((try r.to(i32)) == er);
1725}
1726
1727test "big.int div trunc single-single -/+" {
1728 const u: i32 = -5;
1729 const v: i32 = 3;
1730
1731 var a = try Int.initSet(al, u);
1732 var b = try Int.initSet(al, v);
1733
1734 var q = try Int.init(al);
1735 var r = try Int.init(al);
1736 try Int.divTrunc(&q, &r, &a, &b);
1737
1738 // n = q * d + r
1739 // -5 = 1 * -3 - 2
1740 const eq = -1;
1741 const er = -2;
1742
1743 debug.assert((try q.to(i32)) == eq);
1744 debug.assert((try r.to(i32)) == er);
1745}
1746
1747test "big.int div trunc single-single +/-" {
1748 const u: i32 = 5;
1749 const v: i32 = -3;
1750
1751 var a = try Int.initSet(al, u);
1752 var b = try Int.initSet(al, v);
1753
1754 var q = try Int.init(al);
1755 var r = try Int.init(al);
1756 try Int.divTrunc(&q, &r, &a, &b);
1757
1758 // n = q * d + r
1759 // 5 = -1 * -3 + 2
1760 const eq = -1;
1761 const er = 2;
1762
1763 debug.assert((try q.to(i32)) == eq);
1764 debug.assert((try r.to(i32)) == er);
1765}
1766
1767test "big.int div trunc single-single -/-" {
1768 const u: i32 = -5;
1769 const v: i32 = -3;
1770
1771 var a = try Int.initSet(al, u);
1772 var b = try Int.initSet(al, v);
1773
1774 var q = try Int.init(al);
1775 var r = try Int.init(al);
1776 try Int.divTrunc(&q, &r, &a, &b);
1777
1778 // n = q * d + r
1779 // -5 = 1 * -3 - 2
1780 const eq = 1;
1781 const er = -2;
1782
1783 debug.assert((try q.to(i32)) == eq);
1784 debug.assert((try r.to(i32)) == er);
1785}
1786
1787test "big.int div floor single-single +/+" {
1788 const u: i32 = 5;
1789 const v: i32 = 3;
1790
1791 var a = try Int.initSet(al, u);
1792 var b = try Int.initSet(al, v);
1793
1794 var q = try Int.init(al);
1795 var r = try Int.init(al);
1796 try Int.divFloor(&q, &r, &a, &b);
1797
1798 // n = q * d + r
1799 // 5 = 1 * 3 + 2
1800 const eq = 1;
1801 const er = 2;
1802
1803 debug.assert((try q.to(i32)) == eq);
1804 debug.assert((try r.to(i32)) == er);
1805}
1806
1807test "big.int div floor single-single -/+" {
1808 const u: i32 = -5;
1809 const v: i32 = 3;
1810
1811 var a = try Int.initSet(al, u);
1812 var b = try Int.initSet(al, v);
1813
1814 var q = try Int.init(al);
1815 var r = try Int.init(al);
1816 try Int.divFloor(&q, &r, &a, &b);
1817
1818 // n = q * d + r
1819 // -5 = -2 * 3 + 1
1820 const eq = -2;
1821 const er = 1;
1822
1823 debug.assert((try q.to(i32)) == eq);
1824 debug.assert((try r.to(i32)) == er);
1825}
1826
1827test "big.int div floor single-single +/-" {
1828 const u: i32 = 5;
1829 const v: i32 = -3;
1830
1831 var a = try Int.initSet(al, u);
1832 var b = try Int.initSet(al, v);
1833
1834 var q = try Int.init(al);
1835 var r = try Int.init(al);
1836 try Int.divFloor(&q, &r, &a, &b);
1837
1838 // n = q * d + r
1839 // 5 = -2 * -3 - 1
1840 const eq = -2;
1841 const er = -1;
1842
1843 debug.assert((try q.to(i32)) == eq);
1844 debug.assert((try r.to(i32)) == er);
1845}
1846
1847test "big.int div floor single-single -/-" {
1848 const u: i32 = -5;
1849 const v: i32 = -3;
1850
1851 var a = try Int.initSet(al, u);
1852 var b = try Int.initSet(al, v);
1853
1854 var q = try Int.init(al);
1855 var r = try Int.init(al);
1856 try Int.divFloor(&q, &r, &a, &b);
1857
1858 // n = q * d + r
1859 // -5 = 2 * -3 + 1
1860 const eq = 1;
1861 const er = -2;
1862
1863 debug.assert((try q.to(i32)) == eq);
1864 debug.assert((try r.to(i32)) == er);
1865}
1866
1867test "big.int div multi-multi with rem" {
1868 var a = try Int.initSet(al, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999);
1869 var b = try Int.initSet(al, 0x99990000111122223333);
1870
1871 var q = try Int.init(al);
1872 var r = try Int.init(al);
1873 try Int.divTrunc(&q, &r, &a, &b);
1874
1875 debug.assert((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
1876 debug.assert((try r.to(u128)) == 0x28de0acacd806823638);
1877}
1878
1879test "big.int div multi-multi no rem" {
1880 var a = try Int.initSet(al, 0x8888999911110000ffffeeeedb4fec200ee3a4286361);
1881 var b = try Int.initSet(al, 0x99990000111122223333);
1882
1883 var q = try Int.init(al);
1884 var r = try Int.init(al);
1885 try Int.divTrunc(&q, &r, &a, &b);
1886
1887 debug.assert((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
1888 debug.assert((try r.to(u128)) == 0);
1889}
1890
1891test "big.int div multi-multi (2 branch)" {
1892 var a = try Int.initSet(al, 0x866666665555555588888887777777761111111111111111);
1893 var b = try Int.initSet(al, 0x86666666555555554444444433333333);
1894
1895 var q = try Int.init(al);
1896 var r = try Int.init(al);
1897 try Int.divTrunc(&q, &r, &a, &b);
1898
1899 debug.assert((try q.to(u128)) == 0x10000000000000000);
1900 debug.assert((try r.to(u128)) == 0x44444443444444431111111111111111);
1901}
1902
1903test "big.int div multi-multi (3.1/3.3 branch)" {
1904 var a = try Int.initSet(al, 0x11111111111111111111111111111111111111111111111111111111111111);
1905 var b = try Int.initSet(al, 0x1111111111111111111111111111111111111111171);
1906
1907 var q = try Int.init(al);
1908 var r = try Int.init(al);
1909 try Int.divTrunc(&q, &r, &a, &b);
1910
1911 debug.assert((try q.to(u128)) == 0xfffffffffffffffffff);
1912 debug.assert((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282);
1913}
1914
1915test "big.int shift-right single" {
1916 var a = try Int.initSet(al, 0xffff0000);
1917 try a.shiftRight(a, 16);
1918
1919 debug.assert((try a.to(u32)) == 0xffff);
1920}
1921
1922test "big.int shift-right multi" {
1923 var a = try Int.initSet(al, 0xffff0000eeee1111dddd2222cccc3333);
1924 try a.shiftRight(a, 67);
1925
1926 debug.assert((try a.to(u64)) == 0x1fffe0001dddc222);
1927}
1928
1929test "big.int shift-left single" {
1930 var a = try Int.initSet(al, 0xffff);
1931 try a.shiftLeft(a, 16);
1932
1933 debug.assert((try a.to(u64)) == 0xffff0000);
1934}
1935
1936test "big.int shift-left multi" {
1937 var a = try Int.initSet(al, 0x1fffe0001dddc222);
1938 try a.shiftLeft(a, 67);
1939
1940 debug.assert((try a.to(u128)) == 0xffff0000eeee11100000000000000000);
1941}
1942
1943test "big.int shift-right negative" {
1944 var a = try Int.init(al);
1945
1946 try a.shiftRight(-20, 2);
1947 debug.assert((try a.to(i32)) == -20 >> 2);
1948
1949 try a.shiftRight(-5, 10);
1950 debug.assert((try a.to(i32)) == -5 >> 10);
1951}
1952
1953test "big.int shift-left negative" {
1954 var a = try Int.init(al);
1955
1956 try a.shiftRight(-10, 1232);
1957 debug.assert((try a.to(i32)) == -10 >> 1232);
1958}
1959
1960test "big.int bitwise and simple" {
1961 var a = try Int.initSet(al, 0xffffffff11111111);
1962 var b = try Int.initSet(al, 0xeeeeeeee22222222);
1963
1964 try a.bitAnd(&a, &b);
1965
1966 debug.assert((try a.to(u64)) == 0xeeeeeeee00000000);
1967}
1968
1969test "big.int bitwise and multi-limb" {
1970 var a = try Int.initSet(al, @maxValue(Limb) + 1);
1971 var b = try Int.initSet(al, @maxValue(Limb));
1972
1973 try a.bitAnd(&a, &b);
1974
1975 debug.assert((try a.to(u128)) == 0);
1976}
1977
1978test "big.int bitwise xor simple" {
1979 var a = try Int.initSet(al, 0xffffffff11111111);
1980 var b = try Int.initSet(al, 0xeeeeeeee22222222);
1981
1982 try a.bitXor(&a, &b);
1983
1984 debug.assert((try a.to(u64)) == 0x1111111133333333);
1985}
1986
1987test "big.int bitwise xor multi-limb" {
1988 var a = try Int.initSet(al, @maxValue(Limb) + 1);
1989 var b = try Int.initSet(al, @maxValue(Limb));
1990
1991 try a.bitXor(&a, &b);
1992
1993 debug.assert((try a.to(DoubleLimb)) == (@maxValue(Limb) + 1) ^ @maxValue(Limb));
1994}
1995
1996test "big.int bitwise or simple" {
1997 var a = try Int.initSet(al, 0xffffffff11111111);
1998 var b = try Int.initSet(al, 0xeeeeeeee22222222);
1999
2000 try a.bitOr(&a, &b);
2001
2002 debug.assert((try a.to(u64)) == 0xffffffff33333333);
2003}
2004
2005test "big.int bitwise or multi-limb" {
2006 var a = try Int.initSet(al, @maxValue(Limb) + 1);
2007 var b = try Int.initSet(al, @maxValue(Limb));
2008
2009 try a.bitOr(&a, &b);
2010
2011 // TODO: big.int.cpp or is wrong on multi-limb.
2012 debug.assert((try a.to(DoubleLimb)) == (@maxValue(Limb) + 1) + @maxValue(Limb));
2013}
2014
2015test "big.int var args" {
2016 var a = try Int.initSet(al, 5);
2017
2018 try a.add(&a, 6);
2019 debug.assert((try a.to(u64)) == 11);
2020
2021 debug.assert(a.cmp(11) == 0);
2022 debug.assert(a.cmp(14) <= 0);
2023}
std/math/index.zig+12-1
...@@ -132,6 +132,8 @@ pub const tan = @import("tan.zig").tan;...@@ -132,6 +132,8 @@ pub const tan = @import("tan.zig").tan;
132pub const complex = @import("complex/index.zig");132pub const complex = @import("complex/index.zig");
133pub const Complex = complex.Complex;133pub const Complex = complex.Complex;
134134
135pub const big = @import("big/index.zig");
136
135test "math" {137test "math" {
136 _ = @import("nan.zig");138 _ = @import("nan.zig");
137 _ = @import("isnan.zig");139 _ = @import("isnan.zig");
...@@ -177,6 +179,8 @@ test "math" {...@@ -177,6 +179,8 @@ test "math" {
177 _ = @import("tan.zig");179 _ = @import("tan.zig");
178180
179 _ = @import("complex/index.zig");181 _ = @import("complex/index.zig");
182
183 _ = @import("big/index.zig");
180}184}
181185
182pub fn min(x: var, y: var) @typeOf(x + y) {186pub fn min(x: var, y: var) @typeOf(x + y) {
...@@ -306,7 +310,14 @@ test "math.rotl" {...@@ -306,7 +310,14 @@ test "math.rotl" {
306}310}
307311
308pub fn Log2Int(comptime T: type) type {312pub fn Log2Int(comptime T: type) type {
309 return @IntType(false, log2(T.bit_count));313 // comptime ceil log2
314 comptime var count: usize = 0;
315 comptime var s = T.bit_count - 1;
316 inline while (s != 0) : (s >>= 1) {
317 count += 1;
318 }
319
320 return @IntType(false, count);
310}321}
311322
312test "math overflow functions" {323test "math overflow functions" {
std/mem.zig+11-11
...@@ -304,20 +304,20 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee...@@ -304,20 +304,20 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee
304}304}
305305
306test "mem.indexOf" {306test "mem.indexOf" {
307 assert(??indexOf(u8, "one two three four", "four") == 14);307 assert(indexOf(u8, "one two three four", "four").? == 14);
308 assert(??lastIndexOf(u8, "one two three two four", "two") == 14);308 assert(lastIndexOf(u8, "one two three two four", "two").? == 14);
309 assert(indexOf(u8, "one two three four", "gour") == null);309 assert(indexOf(u8, "one two three four", "gour") == null);
310 assert(lastIndexOf(u8, "one two three four", "gour") == null);310 assert(lastIndexOf(u8, "one two three four", "gour") == null);
311 assert(??indexOf(u8, "foo", "foo") == 0);311 assert(indexOf(u8, "foo", "foo").? == 0);
312 assert(??lastIndexOf(u8, "foo", "foo") == 0);312 assert(lastIndexOf(u8, "foo", "foo").? == 0);
313 assert(indexOf(u8, "foo", "fool") == null);313 assert(indexOf(u8, "foo", "fool") == null);
314 assert(lastIndexOf(u8, "foo", "lfoo") == null);314 assert(lastIndexOf(u8, "foo", "lfoo") == null);
315 assert(lastIndexOf(u8, "foo", "fool") == null);315 assert(lastIndexOf(u8, "foo", "fool") == null);
316316
317 assert(??indexOf(u8, "foo foo", "foo") == 0);317 assert(indexOf(u8, "foo foo", "foo").? == 0);
318 assert(??lastIndexOf(u8, "foo foo", "foo") == 4);318 assert(lastIndexOf(u8, "foo foo", "foo").? == 4);
319 assert(??lastIndexOfAny(u8, "boo, cat", "abo") == 6);319 assert(lastIndexOfAny(u8, "boo, cat", "abo").? == 6);
320 assert(??lastIndexOfScalar(u8, "boo", 'o') == 2);320 assert(lastIndexOfScalar(u8, "boo", 'o').? == 2);
321}321}
322322
323/// Reads an integer from memory with size equal to bytes.len.323/// Reads an integer from memory with size equal to bytes.len.
...@@ -432,9 +432,9 @@ pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator {...@@ -432,9 +432,9 @@ pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator {
432432
433test "mem.split" {433test "mem.split" {
434 var it = split(" abc def ghi ", " ");434 var it = split(" abc def ghi ", " ");
435 assert(eql(u8, ??it.next(), "abc"));435 assert(eql(u8, it.next().?, "abc"));
436 assert(eql(u8, ??it.next(), "def"));436 assert(eql(u8, it.next().?, "def"));
437 assert(eql(u8, ??it.next(), "ghi"));437 assert(eql(u8, it.next().?, "ghi"));
438 assert(it.next() == null);438 assert(it.next() == null);
439}439}
440440
std/os/child_process.zig+9-9
...@@ -156,7 +156,7 @@ pub const ChildProcess = struct {...@@ -156,7 +156,7 @@ pub const ChildProcess = struct {
156 };156 };
157 }157 }
158 try self.waitUnwrappedWindows();158 try self.waitUnwrappedWindows();
159 return ??self.term;159 return self.term.?;
160 }160 }
161161
162 pub fn killPosix(self: *ChildProcess) !Term {162 pub fn killPosix(self: *ChildProcess) !Term {
...@@ -175,7 +175,7 @@ pub const ChildProcess = struct {...@@ -175,7 +175,7 @@ pub const ChildProcess = struct {
175 };175 };
176 }176 }
177 self.waitUnwrapped();177 self.waitUnwrapped();
178 return ??self.term;178 return self.term.?;
179 }179 }
180180
181 /// Blocks until child process terminates and then cleans up all resources.181 /// Blocks until child process terminates and then cleans up all resources.
...@@ -212,8 +212,8 @@ pub const ChildProcess = struct {...@@ -212,8 +212,8 @@ pub const ChildProcess = struct {
212 defer Buffer.deinit(&stdout);212 defer Buffer.deinit(&stdout);
213 defer Buffer.deinit(&stderr);213 defer Buffer.deinit(&stderr);
214214
215 var stdout_file_in_stream = io.FileInStream.init(&??child.stdout);215 var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
216 var stderr_file_in_stream = io.FileInStream.init(&??child.stderr);216 var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
217217
218 try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);218 try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);
219 try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size);219 try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size);
...@@ -232,7 +232,7 @@ pub const ChildProcess = struct {...@@ -232,7 +232,7 @@ pub const ChildProcess = struct {
232 }232 }
233233
234 try self.waitUnwrappedWindows();234 try self.waitUnwrappedWindows();
235 return ??self.term;235 return self.term.?;
236 }236 }
237237
238 fn waitPosix(self: *ChildProcess) !Term {238 fn waitPosix(self: *ChildProcess) !Term {
...@@ -242,7 +242,7 @@ pub const ChildProcess = struct {...@@ -242,7 +242,7 @@ pub const ChildProcess = struct {
242 }242 }
243243
244 self.waitUnwrapped();244 self.waitUnwrapped();
245 return ??self.term;245 return self.term.?;
246 }246 }
247247
248 pub fn deinit(self: *ChildProcess) void {248 pub fn deinit(self: *ChildProcess) void {
...@@ -619,13 +619,13 @@ pub const ChildProcess = struct {...@@ -619,13 +619,13 @@ pub const ChildProcess = struct {
619 self.term = null;619 self.term = null;
620620
621 if (self.stdin_behavior == StdIo.Pipe) {621 if (self.stdin_behavior == StdIo.Pipe) {
622 os.close(??g_hChildStd_IN_Rd);622 os.close(g_hChildStd_IN_Rd.?);
623 }623 }
624 if (self.stderr_behavior == StdIo.Pipe) {624 if (self.stderr_behavior == StdIo.Pipe) {
625 os.close(??g_hChildStd_ERR_Wr);625 os.close(g_hChildStd_ERR_Wr.?);
626 }626 }
627 if (self.stdout_behavior == StdIo.Pipe) {627 if (self.stdout_behavior == StdIo.Pipe) {
628 os.close(??g_hChildStd_OUT_Wr);628 os.close(g_hChildStd_OUT_Wr.?);
629 }629 }
630 }630 }
631631
std/os/darwin.zig+4-4
...@@ -327,7 +327,7 @@ pub fn raise(sig: i32) usize {...@@ -327,7 +327,7 @@ pub fn raise(sig: i32) usize {
327}327}
328328
329pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize {329pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize {
330 return errnoWrap(c.read(fd, @ptrCast([*]c_void, buf), nbyte));330 return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte));
331}331}
332332
333pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {333pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {
...@@ -335,17 +335,17 @@ pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {...@@ -335,17 +335,17 @@ pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {
335}335}
336336
337pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize {337pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize {
338 return errnoWrap(c.write(fd, @ptrCast([*]const c_void, buf), nbyte));338 return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte));
339}339}
340340
341pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {341pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
342 const ptr_result = c.mmap(@ptrCast([*]c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset);342 const ptr_result = c.mmap(@ptrCast(*c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset);
343 const isize_result = @bitCast(isize, @ptrToInt(ptr_result));343 const isize_result = @bitCast(isize, @ptrToInt(ptr_result));
344 return errnoWrap(isize_result);344 return errnoWrap(isize_result);
345}345}
346346
347pub fn munmap(address: usize, length: usize) usize {347pub fn munmap(address: usize, length: usize) usize {
348 return errnoWrap(c.munmap(@intToPtr([*]c_void, address), length));348 return errnoWrap(c.munmap(@intToPtr(*c_void, address), length));
349}349}
350350
351pub fn unlink(path: [*]const u8) usize {351pub fn unlink(path: [*]const u8) usize {
std/os/file.zig+16-3
...@@ -96,7 +96,20 @@ pub const File = struct {...@@ -96,7 +96,20 @@ pub const File = struct {
96 return File{ .handle = handle };96 return File{ .handle = handle };
97 }97 }
9898
99 pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool {99 pub const AccessError = error{
100 PermissionDenied,
101 NotFound,
102 NameTooLong,
103 BadMode,
104 BadPathName,
105 Io,
106 SystemResources,
107 OutOfMemory,
108
109 Unexpected,
110 };
111
112 pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) AccessError!bool {
100 const path_with_null = try std.cstr.addNullByte(allocator, path);113 const path_with_null = try std.cstr.addNullByte(allocator, path);
101 defer allocator.free(path_with_null);114 defer allocator.free(path_with_null);
102115
...@@ -123,7 +136,7 @@ pub const File = struct {...@@ -123,7 +136,7 @@ pub const File = struct {
123 }136 }
124 return true;137 return true;
125 } else if (is_windows) {138 } else if (is_windows) {
126 if (os.windows.PathFileExists(path_with_null.ptr) == os.windows.TRUE) {139 if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
127 return true;140 return true;
128 }141 }
129142
...@@ -334,7 +347,7 @@ pub const File = struct {...@@ -334,7 +347,7 @@ pub const File = struct {
334 while (index < buffer.len) {347 while (index < buffer.len) {
335 const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));348 const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));
336 var amt_read: windows.DWORD = undefined;349 var amt_read: windows.DWORD = undefined;
337 if (windows.ReadFile(self.handle, @ptrCast([*]c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) {350 if (windows.ReadFile(self.handle, @ptrCast(*c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) {
338 const err = windows.GetLastError();351 const err = windows.GetLastError();
339 return switch (err) {352 return switch (err) {
340 windows.ERROR.OPERATION_ABORTED => continue,353 windows.ERROR.OPERATION_ABORTED => continue,
std/os/index.zig+213-93
...@@ -422,10 +422,10 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator:...@@ -422,10 +422,10 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator:
422422
423 const exe_path = argv[0];423 const exe_path = argv[0];
424 if (mem.indexOfScalar(u8, exe_path, '/') != null) {424 if (mem.indexOfScalar(u8, exe_path, '/') != null) {
425 return posixExecveErrnoToErr(posix.getErrno(posix.execve(??argv_buf[0], argv_buf.ptr, envp_buf.ptr)));425 return posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr)));
426 }426 }
427427
428 const PATH = getEnvPosix("PATH") ?? "/usr/local/bin:/bin/:/usr/bin";428 const PATH = getEnvPosix("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
429 // PATH.len because it is >= the largest search_path429 // PATH.len because it is >= the largest search_path
430 // +1 for the / to join the search path and exe_path430 // +1 for the / to join the search path and exe_path
431 // +1 for the null terminating byte431 // +1 for the null terminating byte
...@@ -490,7 +490,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {...@@ -490,7 +490,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
490 errdefer result.deinit();490 errdefer result.deinit();
491491
492 if (is_windows) {492 if (is_windows) {
493 const ptr = windows.GetEnvironmentStringsA() ?? return error.OutOfMemory;493 const ptr = windows.GetEnvironmentStringsA() orelse return error.OutOfMemory;
494 defer assert(windows.FreeEnvironmentStringsA(ptr) != 0);494 defer assert(windows.FreeEnvironmentStringsA(ptr) != 0);
495495
496 var i: usize = 0;496 var i: usize = 0;
...@@ -573,7 +573,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 {...@@ -573,7 +573,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 {
573 return allocator.shrink(u8, buf, result);573 return allocator.shrink(u8, buf, result);
574 }574 }
575 } else {575 } else {
576 const result = getEnvPosix(key) ?? return error.EnvironmentVariableNotFound;576 const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound;
577 return mem.dupe(allocator, u8, result);577 return mem.dupe(allocator, u8, result);
578 }578 }
579}579}
...@@ -714,7 +714,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:...@@ -714,7 +714,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
714 else => return err, // TODO zig should know this set does not include PathAlreadyExists714 else => return err, // TODO zig should know this set does not include PathAlreadyExists
715 }715 }
716716
717 const dirname = os.path.dirname(new_path);717 const dirname = os.path.dirname(new_path) orelse ".";
718718
719 var rand_buf: [12]u8 = undefined;719 var rand_buf: [12]u8 = undefined;
720 const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));720 const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));
...@@ -734,7 +734,23 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:...@@ -734,7 +734,23 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
734 }734 }
735}735}
736736
737pub fn deleteFile(allocator: *Allocator, file_path: []const u8) !void {737pub const DeleteFileError = error{
738 FileNotFound,
739 AccessDenied,
740 FileBusy,
741 FileSystem,
742 IsDir,
743 SymLinkLoop,
744 NameTooLong,
745 NotDir,
746 SystemResources,
747 ReadOnlyFileSystem,
748 OutOfMemory,
749
750 Unexpected,
751};
752
753pub fn deleteFile(allocator: *Allocator, file_path: []const u8) DeleteFileError!void {
738 if (builtin.os == Os.windows) {754 if (builtin.os == Os.windows) {
739 return deleteFileWindows(allocator, file_path);755 return deleteFileWindows(allocator, file_path);
740 } else {756 } else {
...@@ -844,14 +860,14 @@ pub const AtomicFile = struct {...@@ -844,14 +860,14 @@ pub const AtomicFile = struct {
844860
845 var rand_buf: [12]u8 = undefined;861 var rand_buf: [12]u8 = undefined;
846862
847 const dirname_component_len = if (dirname.len == 0) 0 else dirname.len + 1;863 const dirname_component_len = if (dirname) |d| d.len + 1 else 0;
848 const tmp_path = try allocator.alloc(u8, dirname_component_len +864 const tmp_path = try allocator.alloc(u8, dirname_component_len +
849 base64.Base64Encoder.calcSize(rand_buf.len));865 base64.Base64Encoder.calcSize(rand_buf.len));
850 errdefer allocator.free(tmp_path);866 errdefer allocator.free(tmp_path);
851867
852 if (dirname.len != 0) {868 if (dirname) |dir| {
853 mem.copy(u8, tmp_path[0..], dirname);869 mem.copy(u8, tmp_path[0..], dir);
854 tmp_path[dirname.len] = os.path.sep;870 tmp_path[dir.len] = os.path.sep;
855 }871 }
856872
857 while (true) {873 while (true) {
...@@ -1019,37 +1035,66 @@ pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {...@@ -1019,37 +1035,66 @@ pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
1019 }1035 }
1020}1036}
10211037
1038pub const DeleteDirError = error{
1039 AccessDenied,
1040 FileBusy,
1041 SymLinkLoop,
1042 NameTooLong,
1043 FileNotFound,
1044 SystemResources,
1045 NotDir,
1046 DirNotEmpty,
1047 ReadOnlyFileSystem,
1048 OutOfMemory,
1049
1050 Unexpected,
1051};
1052
1022/// Returns ::error.DirNotEmpty if the directory is not empty.1053/// Returns ::error.DirNotEmpty if the directory is not empty.
1023/// To delete a directory recursively, see ::deleteTree1054/// To delete a directory recursively, see ::deleteTree
1024pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) !void {1055pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) DeleteDirError!void {
1025 const path_buf = try allocator.alloc(u8, dir_path.len + 1);1056 const path_buf = try allocator.alloc(u8, dir_path.len + 1);
1026 defer allocator.free(path_buf);1057 defer allocator.free(path_buf);
10271058
1028 mem.copy(u8, path_buf, dir_path);1059 mem.copy(u8, path_buf, dir_path);
1029 path_buf[dir_path.len] = 0;1060 path_buf[dir_path.len] = 0;
10301061
1031 const err = posix.getErrno(posix.rmdir(path_buf.ptr));1062 switch (builtin.os) {
1032 if (err > 0) {1063 Os.windows => {
1033 return switch (err) {1064 if (windows.RemoveDirectoryA(path_buf.ptr) == 0) {
1034 posix.EACCES, posix.EPERM => error.AccessDenied,1065 const err = windows.GetLastError();
1035 posix.EBUSY => error.FileBusy,1066 return switch (err) {
1036 posix.EFAULT, posix.EINVAL => unreachable,1067 windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
1037 posix.ELOOP => error.SymLinkLoop,1068 windows.ERROR.DIR_NOT_EMPTY => error.DirNotEmpty,
1038 posix.ENAMETOOLONG => error.NameTooLong,1069 else => unexpectedErrorWindows(err),
1039 posix.ENOENT => error.FileNotFound,1070 };
1040 posix.ENOMEM => error.SystemResources,1071 }
1041 posix.ENOTDIR => error.NotDir,1072 },
1042 posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,1073 Os.linux, Os.macosx, Os.ios => {
1043 posix.EROFS => error.ReadOnlyFileSystem,1074 const err = posix.getErrno(posix.rmdir(path_buf.ptr));
1044 else => unexpectedErrorPosix(err),1075 if (err > 0) {
1045 };1076 return switch (err) {
1077 posix.EACCES, posix.EPERM => error.AccessDenied,
1078 posix.EBUSY => error.FileBusy,
1079 posix.EFAULT, posix.EINVAL => unreachable,
1080 posix.ELOOP => error.SymLinkLoop,
1081 posix.ENAMETOOLONG => error.NameTooLong,
1082 posix.ENOENT => error.FileNotFound,
1083 posix.ENOMEM => error.SystemResources,
1084 posix.ENOTDIR => error.NotDir,
1085 posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
1086 posix.EROFS => error.ReadOnlyFileSystem,
1087 else => unexpectedErrorPosix(err),
1088 };
1089 }
1090 },
1091 else => @compileError("unimplemented"),
1046 }1092 }
1047}1093}
10481094
1049/// Whether ::full_path describes a symlink, file, or directory, this function1095/// Whether ::full_path describes a symlink, file, or directory, this function
1050/// removes it. If it cannot be removed because it is a non-empty directory,1096/// removes it. If it cannot be removed because it is a non-empty directory,
1051/// this function recursively removes its entries and then tries again.1097/// this function recursively removes its entries and then tries again.
1052/// TODO non-recursive implementation
1053const DeleteTreeError = error{1098const DeleteTreeError = error{
1054 OutOfMemory,1099 OutOfMemory,
1055 AccessDenied,1100 AccessDenied,
...@@ -1128,7 +1173,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -1128,7 +1173,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
1128 try full_entry_buf.resize(full_path.len + entry.name.len + 1);1173 try full_entry_buf.resize(full_path.len + entry.name.len + 1);
1129 const full_entry_path = full_entry_buf.toSlice();1174 const full_entry_path = full_entry_buf.toSlice();
1130 mem.copy(u8, full_entry_path, full_path);1175 mem.copy(u8, full_entry_path, full_path);
1131 full_entry_path[full_path.len] = '/';1176 full_entry_path[full_path.len] = path.sep;
1132 mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name);1177 mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name);
11331178
1134 try deleteTree(allocator, full_entry_path);1179 try deleteTree(allocator, full_entry_path);
...@@ -1139,16 +1184,29 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -1139,16 +1184,29 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
1139}1184}
11401185
1141pub const Dir = struct {1186pub const Dir = struct {
1142 fd: i32,1187 handle: Handle,
1143 darwin_seek: darwin_seek_t,
1144 allocator: *Allocator,1188 allocator: *Allocator,
1145 buf: []u8,
1146 index: usize,
1147 end_index: usize,
11481189
1149 const darwin_seek_t = switch (builtin.os) {1190 pub const Handle = switch (builtin.os) {
1150 Os.macosx, Os.ios => i64,1191 Os.macosx, Os.ios => struct {
1151 else => void,1192 fd: i32,
1193 seek: i64,
1194 buf: []u8,
1195 index: usize,
1196 end_index: usize,
1197 },
1198 Os.linux => struct {
1199 fd: i32,
1200 buf: []u8,
1201 index: usize,
1202 end_index: usize,
1203 },
1204 Os.windows => struct {
1205 handle: windows.HANDLE,
1206 find_file_data: windows.WIN32_FIND_DATAA,
1207 first: bool,
1208 },
1209 else => @compileError("unimplemented"),
1152 };1210 };
11531211
1154 pub const Entry = struct {1212 pub const Entry = struct {
...@@ -1168,81 +1226,122 @@ pub const Dir = struct {...@@ -1168,81 +1226,122 @@ pub const Dir = struct {
1168 };1226 };
1169 };1227 };
11701228
1171 pub fn open(allocator: *Allocator, dir_path: []const u8) !Dir {1229 pub const OpenError = error{
1172 const fd = switch (builtin.os) {1230 PathNotFound,
1173 Os.windows => @compileError("TODO support Dir.open for windows"),1231 NotDir,
1174 Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),1232 AccessDenied,
1175 Os.macosx, Os.ios => try posixOpen(1233 FileTooBig,
1176 allocator,1234 IsDir,
1177 dir_path,1235 SymLinkLoop,
1178 posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,1236 ProcessFdQuotaExceeded,
1179 0,1237 NameTooLong,
1180 ),1238 SystemFdQuotaExceeded,
1181 else => @compileError("Dir.open is not supported for this platform"),1239 NoDevice,
1182 };1240 SystemResources,
1183 const darwin_seek_init = switch (builtin.os) {1241 NoSpaceLeft,
1184 Os.macosx, Os.ios => 0,1242 PathAlreadyExists,
1185 else => {},1243 OutOfMemory,
1186 };1244
1245 Unexpected,
1246 };
1247
1248 pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
1187 return Dir{1249 return Dir{
1188 .allocator = allocator,1250 .allocator = allocator,
1189 .fd = fd,1251 .handle = switch (builtin.os) {
1190 .darwin_seek = darwin_seek_init,1252 Os.windows => blk: {
1191 .index = 0,1253 var find_file_data: windows.WIN32_FIND_DATAA = undefined;
1192 .end_index = 0,1254 const handle = try windows_util.windowsFindFirstFile(allocator, dir_path, &find_file_data);
1193 .buf = []u8{},1255 break :blk Handle{
1256 .handle = handle,
1257 .find_file_data = find_file_data, // TODO guaranteed copy elision
1258 .first = true,
1259 };
1260 },
1261 Os.macosx, Os.ios => Handle{
1262 .fd = try posixOpen(
1263 allocator,
1264 dir_path,
1265 posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,
1266 0,
1267 ),
1268 .seek = 0,
1269 .index = 0,
1270 .end_index = 0,
1271 .buf = []u8{},
1272 },
1273 Os.linux => Handle{
1274 .fd = try posixOpen(
1275 allocator,
1276 dir_path,
1277 posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC,
1278 0,
1279 ),
1280 .index = 0,
1281 .end_index = 0,
1282 .buf = []u8{},
1283 },
1284 else => @compileError("unimplemented"),
1285 },
1194 };1286 };
1195 }1287 }
11961288
1197 pub fn close(self: *Dir) void {1289 pub fn close(self: *Dir) void {
1198 self.allocator.free(self.buf);1290 switch (builtin.os) {
1199 os.close(self.fd);1291 Os.windows => {
1292 _ = windows.FindClose(self.handle.handle);
1293 },
1294 Os.macosx, Os.ios, Os.linux => {
1295 self.allocator.free(self.handle.buf);
1296 os.close(self.handle.fd);
1297 },
1298 else => @compileError("unimplemented"),
1299 }
1200 }1300 }
12011301
1202 /// Memory such as file names referenced in this returned entry becomes invalid1302 /// Memory such as file names referenced in this returned entry becomes invalid
1203 /// with subsequent calls to next, as well as when this ::Dir is deinitialized.1303 /// with subsequent calls to next, as well as when this `Dir` is deinitialized.
1204 pub fn next(self: *Dir) !?Entry {1304 pub fn next(self: *Dir) !?Entry {
1205 switch (builtin.os) {1305 switch (builtin.os) {
1206 Os.linux => return self.nextLinux(),1306 Os.linux => return self.nextLinux(),
1207 Os.macosx, Os.ios => return self.nextDarwin(),1307 Os.macosx, Os.ios => return self.nextDarwin(),
1208 Os.windows => return self.nextWindows(),1308 Os.windows => return self.nextWindows(),
1209 else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),1309 else => @compileError("unimplemented"),
1210 }1310 }
1211 }1311 }
12121312
1213 fn nextDarwin(self: *Dir) !?Entry {1313 fn nextDarwin(self: *Dir) !?Entry {
1214 start_over: while (true) {1314 start_over: while (true) {
1215 if (self.index >= self.end_index) {1315 if (self.handle.index >= self.handle.end_index) {
1216 if (self.buf.len == 0) {1316 if (self.handle.buf.len == 0) {
1217 self.buf = try self.allocator.alloc(u8, page_size);1317 self.handle.buf = try self.allocator.alloc(u8, page_size);
1218 }1318 }
12191319
1220 while (true) {1320 while (true) {
1221 const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, &self.darwin_seek);1321 const result = posix.getdirentries64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek);
1222 const err = posix.getErrno(result);1322 const err = posix.getErrno(result);
1223 if (err > 0) {1323 if (err > 0) {
1224 switch (err) {1324 switch (err) {
1225 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,1325 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
1226 posix.EINVAL => {1326 posix.EINVAL => {
1227 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);1327 self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2);
1228 continue;1328 continue;
1229 },1329 },
1230 else => return unexpectedErrorPosix(err),1330 else => return unexpectedErrorPosix(err),
1231 }1331 }
1232 }1332 }
1233 if (result == 0) return null;1333 if (result == 0) return null;
1234 self.index = 0;1334 self.handle.index = 0;
1235 self.end_index = result;1335 self.handle.end_index = result;
1236 break;1336 break;
1237 }1337 }
1238 }1338 }
1239 const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.buf[self.index]);1339 const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]);
1240 const next_index = self.index + darwin_entry.d_reclen;1340 const next_index = self.handle.index + darwin_entry.d_reclen;
1241 self.index = next_index;1341 self.handle.index = next_index;
12421342
1243 const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen];1343 const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen];
12441344
1245 // skip . and .. entries
1246 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {1345 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
1247 continue :start_over;1346 continue :start_over;
1248 }1347 }
...@@ -1266,38 +1365,59 @@ pub const Dir = struct {...@@ -1266,38 +1365,59 @@ pub const Dir = struct {
1266 }1365 }
12671366
1268 fn nextWindows(self: *Dir) !?Entry {1367 fn nextWindows(self: *Dir) !?Entry {
1269 @compileError("TODO support Dir.next for windows");1368 while (true) {
1369 if (self.handle.first) {
1370 self.handle.first = false;
1371 } else {
1372 if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data))
1373 return null;
1374 }
1375 const name = std.cstr.toSlice(self.handle.find_file_data.cFileName[0..].ptr);
1376 if (mem.eql(u8, name, ".") or mem.eql(u8, name, ".."))
1377 continue;
1378 const kind = blk: {
1379 const attrs = self.handle.find_file_data.dwFileAttributes;
1380 if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
1381 if (attrs & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;
1382 if (attrs & windows.FILE_ATTRIBUTE_NORMAL != 0) break :blk Entry.Kind.File;
1383 break :blk Entry.Kind.Unknown;
1384 };
1385 return Entry{
1386 .name = name,
1387 .kind = kind,
1388 };
1389 }
1270 }1390 }
12711391
1272 fn nextLinux(self: *Dir) !?Entry {1392 fn nextLinux(self: *Dir) !?Entry {
1273 start_over: while (true) {1393 start_over: while (true) {
1274 if (self.index >= self.end_index) {1394 if (self.handle.index >= self.handle.end_index) {
1275 if (self.buf.len == 0) {1395 if (self.handle.buf.len == 0) {
1276 self.buf = try self.allocator.alloc(u8, page_size);1396 self.handle.buf = try self.allocator.alloc(u8, page_size);
1277 }1397 }
12781398
1279 while (true) {1399 while (true) {
1280 const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);1400 const result = posix.getdents(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len);
1281 const err = posix.getErrno(result);1401 const err = posix.getErrno(result);
1282 if (err > 0) {1402 if (err > 0) {
1283 switch (err) {1403 switch (err) {
1284 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,1404 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
1285 posix.EINVAL => {1405 posix.EINVAL => {
1286 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);1406 self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2);
1287 continue;1407 continue;
1288 },1408 },
1289 else => return unexpectedErrorPosix(err),1409 else => return unexpectedErrorPosix(err),
1290 }1410 }
1291 }1411 }
1292 if (result == 0) return null;1412 if (result == 0) return null;
1293 self.index = 0;1413 self.handle.index = 0;
1294 self.end_index = result;1414 self.handle.end_index = result;
1295 break;1415 break;
1296 }1416 }
1297 }1417 }
1298 const linux_entry = @ptrCast(*align(1) posix.dirent, &self.buf[self.index]);1418 const linux_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]);
1299 const next_index = self.index + linux_entry.d_reclen;1419 const next_index = self.handle.index + linux_entry.d_reclen;
1300 self.index = next_index;1420 self.handle.index = next_index;
13011421
1302 const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name));1422 const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name));
13031423
...@@ -1306,7 +1426,7 @@ pub const Dir = struct {...@@ -1306,7 +1426,7 @@ pub const Dir = struct {
1306 continue :start_over;1426 continue :start_over;
1307 }1427 }
13081428
1309 const type_char = self.buf[next_index - 1];1429 const type_char = self.handle.buf[next_index - 1];
1310 const entry_kind = switch (type_char) {1430 const entry_kind = switch (type_char) {
1311 posix.DT_BLK => Entry.Kind.BlockDevice,1431 posix.DT_BLK => Entry.Kind.BlockDevice,
1312 posix.DT_CHR => Entry.Kind.CharacterDevice,1432 posix.DT_CHR => Entry.Kind.CharacterDevice,
...@@ -1641,7 +1761,7 @@ pub const ArgIterator = struct {...@@ -1641,7 +1761,7 @@ pub const ArgIterator = struct {
1641 if (builtin.os == Os.windows) {1761 if (builtin.os == Os.windows) {
1642 return self.inner.next(allocator);1762 return self.inner.next(allocator);
1643 } else {1763 } else {
1644 return mem.dupe(allocator, u8, self.inner.next() ?? return null);1764 return mem.dupe(allocator, u8, self.inner.next() orelse return null);
1645 }1765 }
1646 }1766 }
16471767
...@@ -1729,7 +1849,7 @@ test "windows arg parsing" {...@@ -1729,7 +1849,7 @@ test "windows arg parsing" {
1729fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void {1849fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void {
1730 var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line);1850 var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line);
1731 for (expected_args) |expected_arg| {1851 for (expected_args) |expected_arg| {
1732 const arg = ??it.next(debug.global_allocator) catch unreachable;1852 const arg = it.next(debug.global_allocator).? catch unreachable;
1733 assert(mem.eql(u8, arg, expected_arg));1853 assert(mem.eql(u8, arg, expected_arg));
1734 }1854 }
1735 assert(it.next(debug.global_allocator) == null);1855 assert(it.next(debug.global_allocator) == null);
...@@ -1845,13 +1965,13 @@ pub fn selfExeDirPath(allocator: *mem.Allocator) ![]u8 {...@@ -1845,13 +1965,13 @@ pub fn selfExeDirPath(allocator: *mem.Allocator) ![]u8 {
1845 // the executable was in when it was run.1965 // the executable was in when it was run.
1846 const full_exe_path = try readLink(allocator, "/proc/self/exe");1966 const full_exe_path = try readLink(allocator, "/proc/self/exe");
1847 errdefer allocator.free(full_exe_path);1967 errdefer allocator.free(full_exe_path);
1848 const dir = path.dirname(full_exe_path);1968 const dir = path.dirname(full_exe_path) orelse ".";
1849 return allocator.shrink(u8, full_exe_path, dir.len);1969 return allocator.shrink(u8, full_exe_path, dir.len);
1850 },1970 },
1851 Os.windows, Os.macosx, Os.ios => {1971 Os.windows, Os.macosx, Os.ios => {
1852 const self_exe_path = try selfExePath(allocator);1972 const self_exe_path = try selfExePath(allocator);
1853 errdefer allocator.free(self_exe_path);1973 errdefer allocator.free(self_exe_path);
1854 const dirname = os.path.dirname(self_exe_path);1974 const dirname = os.path.dirname(self_exe_path) orelse ".";
1855 return allocator.shrink(u8, self_exe_path, dirname.len);1975 return allocator.shrink(u8, self_exe_path, dirname.len);
1856 },1976 },
1857 else => @compileError("unimplemented: std.os.selfExeDirPath for " ++ @tagName(builtin.os)),1977 else => @compileError("unimplemented: std.os.selfExeDirPath for " ++ @tagName(builtin.os)),
...@@ -2362,7 +2482,7 @@ pub const Thread = struct {...@@ -2362,7 +2482,7 @@ pub const Thread = struct {
2362 },2482 },
2363 builtin.Os.windows => struct {2483 builtin.Os.windows => struct {
2364 handle: windows.HANDLE,2484 handle: windows.HANDLE,
2365 alloc_start: [*]c_void,2485 alloc_start: *c_void,
2366 heap_handle: windows.HANDLE,2486 heap_handle: windows.HANDLE,
2367 },2487 },
2368 else => @compileError("Unsupported OS"),2488 else => @compileError("Unsupported OS"),
...@@ -2457,9 +2577,9 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -2457,9 +2577,9 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
2457 }2577 }
2458 };2578 };
24592579
2460 const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory;2580 const heap_handle = windows.GetProcessHeap() orelse return SpawnThreadError.OutOfMemory;
2461 const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);2581 const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);
2462 const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory;2582 const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory;
2463 errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0);2583 errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0);
2464 const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count];2584 const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count];
2465 const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;2585 const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
...@@ -2468,7 +2588,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -2468,7 +2588,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
2468 outer_context.thread.data.alloc_start = bytes_ptr;2588 outer_context.thread.data.alloc_start = bytes_ptr;
24692589
2470 const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner);2590 const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner);
2471 outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? {2591 outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse {
2472 const err = windows.GetLastError();2592 const err = windows.GetLastError();
2473 return switch (err) {2593 return switch (err) {
2474 else => os.unexpectedErrorWindows(err),2594 else => os.unexpectedErrorWindows(err),
...@@ -2533,7 +2653,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -2533,7 +2653,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
25332653
2534 // align to page2654 // align to page
2535 stack_end -= stack_end % os.page_size;2655 stack_end -= stack_end % os.page_size;
2536 assert(c.pthread_attr_setstack(&attr, @intToPtr([*]c_void, stack_addr), stack_end - stack_addr) == 0);2656 assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0);
25372657
2538 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));2658 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
2539 switch (err) {2659 switch (err) {
std/os/linux/vdso.zig+5-5
...@@ -28,7 +28,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {...@@ -28,7 +28,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {
28 }28 }
29 }29 }
30 }30 }
31 const dynv = maybe_dynv ?? return 0;31 const dynv = maybe_dynv orelse return 0;
32 if (base == @maxValue(usize)) return 0;32 if (base == @maxValue(usize)) return 0;
3333
34 var maybe_strings: ?[*]u8 = null;34 var maybe_strings: ?[*]u8 = null;
...@@ -52,9 +52,9 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {...@@ -52,9 +52,9 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {
52 }52 }
53 }53 }
5454
55 const strings = maybe_strings ?? return 0;55 const strings = maybe_strings orelse return 0;
56 const syms = maybe_syms ?? return 0;56 const syms = maybe_syms orelse return 0;
57 const hashtab = maybe_hashtab ?? return 0;57 const hashtab = maybe_hashtab orelse return 0;
58 if (maybe_verdef == null) maybe_versym = null;58 if (maybe_verdef == null) maybe_versym = null;
5959
60 const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON);60 const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON);
...@@ -67,7 +67,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {...@@ -67,7 +67,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {
67 if (0 == syms[i].st_shndx) continue;67 if (0 == syms[i].st_shndx) continue;
68 if (!mem.eql(u8, name, cstr.toSliceConst(strings + syms[i].st_name))) continue;68 if (!mem.eql(u8, name, cstr.toSliceConst(strings + syms[i].st_name))) continue;
69 if (maybe_versym) |versym| {69 if (maybe_versym) |versym| {
70 if (!checkver(??maybe_verdef, versym[i], vername, strings))70 if (!checkver(maybe_verdef.?, versym[i], vername, strings))
71 continue;71 continue;
72 }72 }
73 return base + syms[i].st_value;73 return base + syms[i].st_value;
std/os/path.zig+44-30
...@@ -182,8 +182,8 @@ pub fn windowsParsePath(path: []const u8) WindowsPath {...@@ -182,8 +182,8 @@ pub fn windowsParsePath(path: []const u8) WindowsPath {
182 }182 }
183183
184 var it = mem.split(path, []u8{this_sep});184 var it = mem.split(path, []u8{this_sep});
185 _ = (it.next() ?? return relative_path);185 _ = (it.next() orelse return relative_path);
186 _ = (it.next() ?? return relative_path);186 _ = (it.next() orelse return relative_path);
187 return WindowsPath{187 return WindowsPath{
188 .is_abs = isAbsoluteWindows(path),188 .is_abs = isAbsoluteWindows(path),
189 .kind = WindowsPath.Kind.NetworkShare,189 .kind = WindowsPath.Kind.NetworkShare,
...@@ -200,8 +200,8 @@ pub fn windowsParsePath(path: []const u8) WindowsPath {...@@ -200,8 +200,8 @@ pub fn windowsParsePath(path: []const u8) WindowsPath {
200 }200 }
201201
202 var it = mem.split(path, []u8{this_sep});202 var it = mem.split(path, []u8{this_sep});
203 _ = (it.next() ?? return relative_path);203 _ = (it.next() orelse return relative_path);
204 _ = (it.next() ?? return relative_path);204 _ = (it.next() orelse return relative_path);
205 return WindowsPath{205 return WindowsPath{
206 .is_abs = isAbsoluteWindows(path),206 .is_abs = isAbsoluteWindows(path),
207 .kind = WindowsPath.Kind.NetworkShare,207 .kind = WindowsPath.Kind.NetworkShare,
...@@ -265,7 +265,7 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) bool {...@@ -265,7 +265,7 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) bool {
265 var it2 = mem.split(ns2, []u8{sep2});265 var it2 = mem.split(ns2, []u8{sep2});
266266
267 // TODO ASCII is wrong, we actually need full unicode support to compare paths.267 // TODO ASCII is wrong, we actually need full unicode support to compare paths.
268 return asciiEqlIgnoreCase(??it1.next(), ??it2.next());268 return asciiEqlIgnoreCase(it1.next().?, it2.next().?);
269}269}
270270
271fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8) bool {271fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8) bool {
...@@ -286,7 +286,7 @@ fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8...@@ -286,7 +286,7 @@ fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8
286 var it2 = mem.split(p2, []u8{sep2});286 var it2 = mem.split(p2, []u8{sep2});
287287
288 // TODO ASCII is wrong, we actually need full unicode support to compare paths.288 // TODO ASCII is wrong, we actually need full unicode support to compare paths.
289 return asciiEqlIgnoreCase(??it1.next(), ??it2.next()) and asciiEqlIgnoreCase(??it1.next(), ??it2.next());289 return asciiEqlIgnoreCase(it1.next().?, it2.next().?) and asciiEqlIgnoreCase(it1.next().?, it2.next().?);
290 },290 },
291 }291 }
292}292}
...@@ -414,8 +414,8 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {...@@ -414,8 +414,8 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
414 WindowsPath.Kind.NetworkShare => {414 WindowsPath.Kind.NetworkShare => {
415 result = try allocator.alloc(u8, max_size);415 result = try allocator.alloc(u8, max_size);
416 var it = mem.split(paths[first_index], "/\\");416 var it = mem.split(paths[first_index], "/\\");
417 const server_name = ??it.next();417 const server_name = it.next().?;
418 const other_name = ??it.next();418 const other_name = it.next().?;
419419
420 result[result_index] = '\\';420 result[result_index] = '\\';
421 result_index += 1;421 result_index += 1;
...@@ -648,8 +648,8 @@ fn testResolvePosix(paths: []const []const u8) []u8 {...@@ -648,8 +648,8 @@ fn testResolvePosix(paths: []const []const u8) []u8 {
648}648}
649649
650/// If the path is a file in the current directory (no directory component)650/// If the path is a file in the current directory (no directory component)
651/// then the returned slice has .len = 0.651/// then returns null
652pub fn dirname(path: []const u8) []const u8 {652pub fn dirname(path: []const u8) ?[]const u8 {
653 if (is_windows) {653 if (is_windows) {
654 return dirnameWindows(path);654 return dirnameWindows(path);
655 } else {655 } else {
...@@ -657,9 +657,9 @@ pub fn dirname(path: []const u8) []const u8 {...@@ -657,9 +657,9 @@ pub fn dirname(path: []const u8) []const u8 {
657 }657 }
658}658}
659659
660pub fn dirnameWindows(path: []const u8) []const u8 {660pub fn dirnameWindows(path: []const u8) ?[]const u8 {
661 if (path.len == 0)661 if (path.len == 0)
662 return path[0..0];662 return null;
663663
664 const root_slice = diskDesignatorWindows(path);664 const root_slice = diskDesignatorWindows(path);
665 if (path.len == root_slice.len)665 if (path.len == root_slice.len)
...@@ -671,13 +671,13 @@ pub fn dirnameWindows(path: []const u8) []const u8 {...@@ -671,13 +671,13 @@ pub fn dirnameWindows(path: []const u8) []const u8 {
671671
672 while ((path[end_index] == '/' or path[end_index] == '\\') and end_index > root_slice.len) {672 while ((path[end_index] == '/' or path[end_index] == '\\') and end_index > root_slice.len) {
673 if (end_index == 0)673 if (end_index == 0)
674 return path[0..0];674 return null;
675 end_index -= 1;675 end_index -= 1;
676 }676 }
677677
678 while (path[end_index] != '/' and path[end_index] != '\\' and end_index > root_slice.len) {678 while (path[end_index] != '/' and path[end_index] != '\\' and end_index > root_slice.len) {
679 if (end_index == 0)679 if (end_index == 0)
680 return path[0..0];680 return null;
681 end_index -= 1;681 end_index -= 1;
682 }682 }
683683
...@@ -685,12 +685,15 @@ pub fn dirnameWindows(path: []const u8) []const u8 {...@@ -685,12 +685,15 @@ pub fn dirnameWindows(path: []const u8) []const u8 {
685 end_index += 1;685 end_index += 1;
686 }686 }
687687
688 if (end_index == 0)
689 return null;
690
688 return path[0..end_index];691 return path[0..end_index];
689}692}
690693
691pub fn dirnamePosix(path: []const u8) []const u8 {694pub fn dirnamePosix(path: []const u8) ?[]const u8 {
692 if (path.len == 0)695 if (path.len == 0)
693 return path[0..0];696 return null;
694697
695 var end_index: usize = path.len - 1;698 var end_index: usize = path.len - 1;
696 while (path[end_index] == '/') {699 while (path[end_index] == '/') {
...@@ -701,13 +704,16 @@ pub fn dirnamePosix(path: []const u8) []const u8 {...@@ -701,13 +704,16 @@ pub fn dirnamePosix(path: []const u8) []const u8 {
701704
702 while (path[end_index] != '/') {705 while (path[end_index] != '/') {
703 if (end_index == 0)706 if (end_index == 0)
704 return path[0..0];707 return null;
705 end_index -= 1;708 end_index -= 1;
706 }709 }
707710
708 if (end_index == 0 and path[end_index] == '/')711 if (end_index == 0 and path[end_index] == '/')
709 return path[0..1];712 return path[0..1];
710713
714 if (end_index == 0)
715 return null;
716
711 return path[0..end_index];717 return path[0..end_index];
712}718}
713719
...@@ -717,10 +723,10 @@ test "os.path.dirnamePosix" {...@@ -717,10 +723,10 @@ test "os.path.dirnamePosix" {
717 testDirnamePosix("/a", "/");723 testDirnamePosix("/a", "/");
718 testDirnamePosix("/", "/");724 testDirnamePosix("/", "/");
719 testDirnamePosix("////", "/");725 testDirnamePosix("////", "/");
720 testDirnamePosix("", "");726 testDirnamePosix("", null);
721 testDirnamePosix("a", "");727 testDirnamePosix("a", null);
722 testDirnamePosix("a/", "");728 testDirnamePosix("a/", null);
723 testDirnamePosix("a//", "");729 testDirnamePosix("a//", null);
724}730}
725731
726test "os.path.dirnameWindows" {732test "os.path.dirnameWindows" {
...@@ -742,7 +748,7 @@ test "os.path.dirnameWindows" {...@@ -742,7 +748,7 @@ test "os.path.dirnameWindows" {
742 testDirnameWindows("c:foo\\bar", "c:foo");748 testDirnameWindows("c:foo\\bar", "c:foo");
743 testDirnameWindows("c:foo\\bar\\", "c:foo");749 testDirnameWindows("c:foo\\bar\\", "c:foo");
744 testDirnameWindows("c:foo\\bar\\baz", "c:foo\\bar");750 testDirnameWindows("c:foo\\bar\\baz", "c:foo\\bar");
745 testDirnameWindows("file:stream", "");751 testDirnameWindows("file:stream", null);
746 testDirnameWindows("dir\\file:stream", "dir");752 testDirnameWindows("dir\\file:stream", "dir");
747 testDirnameWindows("\\\\unc\\share", "\\\\unc\\share");753 testDirnameWindows("\\\\unc\\share", "\\\\unc\\share");
748 testDirnameWindows("\\\\unc\\share\\foo", "\\\\unc\\share\\");754 testDirnameWindows("\\\\unc\\share\\foo", "\\\\unc\\share\\");
...@@ -753,18 +759,26 @@ test "os.path.dirnameWindows" {...@@ -753,18 +759,26 @@ test "os.path.dirnameWindows" {
753 testDirnameWindows("/a/b/", "/a");759 testDirnameWindows("/a/b/", "/a");
754 testDirnameWindows("/a/b", "/a");760 testDirnameWindows("/a/b", "/a");
755 testDirnameWindows("/a", "/");761 testDirnameWindows("/a", "/");
756 testDirnameWindows("", "");762 testDirnameWindows("", null);
757 testDirnameWindows("/", "/");763 testDirnameWindows("/", "/");
758 testDirnameWindows("////", "/");764 testDirnameWindows("////", "/");
759 testDirnameWindows("foo", "");765 testDirnameWindows("foo", null);
760}766}
761767
762fn testDirnamePosix(input: []const u8, expected_output: []const u8) void {768fn testDirnamePosix(input: []const u8, expected_output: ?[]const u8) void {
763 assert(mem.eql(u8, dirnamePosix(input), expected_output));769 if (dirnamePosix(input)) |output| {
770 assert(mem.eql(u8, output, expected_output.?));
771 } else {
772 assert(expected_output == null);
773 }
764}774}
765775
766fn testDirnameWindows(input: []const u8, expected_output: []const u8) void {776fn testDirnameWindows(input: []const u8, expected_output: ?[]const u8) void {
767 assert(mem.eql(u8, dirnameWindows(input), expected_output));777 if (dirnameWindows(input)) |output| {
778 assert(mem.eql(u8, output, expected_output.?));
779 } else {
780 assert(expected_output == null);
781 }
768}782}
769783
770pub fn basename(path: []const u8) []const u8 {784pub fn basename(path: []const u8) []const u8 {
...@@ -923,7 +937,7 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8)...@@ -923,7 +937,7 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8)
923 var from_it = mem.split(resolved_from, "/\\");937 var from_it = mem.split(resolved_from, "/\\");
924 var to_it = mem.split(resolved_to, "/\\");938 var to_it = mem.split(resolved_to, "/\\");
925 while (true) {939 while (true) {
926 const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest());940 const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest());
927 const to_rest = to_it.rest();941 const to_rest = to_it.rest();
928 if (to_it.next()) |to_component| {942 if (to_it.next()) |to_component| {
929 // TODO ASCII is wrong, we actually need full unicode support to compare paths.943 // TODO ASCII is wrong, we actually need full unicode support to compare paths.
...@@ -974,7 +988,7 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![...@@ -974,7 +988,7 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![
974 var from_it = mem.split(resolved_from, "/");988 var from_it = mem.split(resolved_from, "/");
975 var to_it = mem.split(resolved_to, "/");989 var to_it = mem.split(resolved_to, "/");
976 while (true) {990 while (true) {
977 const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest());991 const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest());
978 const to_rest = to_it.rest();992 const to_rest = to_it.rest();
979 if (to_it.next()) |to_component| {993 if (to_it.next()) |to_component| {
980 if (mem.eql(u8, from_component, to_component))994 if (mem.eql(u8, from_component, to_component))
std/os/test.zig-9
...@@ -10,11 +10,6 @@ const AtomicRmwOp = builtin.AtomicRmwOp;...@@ -10,11 +10,6 @@ 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 if (builtin.os == builtin.Os.windows) {
14 // TODO implement os.Dir for windows
15 // https://github.com/ziglang/zig/issues/709
16 return;
17 }
18 try os.makePath(a, "os_test_tmp/b/c");13 try os.makePath(a, "os_test_tmp/b/c");
19 try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense");14 try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense");
20 try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah");15 try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah");
...@@ -27,10 +22,6 @@ test "makePath, put some files in it, deleteTree" {...@@ -27,10 +22,6 @@ test "makePath, put some files in it, deleteTree" {
27}22}
2823
29test "access file" {24test "access file" {
30 if (builtin.os == builtin.Os.windows) {
31 return;
32 }
33
34 try os.makePath(a, "os_test_tmp");25 try os.makePath(a, "os_test_tmp");
35 if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {26 if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
36 unreachable;27 unreachable;
std/os/time.zig+4-2
...@@ -68,11 +68,13 @@ pub const milliTimestamp = switch (builtin.os) {...@@ -68,11 +68,13 @@ pub const milliTimestamp = switch (builtin.os) {
68fn milliTimestampWindows() u64 {68fn milliTimestampWindows() u64 {
69 //FileTime has a granularity of 100 nanoseconds69 //FileTime has a granularity of 100 nanoseconds
70 // and uses the NTFS/Windows epoch70 // and uses the NTFS/Windows epoch
71 var ft: i64 = undefined;71 var ft: windows.FILETIME = undefined;
72 windows.GetSystemTimeAsFileTime(&ft);72 windows.GetSystemTimeAsFileTime(&ft);
73 const hns_per_ms = (ns_per_s / 100) / ms_per_s;73 const hns_per_ms = (ns_per_s / 100) / ms_per_s;
74 const epoch_adj = epoch.windows * ms_per_s;74 const epoch_adj = epoch.windows * ms_per_s;
75 return u64(@divFloor(ft, hns_per_ms) + epoch_adj);75
76 const ft64 = (u64(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
77 return @divFloor(ft64, hns_per_ms) - -epoch_adj;
76}78}
7779
78fn milliTimestampDarwin() u64 {80fn milliTimestampDarwin() u64 {
std/os/windows/index.zig+52-14
...@@ -1,3 +1,7 @@...@@ -1,3 +1,7 @@
1test "import" {
2 _ = @import("util.zig");
3}
4
1pub const ERROR = @import("error.zig");5pub const ERROR = @import("error.zig");
26
3pub extern "advapi32" stdcallcc fn CryptAcquireContextA(7pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
...@@ -61,6 +65,10 @@ pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;...@@ -61,6 +65,10 @@ pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
6165
62pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;66pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
6367
68pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
69pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
70pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
71
64pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;72pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
6573
66pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;74pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
...@@ -77,6 +85,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo...@@ -77,6 +85,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
7785
78pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;86pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
7987
88pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
89
80pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;90pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
8191
82pub extern "kernel32" stdcallcc fn GetLastError() DWORD;92pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
...@@ -97,21 +107,21 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(...@@ -97,21 +107,21 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
97107
98pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;108pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
99109
100pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?*FILETIME) void;110pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
101111
102pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;112pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
103pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;113pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
104pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void, dwBytes: SIZE_T) ?[*]c_void;114pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
105pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) SIZE_T;115pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
106pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) BOOL;116pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
107pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;117pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
108pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;118pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
109119
110pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;120pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
111121
112pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?[*]c_void;122pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
113123
114pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void) BOOL;124pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
115125
116pub extern "kernel32" stdcallcc fn MoveFileExA(126pub extern "kernel32" stdcallcc fn MoveFileExA(
117 lpExistingFileName: LPCSTR,127 lpExistingFileName: LPCSTR,
...@@ -123,16 +133,16 @@ pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *...@@ -123,16 +133,16 @@ pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *
123133
124pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;134pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
125135
126pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL;
127
128pub extern "kernel32" stdcallcc fn ReadFile(136pub extern "kernel32" stdcallcc fn ReadFile(
129 in_hFile: HANDLE,137 in_hFile: HANDLE,
130 out_lpBuffer: [*]c_void,138 out_lpBuffer: *c_void,
131 in_nNumberOfBytesToRead: DWORD,139 in_nNumberOfBytesToRead: DWORD,
132 out_lpNumberOfBytesRead: *DWORD,140 out_lpNumberOfBytesRead: *DWORD,
133 in_out_lpOverlapped: ?*OVERLAPPED,141 in_out_lpOverlapped: ?*OVERLAPPED,
134) BOOL;142) BOOL;
135143
144pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
145
136pub extern "kernel32" stdcallcc fn SetFilePointerEx(146pub extern "kernel32" stdcallcc fn SetFilePointerEx(
137 in_fFile: HANDLE,147 in_fFile: HANDLE,
138 in_liDistanceToMove: LARGE_INTEGER,148 in_liDistanceToMove: LARGE_INTEGER,
...@@ -150,7 +160,7 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis...@@ -150,7 +160,7 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis
150160
151pub extern "kernel32" stdcallcc fn WriteFile(161pub extern "kernel32" stdcallcc fn WriteFile(
152 in_hFile: HANDLE,162 in_hFile: HANDLE,
153 in_lpBuffer: [*]const c_void,163 in_lpBuffer: *const c_void,
154 in_nNumberOfBytesToWrite: DWORD,164 in_nNumberOfBytesToWrite: DWORD,
155 out_lpNumberOfBytesWritten: ?*DWORD,165 out_lpNumberOfBytesWritten: ?*DWORD,
156 in_out_lpOverlapped: ?*OVERLAPPED,166 in_out_lpOverlapped: ?*OVERLAPPED,
...@@ -163,6 +173,8 @@ pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;...@@ -163,6 +173,8 @@ pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
163173
164pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;174pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
165175
176pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
177
166pub const PROV_RSA_FULL = 1;178pub const PROV_RSA_FULL = 1;
167179
168pub const BOOL = c_int;180pub const BOOL = c_int;
...@@ -196,7 +208,6 @@ pub const UNICODE = false;...@@ -196,7 +208,6 @@ pub const UNICODE = false;
196pub const WCHAR = u16;208pub const WCHAR = u16;
197pub const WORD = u16;209pub const WORD = u16;
198pub const LARGE_INTEGER = i64;210pub const LARGE_INTEGER = i64;
199pub const FILETIME = i64;
200211
201pub const TRUE = 1;212pub const TRUE = 1;
202pub const FALSE = 0;213pub const FALSE = 0;
...@@ -212,6 +223,8 @@ pub const STD_ERROR_HANDLE = @maxValue(DWORD) - 12 + 1;...@@ -212,6 +223,8 @@ pub const STD_ERROR_HANDLE = @maxValue(DWORD) - 12 + 1;
212223
213pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, @maxValue(usize));224pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, @maxValue(usize));
214225
226pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD));
227
215pub const OVERLAPPED = extern struct {228pub const OVERLAPPED = extern struct {
216 Internal: ULONG_PTR,229 Internal: ULONG_PTR,
217 InternalHigh: ULONG_PTR,230 InternalHigh: ULONG_PTR,
...@@ -293,13 +306,24 @@ pub const OPEN_EXISTING = 3;...@@ -293,13 +306,24 @@ pub const OPEN_EXISTING = 3;
293pub const TRUNCATE_EXISTING = 5;306pub const TRUNCATE_EXISTING = 5;
294307
295pub const FILE_ATTRIBUTE_ARCHIVE = 0x20;308pub const FILE_ATTRIBUTE_ARCHIVE = 0x20;
309pub const FILE_ATTRIBUTE_COMPRESSED = 0x800;
310pub const FILE_ATTRIBUTE_DEVICE = 0x40;
311pub const FILE_ATTRIBUTE_DIRECTORY = 0x10;
296pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000;312pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
297pub const FILE_ATTRIBUTE_HIDDEN = 0x2;313pub const FILE_ATTRIBUTE_HIDDEN = 0x2;
314pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
298pub const FILE_ATTRIBUTE_NORMAL = 0x80;315pub const FILE_ATTRIBUTE_NORMAL = 0x80;
316pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
317pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
299pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;318pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;
300pub const FILE_ATTRIBUTE_READONLY = 0x1;319pub const FILE_ATTRIBUTE_READONLY = 0x1;
320pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000;
321pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000;
322pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
323pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
301pub const FILE_ATTRIBUTE_SYSTEM = 0x4;324pub const FILE_ATTRIBUTE_SYSTEM = 0x4;
302pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;325pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;
326pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000;
303327
304pub const PROCESS_INFORMATION = extern struct {328pub const PROCESS_INFORMATION = extern struct {
305 hProcess: HANDLE,329 hProcess: HANDLE,
...@@ -372,6 +396,20 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;...@@ -372,6 +396,20 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;
372pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;396pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
373pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;397pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
374398
375test "import" {399pub const WIN32_FIND_DATAA = extern struct {
376 _ = @import("util.zig");400 dwFileAttributes: DWORD,
377}401 ftCreationTime: FILETIME,
402 ftLastAccessTime: FILETIME,
403 ftLastWriteTime: FILETIME,
404 nFileSizeHigh: DWORD,
405 nFileSizeLow: DWORD,
406 dwReserved0: DWORD,
407 dwReserved1: DWORD,
408 cFileName: [260]CHAR,
409 cAlternateFileName: [14]CHAR,
410};
411
412pub const FILETIME = extern struct {
413 dwLowDateTime: DWORD,
414 dwHighDateTime: DWORD,
415};
std/os/windows/util.zig+41-2
...@@ -42,7 +42,7 @@ pub const WriteError = error{...@@ -42,7 +42,7 @@ pub const WriteError = error{
42};42};
4343
44pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void {44pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void {
45 if (windows.WriteFile(handle, @ptrCast([*]const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) {45 if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) {
46 const err = windows.GetLastError();46 const err = windows.GetLastError();
47 return switch (err) {47 return switch (err) {
48 windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,48 windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,
...@@ -153,7 +153,7 @@ pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap)...@@ -153,7 +153,7 @@ pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap)
153pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE {153pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE {
154 const padded_buff = try cstr.addNullByte(allocator, dll_path);154 const padded_buff = try cstr.addNullByte(allocator, dll_path);
155 defer allocator.free(padded_buff);155 defer allocator.free(padded_buff);
156 return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound;156 return windows.LoadLibraryA(padded_buff.ptr) orelse error.DllNotFound;
157}157}
158158
159pub fn windowsUnloadDll(hModule: windows.HMODULE) void {159pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
...@@ -170,3 +170,42 @@ test "InvalidDll" {...@@ -170,3 +170,42 @@ test "InvalidDll" {
170 return;170 return;
171 };171 };
172}172}
173
174pub fn windowsFindFirstFile(
175 allocator: *mem.Allocator,
176 dir_path: []const u8,
177 find_file_data: *windows.WIN32_FIND_DATAA,
178) !windows.HANDLE {
179 const wild_and_null = []u8{ '\\', '*', 0 };
180 const path_with_wild_and_null = try allocator.alloc(u8, dir_path.len + wild_and_null.len);
181 defer allocator.free(path_with_wild_and_null);
182
183 mem.copy(u8, path_with_wild_and_null, dir_path);
184 mem.copy(u8, path_with_wild_and_null[dir_path.len..], wild_and_null);
185
186 const handle = windows.FindFirstFileA(path_with_wild_and_null.ptr, find_file_data);
187
188 if (handle == windows.INVALID_HANDLE_VALUE) {
189 const err = windows.GetLastError();
190 switch (err) {
191 windows.ERROR.FILE_NOT_FOUND,
192 windows.ERROR.PATH_NOT_FOUND,
193 => return error.PathNotFound,
194 else => return os.unexpectedErrorWindows(err),
195 }
196 }
197
198 return handle;
199}
200
201/// Returns `true` if there was another file, `false` otherwise.
202pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAA) !bool {
203 if (windows.FindNextFileA(handle, find_file_data) == 0) {
204 const err = windows.GetLastError();
205 return switch (err) {
206 windows.ERROR.NO_MORE_FILES => false,
207 else => os.unexpectedErrorWindows(err),
208 };
209 }
210 return true;
211}
std/segmented_list.zig+4-4
...@@ -364,7 +364,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void {...@@ -364,7 +364,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void {
364 assert(x == 0);364 assert(x == 0);
365 }365 }
366366
367 assert(??list.pop() == 100);367 assert(list.pop().? == 100);
368 assert(list.len == 99);368 assert(list.len == 99);
369369
370 try list.pushMany([]i32{370 try list.pushMany([]i32{
...@@ -373,9 +373,9 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void {...@@ -373,9 +373,9 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void {
373 3,373 3,
374 });374 });
375 assert(list.len == 102);375 assert(list.len == 102);
376 assert(??list.pop() == 3);376 assert(list.pop().? == 3);
377 assert(??list.pop() == 2);377 assert(list.pop().? == 2);
378 assert(??list.pop() == 1);378 assert(list.pop().? == 1);
379 assert(list.len == 99);379 assert(list.len == 99);
380380
381 try list.pushMany([]const i32{});381 try list.pushMany([]const i32{});
std/special/bootstrap.zig+4-4
...@@ -51,13 +51,13 @@ extern fn WinMainCRTStartup() noreturn {...@@ -51,13 +51,13 @@ extern fn WinMainCRTStartup() noreturn {
5151
52// TODO https://github.com/ziglang/zig/issues/26552// TODO https://github.com/ziglang/zig/issues/265
53fn posixCallMainAndExit() noreturn {53fn posixCallMainAndExit() noreturn {
54 const argc = argc_ptr.*;54 const argc = argc_ptr[0];
55 const argv = @ptrCast([*][*]u8, argc_ptr + 1);55 const argv = @ptrCast([*][*]u8, argc_ptr + 1);
5656
57 const envp_nullable = @ptrCast([*]?[*]u8, argv + argc + 1);57 const envp_optional = @ptrCast([*]?[*]u8, argv + argc + 1);
58 var envp_count: usize = 0;58 var envp_count: usize = 0;
59 while (envp_nullable[envp_count]) |_| : (envp_count += 1) {}59 while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
60 const envp = @ptrCast([*][*]u8, envp_nullable)[0..envp_count];60 const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];
61 if (builtin.os == builtin.Os.linux) {61 if (builtin.os == builtin.Os.linux) {
62 const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1);62 const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1);
63 var i: usize = 0;63 var i: usize = 0;
std/special/build_runner.zig+5-5
...@@ -27,15 +27,15 @@ pub fn main() !void {...@@ -27,15 +27,15 @@ pub fn main() !void {
27 // skip my own exe name27 // skip my own exe name
28 _ = arg_it.skip();28 _ = arg_it.skip();
2929
30 const zig_exe = try unwrapArg(arg_it.next(allocator) ?? {30 const zig_exe = try unwrapArg(arg_it.next(allocator) orelse {
31 warn("Expected first argument to be path to zig compiler\n");31 warn("Expected first argument to be path to zig compiler\n");
32 return error.InvalidArgs;32 return error.InvalidArgs;
33 });33 });
34 const build_root = try unwrapArg(arg_it.next(allocator) ?? {34 const build_root = try unwrapArg(arg_it.next(allocator) orelse {
35 warn("Expected second argument to be build root directory path\n");35 warn("Expected second argument to be build root directory path\n");
36 return error.InvalidArgs;36 return error.InvalidArgs;
37 });37 });
38 const cache_root = try unwrapArg(arg_it.next(allocator) ?? {38 const cache_root = try unwrapArg(arg_it.next(allocator) orelse {
39 warn("Expected third argument to be cache root directory path\n");39 warn("Expected third argument to be cache root directory path\n");
40 return error.InvalidArgs;40 return error.InvalidArgs;
41 });41 });
...@@ -84,12 +84,12 @@ pub fn main() !void {...@@ -84,12 +84,12 @@ pub fn main() !void {
84 } else if (mem.eql(u8, arg, "--help")) {84 } else if (mem.eql(u8, arg, "--help")) {
85 return usage(&builder, false, try stdout_stream);85 return usage(&builder, false, try stdout_stream);
86 } else if (mem.eql(u8, arg, "--prefix")) {86 } else if (mem.eql(u8, arg, "--prefix")) {
87 prefix = try unwrapArg(arg_it.next(allocator) ?? {87 prefix = try unwrapArg(arg_it.next(allocator) orelse {
88 warn("Expected argument after --prefix\n\n");88 warn("Expected argument after --prefix\n\n");
89 return usageAndErr(&builder, false, try stderr_stream);89 return usageAndErr(&builder, false, try stderr_stream);
90 });90 });
91 } else if (mem.eql(u8, arg, "--search-prefix")) {91 } else if (mem.eql(u8, arg, "--search-prefix")) {
92 const search_prefix = try unwrapArg(arg_it.next(allocator) ?? {92 const search_prefix = try unwrapArg(arg_it.next(allocator) orelse {
93 warn("Expected argument after --search-prefix\n\n");93 warn("Expected argument after --search-prefix\n\n");
94 return usageAndErr(&builder, false, try stderr_stream);94 return usageAndErr(&builder, false, try stderr_stream);
95 });95 });
std/special/builtin.zig+4-4
...@@ -19,7 +19,7 @@ export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 {...@@ -19,7 +19,7 @@ export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 {
1919
20 var index: usize = 0;20 var index: usize = 0;
21 while (index != n) : (index += 1)21 while (index != n) : (index += 1)
22 (??dest)[index] = c;22 dest.?[index] = c;
2323
24 return dest;24 return dest;
25}25}
...@@ -29,7 +29,7 @@ export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*]...@@ -29,7 +29,7 @@ export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*]
2929
30 var index: usize = 0;30 var index: usize = 0;
31 while (index != n) : (index += 1)31 while (index != n) : (index += 1)
32 (??dest)[index] = (??src)[index];32 dest.?[index] = src.?[index];
3333
34 return dest;34 return dest;
35}35}
...@@ -40,13 +40,13 @@ export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 {...@@ -40,13 +40,13 @@ export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 {
40 if (@ptrToInt(dest) < @ptrToInt(src)) {40 if (@ptrToInt(dest) < @ptrToInt(src)) {
41 var index: usize = 0;41 var index: usize = 0;
42 while (index != n) : (index += 1) {42 while (index != n) : (index += 1) {
43 (??dest)[index] = (??src)[index];43 dest.?[index] = src.?[index];
44 }44 }
45 } else {45 } else {
46 var index = n;46 var index = n;
47 while (index != 0) {47 while (index != 0) {
48 index -= 1;48 index -= 1;
49 (??dest)[index] = (??src)[index];49 dest.?[index] = src.?[index];
50 }50 }
51 }51 }
5252
std/special/compiler_rt/divti3.zig created+26
...@@ -0,0 +1,26 @@
1const udivmod = @import("udivmod.zig").udivmod;
2const builtin = @import("builtin");
3const compiler_rt = @import("index.zig");
4
5pub extern fn __divti3(a: i128, b: i128) i128 {
6 @setRuntimeSafety(builtin.is_test);
7
8 const s_a = a >> (i128.bit_count - 1);
9 const s_b = b >> (i128.bit_count - 1);
10
11 const an = (a ^ s_a) -% s_a;
12 const bn = (b ^ s_b) -% s_b;
13
14 const r = udivmod(u128, @bitCast(u128, an), @bitCast(u128, bn), null);
15 const s = s_a ^ s_b;
16 return (i128(r) ^ s) -% s;
17}
18
19pub extern fn __divti3_windows_x86_64(a: *const i128, b: *const i128) void {
20 @setRuntimeSafety(builtin.is_test);
21 compiler_rt.setXmm0(i128, __divti3(a.*, b.*));
22}
23
24test "import divti3" {
25 _ = @import("divti3_test.zig");
26}
std/special/compiler_rt/divti3_test.zig created+21
...@@ -0,0 +1,21 @@
1const __divti3 = @import("divti3.zig").__divti3;
2const assert = @import("std").debug.assert;
3
4fn test__divti3(a: i128, b: i128, expected: i128) void {
5 const x = __divti3(a, b);
6 assert(x == expected);
7}
8
9test "divti3" {
10 test__divti3(0, 1, 0);
11 test__divti3(0, -1, 0);
12 test__divti3(2, 1, 2);
13 test__divti3(2, -1, -2);
14 test__divti3(-2, 1, -2);
15 test__divti3(-2, -1, 2);
16
17 test__divti3(@bitCast(i128, u128(0x8 << 124)), 1, @bitCast(i128, u128(0x8 << 124)));
18 test__divti3(@bitCast(i128, u128(0x8 << 124)), -1, @bitCast(i128, u128(0x8 << 124)));
19 test__divti3(@bitCast(i128, u128(0x8 << 124)), -2, @bitCast(i128, u128(0x4 << 124)));
20 test__divti3(@bitCast(i128, u128(0x8 << 124)), 2, @bitCast(i128, u128(0xc << 124)));
21}
std/special/compiler_rt/index.zig+4
...@@ -58,6 +58,8 @@ comptime {...@@ -58,6 +58,8 @@ comptime {
58 @export("__chkstk", __chkstk, strong_linkage);58 @export("__chkstk", __chkstk, strong_linkage);
59 @export("___chkstk_ms", ___chkstk_ms, linkage);59 @export("___chkstk_ms", ___chkstk_ms, linkage);
60 }60 }
61 @export("__divti3", @import("divti3.zig").__divti3_windows_x86_64, linkage);
62 @export("__muloti4", @import("muloti4.zig").__muloti4_windows_x86_64, linkage);
61 @export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage);63 @export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage);
62 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage);64 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage);
63 @export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage);65 @export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage);
...@@ -65,6 +67,8 @@ comptime {...@@ -65,6 +67,8 @@ comptime {
65 else => {},67 else => {},
66 }68 }
67 } else {69 } else {
70 @export("__divti3", @import("divti3.zig").__divti3, linkage);
71 @export("__muloti4", @import("muloti4.zig").__muloti4, linkage);
68 @export("__udivti3", @import("udivti3.zig").__udivti3, linkage);72 @export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
69 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);73 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
70 @export("__umodti3", @import("umodti3.zig").__umodti3, linkage);74 @export("__umodti3", @import("umodti3.zig").__umodti3, linkage);
std/special/compiler_rt/muloti4.zig created+55
...@@ -0,0 +1,55 @@
1const udivmod = @import("udivmod.zig").udivmod;
2const builtin = @import("builtin");
3const compiler_rt = @import("index.zig");
4
5pub extern fn __muloti4(a: i128, b: i128, overflow: *c_int) i128 {
6 @setRuntimeSafety(builtin.is_test);
7
8 const min = @bitCast(i128, u128(1 << (i128.bit_count - 1)));
9 const max = ~min;
10 overflow.* = 0;
11
12 const r = a *% b;
13 if (a == min) {
14 if (b != 0 and b != 1) {
15 overflow.* = 1;
16 }
17 return r;
18 }
19 if (b == min) {
20 if (a != 0 and a != 1) {
21 overflow.* = 1;
22 }
23 return r;
24 }
25
26 const sa = a >> (i128.bit_count - 1);
27 const abs_a = (a ^ sa) -% sa;
28 const sb = b >> (i128.bit_count - 1);
29 const abs_b = (b ^ sb) -% sb;
30
31 if (abs_a < 2 or abs_b < 2) {
32 return r;
33 }
34
35 if (sa == sb) {
36 if (abs_a > @divFloor(max, abs_b)) {
37 overflow.* = 1;
38 }
39 } else {
40 if (abs_a > @divFloor(min, -abs_b)) {
41 overflow.* = 1;
42 }
43 }
44
45 return r;
46}
47
48pub extern fn __muloti4_windows_x86_64(a: *const i128, b: *const i128, overflow: *c_int) void {
49 @setRuntimeSafety(builtin.is_test);
50 compiler_rt.setXmm0(i128, __muloti4(a.*, b.*, overflow));
51}
52
53test "import muloti4" {
54 _ = @import("muloti4_test.zig");
55}
std/special/compiler_rt/muloti4_test.zig created+76
...@@ -0,0 +1,76 @@
1const __muloti4 = @import("muloti4.zig").__muloti4;
2const assert = @import("std").debug.assert;
3
4fn test__muloti4(a: i128, b: i128, expected: i128, expected_overflow: c_int) void {
5 var overflow: c_int = undefined;
6 const x = __muloti4(a, b, &overflow);
7 assert(overflow == expected_overflow and (expected_overflow != 0 or x == expected));
8}
9
10test "muloti4" {
11 test__muloti4(0, 0, 0, 0);
12 test__muloti4(0, 1, 0, 0);
13 test__muloti4(1, 0, 0, 0);
14 test__muloti4(0, 10, 0, 0);
15 test__muloti4(10, 0, 0, 0);
16
17 test__muloti4(0, 81985529216486895, 0, 0);
18 test__muloti4(81985529216486895, 0, 0, 0);
19
20 test__muloti4(0, -1, 0, 0);
21 test__muloti4(-1, 0, 0, 0);
22 test__muloti4(0, -10, 0, 0);
23 test__muloti4(-10, 0, 0, 0);
24 test__muloti4(0, -81985529216486895, 0, 0);
25 test__muloti4(-81985529216486895, 0, 0, 0);
26
27 test__muloti4(3037000499, 3037000499, 9223372030926249001, 0);
28 test__muloti4(-3037000499, 3037000499, -9223372030926249001, 0);
29 test__muloti4(3037000499, -3037000499, -9223372030926249001, 0);
30 test__muloti4(-3037000499, -3037000499, 9223372030926249001, 0);
31
32 test__muloti4(4398046511103, 2097152, 9223372036852678656, 0);
33 test__muloti4(-4398046511103, 2097152, -9223372036852678656, 0);
34 test__muloti4(4398046511103, -2097152, -9223372036852678656, 0);
35 test__muloti4(-4398046511103, -2097152, 9223372036852678656, 0);
36
37 test__muloti4(2097152, 4398046511103, 9223372036852678656, 0);
38 test__muloti4(-2097152, 4398046511103, -9223372036852678656, 0);
39 test__muloti4(2097152, -4398046511103, -9223372036852678656, 0);
40 test__muloti4(-2097152, -4398046511103, 9223372036852678656, 0);
41
42 test__muloti4(@bitCast(i128, u128(0x00000000000000B504F333F9DE5BE000)), @bitCast(i128, u128(0x000000000000000000B504F333F9DE5B)), @bitCast(i128, u128(0x7FFFFFFFFFFFF328DF915DA296E8A000)), 0);
43 test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), -2, @bitCast(i128, u128(0x80000000000000000000000000000001)), 1);
44 test__muloti4(-2, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 1);
45
46 test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), -1, @bitCast(i128, u128(0x80000000000000000000000000000001)), 0);
47 test__muloti4(-1, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 0);
48 test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0, 0, 0);
49 test__muloti4(0, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0, 0);
50 test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 1, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0);
51 test__muloti4(1, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0);
52 test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 2, @bitCast(i128, u128(0x80000000000000000000000000000001)), 1);
53 test__muloti4(2, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 1);
54
55 test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), -2, @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
56 test__muloti4(-2, @bitCast(i128, u128(0x80000000000000000000000000000000)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
57 test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), -1, @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
58 test__muloti4(-1, @bitCast(i128, u128(0x80000000000000000000000000000000)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
59 test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), 0, 0, 0);
60 test__muloti4(0, @bitCast(i128, u128(0x80000000000000000000000000000000)), 0, 0);
61 test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), 1, @bitCast(i128, u128(0x80000000000000000000000000000000)), 0);
62 test__muloti4(1, @bitCast(i128, u128(0x80000000000000000000000000000000)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 0);
63 test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), 2, @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
64 test__muloti4(2, @bitCast(i128, u128(0x80000000000000000000000000000000)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
65
66 test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), -2, @bitCast(i128, u128(0x80000000000000000000000000000001)), 1);
67 test__muloti4(-2, @bitCast(i128, u128(0x80000000000000000000000000000001)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 1);
68 test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), -1, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0);
69 test__muloti4(-1, @bitCast(i128, u128(0x80000000000000000000000000000001)), @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0);
70 test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), 0, 0, 0);
71 test__muloti4(0, @bitCast(i128, u128(0x80000000000000000000000000000001)), 0, 0);
72 test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), 1, @bitCast(i128, u128(0x80000000000000000000000000000001)), 0);
73 test__muloti4(1, @bitCast(i128, u128(0x80000000000000000000000000000001)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 0);
74 test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), 2, @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
75 test__muloti4(2, @bitCast(i128, u128(0x80000000000000000000000000000001)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
76}
std/unicode.zig+13-13
...@@ -220,7 +220,7 @@ const Utf8Iterator = struct {...@@ -220,7 +220,7 @@ const Utf8Iterator = struct {
220 }220 }
221221
222 pub fn nextCodepoint(it: *Utf8Iterator) ?u32 {222 pub fn nextCodepoint(it: *Utf8Iterator) ?u32 {
223 const slice = it.nextCodepointSlice() ?? return null;223 const slice = it.nextCodepointSlice() orelse return null;
224224
225 switch (slice.len) {225 switch (slice.len) {
226 1 => return u32(slice[0]),226 1 => return u32(slice[0]),
...@@ -286,15 +286,15 @@ fn testUtf8IteratorOnAscii() void {...@@ -286,15 +286,15 @@ fn testUtf8IteratorOnAscii() void {
286 const s = Utf8View.initComptime("abc");286 const s = Utf8View.initComptime("abc");
287287
288 var it1 = s.iterator();288 var it1 = s.iterator();
289 debug.assert(std.mem.eql(u8, "a", ??it1.nextCodepointSlice()));289 debug.assert(std.mem.eql(u8, "a", it1.nextCodepointSlice().?));
290 debug.assert(std.mem.eql(u8, "b", ??it1.nextCodepointSlice()));290 debug.assert(std.mem.eql(u8, "b", it1.nextCodepointSlice().?));
291 debug.assert(std.mem.eql(u8, "c", ??it1.nextCodepointSlice()));291 debug.assert(std.mem.eql(u8, "c", it1.nextCodepointSlice().?));
292 debug.assert(it1.nextCodepointSlice() == null);292 debug.assert(it1.nextCodepointSlice() == null);
293293
294 var it2 = s.iterator();294 var it2 = s.iterator();
295 debug.assert(??it2.nextCodepoint() == 'a');295 debug.assert(it2.nextCodepoint().? == 'a');
296 debug.assert(??it2.nextCodepoint() == 'b');296 debug.assert(it2.nextCodepoint().? == 'b');
297 debug.assert(??it2.nextCodepoint() == 'c');297 debug.assert(it2.nextCodepoint().? == 'c');
298 debug.assert(it2.nextCodepoint() == null);298 debug.assert(it2.nextCodepoint() == null);
299}299}
300300
...@@ -321,15 +321,15 @@ fn testUtf8ViewOk() void {...@@ -321,15 +321,15 @@ fn testUtf8ViewOk() void {
321 const s = Utf8View.initComptime("東京市");321 const s = Utf8View.initComptime("東京市");
322322
323 var it1 = s.iterator();323 var it1 = s.iterator();
324 debug.assert(std.mem.eql(u8, "東", ??it1.nextCodepointSlice()));324 debug.assert(std.mem.eql(u8, "東", it1.nextCodepointSlice().?));
325 debug.assert(std.mem.eql(u8, "京", ??it1.nextCodepointSlice()));325 debug.assert(std.mem.eql(u8, "京", it1.nextCodepointSlice().?));
326 debug.assert(std.mem.eql(u8, "市", ??it1.nextCodepointSlice()));326 debug.assert(std.mem.eql(u8, "市", it1.nextCodepointSlice().?));
327 debug.assert(it1.nextCodepointSlice() == null);327 debug.assert(it1.nextCodepointSlice() == null);
328328
329 var it2 = s.iterator();329 var it2 = s.iterator();
330 debug.assert(??it2.nextCodepoint() == 0x6771);330 debug.assert(it2.nextCodepoint().? == 0x6771);
331 debug.assert(??it2.nextCodepoint() == 0x4eac);331 debug.assert(it2.nextCodepoint().? == 0x4eac);
332 debug.assert(??it2.nextCodepoint() == 0x5e02);332 debug.assert(it2.nextCodepoint().? == 0x5e02);
333 debug.assert(it2.nextCodepoint() == null);333 debug.assert(it2.nextCodepoint() == null);
334}334}
335335
std/zig/ast.zig+32-16
...@@ -734,7 +734,7 @@ pub const Node = struct {...@@ -734,7 +734,7 @@ pub const Node = struct {
734 var i = index;734 var i = index;
735735
736 if (self.doc_comments) |comments| {736 if (self.doc_comments) |comments| {
737 if (i < 1) return *comments.base;737 if (i < 1) return &comments.base;
738 i -= 1;738 i -= 1;
739 }739 }
740740
...@@ -1243,7 +1243,7 @@ pub const Node = struct {...@@ -1243,7 +1243,7 @@ pub const Node = struct {
1243 i -= 1;1243 i -= 1;
12441244
1245 if (self.@"else") |@"else"| {1245 if (self.@"else") |@"else"| {
1246 if (i < 1) return *@"else".base;1246 if (i < 1) return &@"else".base;
1247 i -= 1;1247 i -= 1;
1248 }1248 }
12491249
...@@ -1296,7 +1296,7 @@ pub const Node = struct {...@@ -1296,7 +1296,7 @@ pub const Node = struct {
1296 i -= 1;1296 i -= 1;
12971297
1298 if (self.@"else") |@"else"| {1298 if (self.@"else") |@"else"| {
1299 if (i < 1) return *@"else".base;1299 if (i < 1) return &@"else".base;
1300 i -= 1;1300 i -= 1;
1301 }1301 }
13021302
...@@ -1347,7 +1347,7 @@ pub const Node = struct {...@@ -1347,7 +1347,7 @@ pub const Node = struct {
1347 i -= 1;1347 i -= 1;
13481348
1349 if (self.@"else") |@"else"| {1349 if (self.@"else") |@"else"| {
1350 if (i < 1) return *@"else".base;1350 if (i < 1) return &@"else".base;
1351 i -= 1;1351 i -= 1;
1352 }1352 }
13531353
...@@ -1417,7 +1417,7 @@ pub const Node = struct {...@@ -1417,7 +1417,7 @@ pub const Node = struct {
1417 Range,1417 Range,
1418 Sub,1418 Sub,
1419 SubWrap,1419 SubWrap,
1420 UnwrapMaybe,1420 UnwrapOptional,
1421 };1421 };
14221422
1423 pub fn iterate(self: *InfixOp, index: usize) ?*Node {1423 pub fn iterate(self: *InfixOp, index: usize) ?*Node {
...@@ -1475,7 +1475,7 @@ pub const Node = struct {...@@ -1475,7 +1475,7 @@ pub const Node = struct {
1475 Op.Range,1475 Op.Range,
1476 Op.Sub,1476 Op.Sub,
1477 Op.SubWrap,1477 Op.SubWrap,
1478 Op.UnwrapMaybe,1478 Op.UnwrapOptional,
1479 => {},1479 => {},
1480 }1480 }
14811481
...@@ -1507,14 +1507,13 @@ pub const Node = struct {...@@ -1507,14 +1507,13 @@ pub const Node = struct {
1507 BitNot,1507 BitNot,
1508 BoolNot,1508 BoolNot,
1509 Cancel,1509 Cancel,
1510 MaybeType,1510 OptionalType,
1511 Negation,1511 Negation,
1512 NegationWrap,1512 NegationWrap,
1513 Resume,1513 Resume,
1514 PtrType: PtrInfo,1514 PtrType: PtrInfo,
1515 SliceType: PtrInfo,1515 SliceType: PtrInfo,
1516 Try,1516 Try,
1517 UnwrapMaybe,
1518 };1517 };
15191518
1520 pub const PtrInfo = struct {1519 pub const PtrInfo = struct {
...@@ -1537,33 +1536,36 @@ pub const Node = struct {...@@ -1537,33 +1536,36 @@ pub const Node = struct {
1537 var i = index;1536 var i = index;
15381537
1539 switch (self.op) {1538 switch (self.op) {
1539 // TODO https://github.com/ziglang/zig/issues/1107
1540 Op.SliceType => |addr_of_info| {1540 Op.SliceType => |addr_of_info| {
1541 if (addr_of_info.align_info) |align_info| {1541 if (addr_of_info.align_info) |align_info| {
1542 if (i < 1) return align_info.node;1542 if (i < 1) return align_info.node;
1543 i -= 1;1543 i -= 1;
1544 }1544 }
1545 },1545 },
1546 Op.AddrOf => |addr_of_info| {1546
1547 Op.PtrType => |addr_of_info| {
1547 if (addr_of_info.align_info) |align_info| {1548 if (addr_of_info.align_info) |align_info| {
1548 if (i < 1) return align_info.node;1549 if (i < 1) return align_info.node;
1549 i -= 1;1550 i -= 1;
1550 }1551 }
1551 },1552 },
1553
1552 Op.ArrayType => |size_expr| {1554 Op.ArrayType => |size_expr| {
1553 if (i < 1) return size_expr;1555 if (i < 1) return size_expr;
1554 i -= 1;1556 i -= 1;
1555 },1557 },
1558
1559 Op.AddressOf,
1556 Op.Await,1560 Op.Await,
1557 Op.BitNot,1561 Op.BitNot,
1558 Op.BoolNot,1562 Op.BoolNot,
1559 Op.Cancel,1563 Op.Cancel,
1560 Op.MaybeType,1564 Op.OptionalType,
1561 Op.Negation,1565 Op.Negation,
1562 Op.NegationWrap,1566 Op.NegationWrap,
1563 Op.Try,1567 Op.Try,
1564 Op.Resume,1568 Op.Resume,
1565 Op.UnwrapMaybe,
1566 Op.PointerType,
1567 => {},1569 => {},
1568 }1570 }
15691571
...@@ -1619,6 +1621,7 @@ pub const Node = struct {...@@ -1619,6 +1621,7 @@ pub const Node = struct {
1619 ArrayInitializer: InitList,1621 ArrayInitializer: InitList,
1620 StructInitializer: InitList,1622 StructInitializer: InitList,
1621 Deref,1623 Deref,
1624 UnwrapOptional,
16221625
1623 pub const InitList = SegmentedList(*Node, 2);1626 pub const InitList = SegmentedList(*Node, 2);
16241627
...@@ -1667,7 +1670,9 @@ pub const Node = struct {...@@ -1667,7 +1670,9 @@ pub const Node = struct {
1667 if (i < fields.len) return fields.at(i).*;1670 if (i < fields.len) return fields.at(i).*;
1668 i -= fields.len;1671 i -= fields.len;
1669 },1672 },
1670 Op.Deref => {},1673 Op.UnwrapOptional,
1674 Op.Deref,
1675 => {},
1671 }1676 }
16721677
1673 return null;1678 return null;
...@@ -2022,7 +2027,7 @@ pub const Node = struct {...@@ -2022,7 +2027,7 @@ pub const Node = struct {
20222027
2023 switch (self.kind) {2028 switch (self.kind) {
2024 Kind.Variable => |variable_name| {2029 Kind.Variable => |variable_name| {
2025 if (i < 1) return *variable_name.base;2030 if (i < 1) return &variable_name.base;
2026 i -= 1;2031 i -= 1;
2027 },2032 },
2028 Kind.Return => |return_type| {2033 Kind.Return => |return_type| {
...@@ -2092,10 +2097,10 @@ pub const Node = struct {...@@ -2092,10 +2097,10 @@ pub const Node = struct {
2092 pub fn iterate(self: *Asm, index: usize) ?*Node {2097 pub fn iterate(self: *Asm, index: usize) ?*Node {
2093 var i = index;2098 var i = index;
20942099
2095 if (i < self.outputs.len) return *(self.outputs.at(index).*).base;2100 if (i < self.outputs.len) return &self.outputs.at(index).*.base;
2096 i -= self.outputs.len;2101 i -= self.outputs.len;
20972102
2098 if (i < self.inputs.len) return *(self.inputs.at(index).*).base;2103 if (i < self.inputs.len) return &self.inputs.at(index).*.base;
2099 i -= self.inputs.len;2104 i -= self.inputs.len;
21002105
2101 return null;2106 return null;
...@@ -2205,3 +2210,14 @@ pub const Node = struct {...@@ -2205,3 +2210,14 @@ pub const Node = struct {
2205 }2210 }
2206 };2211 };
2207};2212};
2213
2214test "iterate" {
2215 var root = Node.Root{
2216 .base = Node{ .id = Node.Id.Root },
2217 .doc_comments = null,
2218 .decls = Node.Root.DeclList.init(std.debug.global_allocator),
2219 .eof_token = 0,
2220 };
2221 var base = &root.base;
2222 assert(base.iterate(0) == null);
2223}
std/zig/parse.zig+44-34
...@@ -43,7 +43,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -43,7 +43,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
4343
44 // skip over line comments at the top of the file44 // skip over line comments at the top of the file
45 while (true) {45 while (true) {
46 const next_tok = tok_it.peek() ?? break;46 const next_tok = tok_it.peek() orelse break;
47 if (next_tok.id != Token.Id.LineComment) break;47 if (next_tok.id != Token.Id.LineComment) break;
48 _ = tok_it.next();48 _ = tok_it.next();
49 }49 }
...@@ -197,7 +197,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -197,7 +197,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
197 const lib_name_token = nextToken(&tok_it, &tree);197 const lib_name_token = nextToken(&tok_it, &tree);
198 const lib_name_token_index = lib_name_token.index;198 const lib_name_token_index = lib_name_token.index;
199 const lib_name_token_ptr = lib_name_token.ptr;199 const lib_name_token_ptr = lib_name_token.ptr;
200 break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, &tree)) ?? {200 break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, &tree)) orelse {
201 prevToken(&tok_it, &tree);201 prevToken(&tok_it, &tree);
202 break :blk null;202 break :blk null;
203 };203 };
...@@ -711,7 +711,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -711,7 +711,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
711 else => {711 else => {
712 // TODO: this is a special case. Remove this when #760 is fixed712 // TODO: this is a special case. Remove this when #760 is fixed
713 if (token_ptr.id == Token.Id.Keyword_error) {713 if (token_ptr.id == Token.Id.Keyword_error) {
714 if ((??tok_it.peek()).id == Token.Id.LBrace) {714 if (tok_it.peek().?.id == Token.Id.LBrace) {
715 const error_type_node = try arena.construct(ast.Node.ErrorType{715 const error_type_node = try arena.construct(ast.Node.ErrorType{
716 .base = ast.Node{ .id = ast.Node.Id.ErrorType },716 .base = ast.Node{ .id = ast.Node.Id.ErrorType },
717 .token = token_index,717 .token = token_index,
...@@ -1434,14 +1434,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -1434,14 +1434,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
1434 try stack.append(State{1434 try stack.append(State{
1435 .ExpectTokenSave = ExpectTokenSave{1435 .ExpectTokenSave = ExpectTokenSave{
1436 .id = Token.Id.AngleBracketRight,1436 .id = Token.Id.AngleBracketRight,
1437 .ptr = &??async_node.rangle_bracket,1437 .ptr = &async_node.rangle_bracket.?,
1438 },1438 },
1439 });1439 });
1440 try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &async_node.allocator_type } });1440 try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &async_node.allocator_type } });
1441 continue;1441 continue;
1442 },1442 },
1443 State.AsyncEnd => |ctx| {1443 State.AsyncEnd => |ctx| {
1444 const node = ctx.ctx.get() ?? continue;1444 const node = ctx.ctx.get() orelse continue;
14451445
1446 switch (node.id) {1446 switch (node.id) {
1447 ast.Node.Id.FnProto => {1447 ast.Node.Id.FnProto => {
...@@ -1567,7 +1567,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -1567,7 +1567,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
1567 .bit_range = null,1567 .bit_range = null,
1568 };1568 };
1569 // TODO https://github.com/ziglang/zig/issues/10221569 // TODO https://github.com/ziglang/zig/issues/1022
1570 const align_info = &??addr_of_info.align_info;1570 const align_info = &addr_of_info.align_info.?;
15711571
1572 try stack.append(State{ .AlignBitRange = align_info });1572 try stack.append(State{ .AlignBitRange = align_info });
1573 try stack.append(State{ .Expression = OptionalCtx{ .Required = &align_info.node } });1573 try stack.append(State{ .Expression = OptionalCtx{ .Required = &align_info.node } });
...@@ -1604,7 +1604,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -1604,7 +1604,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
1604 switch (token.ptr.id) {1604 switch (token.ptr.id) {
1605 Token.Id.Colon => {1605 Token.Id.Colon => {
1606 align_info.bit_range = ast.Node.PrefixOp.PtrInfo.Align.BitRange(undefined);1606 align_info.bit_range = ast.Node.PrefixOp.PtrInfo.Align.BitRange(undefined);
1607 const bit_range = &??align_info.bit_range;1607 const bit_range = &align_info.bit_range.?;
16081608
1609 try stack.append(State{ .ExpectToken = Token.Id.RParen });1609 try stack.append(State{ .ExpectToken = Token.Id.RParen });
1610 try stack.append(State{ .Expression = OptionalCtx{ .Required = &bit_range.end } });1610 try stack.append(State{ .Expression = OptionalCtx{ .Required = &bit_range.end } });
...@@ -1814,7 +1814,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -1814,7 +1814,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
1814 continue;1814 continue;
1815 },1815 },
1816 State.RangeExpressionEnd => |opt_ctx| {1816 State.RangeExpressionEnd => |opt_ctx| {
1817 const lhs = opt_ctx.get() ?? continue;1817 const lhs = opt_ctx.get() orelse continue;
18181818
1819 if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| {1819 if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| {
1820 const node = try arena.construct(ast.Node.InfixOp{1820 const node = try arena.construct(ast.Node.InfixOp{
...@@ -1836,7 +1836,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -1836,7 +1836,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
1836 },1836 },
18371837
1838 State.AssignmentExpressionEnd => |opt_ctx| {1838 State.AssignmentExpressionEnd => |opt_ctx| {
1839 const lhs = opt_ctx.get() ?? continue;1839 const lhs = opt_ctx.get() orelse continue;
18401840
1841 const token = nextToken(&tok_it, &tree);1841 const token = nextToken(&tok_it, &tree);
1842 const token_index = token.index;1842 const token_index = token.index;
...@@ -1866,7 +1866,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -1866,7 +1866,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
1866 },1866 },
18671867
1868 State.UnwrapExpressionEnd => |opt_ctx| {1868 State.UnwrapExpressionEnd => |opt_ctx| {
1869 const lhs = opt_ctx.get() ?? continue;1869 const lhs = opt_ctx.get() orelse continue;
18701870
1871 const token = nextToken(&tok_it, &tree);1871 const token = nextToken(&tok_it, &tree);
1872 const token_index = token.index;1872 const token_index = token.index;
...@@ -1901,7 +1901,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -1901,7 +1901,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
1901 },1901 },
19021902
1903 State.BoolOrExpressionEnd => |opt_ctx| {1903 State.BoolOrExpressionEnd => |opt_ctx| {
1904 const lhs = opt_ctx.get() ?? continue;1904 const lhs = opt_ctx.get() orelse continue;
19051905
1906 if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| {1906 if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| {
1907 const node = try arena.construct(ast.Node.InfixOp{1907 const node = try arena.construct(ast.Node.InfixOp{
...@@ -1925,7 +1925,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -1925,7 +1925,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
1925 },1925 },
19261926
1927 State.BoolAndExpressionEnd => |opt_ctx| {1927 State.BoolAndExpressionEnd => |opt_ctx| {
1928 const lhs = opt_ctx.get() ?? continue;1928 const lhs = opt_ctx.get() orelse continue;
19291929
1930 if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| {1930 if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| {
1931 const node = try arena.construct(ast.Node.InfixOp{1931 const node = try arena.construct(ast.Node.InfixOp{
...@@ -1949,7 +1949,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -1949,7 +1949,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
1949 },1949 },
19501950
1951 State.ComparisonExpressionEnd => |opt_ctx| {1951 State.ComparisonExpressionEnd => |opt_ctx| {
1952 const lhs = opt_ctx.get() ?? continue;1952 const lhs = opt_ctx.get() orelse continue;
19531953
1954 const token = nextToken(&tok_it, &tree);1954 const token = nextToken(&tok_it, &tree);
1955 const token_index = token.index;1955 const token_index = token.index;
...@@ -1979,7 +1979,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -1979,7 +1979,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
1979 },1979 },
19801980
1981 State.BinaryOrExpressionEnd => |opt_ctx| {1981 State.BinaryOrExpressionEnd => |opt_ctx| {
1982 const lhs = opt_ctx.get() ?? continue;1982 const lhs = opt_ctx.get() orelse continue;
19831983
1984 if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| {1984 if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| {
1985 const node = try arena.construct(ast.Node.InfixOp{1985 const node = try arena.construct(ast.Node.InfixOp{
...@@ -2003,7 +2003,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -2003,7 +2003,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
2003 },2003 },
20042004
2005 State.BinaryXorExpressionEnd => |opt_ctx| {2005 State.BinaryXorExpressionEnd => |opt_ctx| {
2006 const lhs = opt_ctx.get() ?? continue;2006 const lhs = opt_ctx.get() orelse continue;
20072007
2008 if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| {2008 if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| {
2009 const node = try arena.construct(ast.Node.InfixOp{2009 const node = try arena.construct(ast.Node.InfixOp{
...@@ -2027,7 +2027,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -2027,7 +2027,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
2027 },2027 },
20282028
2029 State.BinaryAndExpressionEnd => |opt_ctx| {2029 State.BinaryAndExpressionEnd => |opt_ctx| {
2030 const lhs = opt_ctx.get() ?? continue;2030 const lhs = opt_ctx.get() orelse continue;
20312031
2032 if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| {2032 if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| {
2033 const node = try arena.construct(ast.Node.InfixOp{2033 const node = try arena.construct(ast.Node.InfixOp{
...@@ -2051,7 +2051,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -2051,7 +2051,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
2051 },2051 },
20522052
2053 State.BitShiftExpressionEnd => |opt_ctx| {2053 State.BitShiftExpressionEnd => |opt_ctx| {
2054 const lhs = opt_ctx.get() ?? continue;2054 const lhs = opt_ctx.get() orelse continue;
20552055
2056 const token = nextToken(&tok_it, &tree);2056 const token = nextToken(&tok_it, &tree);
2057 const token_index = token.index;2057 const token_index = token.index;
...@@ -2081,7 +2081,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -2081,7 +2081,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
2081 },2081 },
20822082
2083 State.AdditionExpressionEnd => |opt_ctx| {2083 State.AdditionExpressionEnd => |opt_ctx| {
2084 const lhs = opt_ctx.get() ?? continue;2084 const lhs = opt_ctx.get() orelse continue;
20852085
2086 const token = nextToken(&tok_it, &tree);2086 const token = nextToken(&tok_it, &tree);
2087 const token_index = token.index;2087 const token_index = token.index;
...@@ -2111,7 +2111,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -2111,7 +2111,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
2111 },2111 },
21122112
2113 State.MultiplyExpressionEnd => |opt_ctx| {2113 State.MultiplyExpressionEnd => |opt_ctx| {
2114 const lhs = opt_ctx.get() ?? continue;2114 const lhs = opt_ctx.get() orelse continue;
21152115
2116 const token = nextToken(&tok_it, &tree);2116 const token = nextToken(&tok_it, &tree);
2117 const token_index = token.index;2117 const token_index = token.index;
...@@ -2142,9 +2142,9 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -2142,9 +2142,9 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
2142 },2142 },
21432143
2144 State.CurlySuffixExpressionEnd => |opt_ctx| {2144 State.CurlySuffixExpressionEnd => |opt_ctx| {
2145 const lhs = opt_ctx.get() ?? continue;2145 const lhs = opt_ctx.get() orelse continue;
21462146
2147 if ((??tok_it.peek()).id == Token.Id.Period) {2147 if (tok_it.peek().?.id == Token.Id.Period) {
2148 const node = try arena.construct(ast.Node.SuffixOp{2148 const node = try arena.construct(ast.Node.SuffixOp{
2149 .base = ast.Node{ .id = ast.Node.Id.SuffixOp },2149 .base = ast.Node{ .id = ast.Node.Id.SuffixOp },
2150 .lhs = lhs,2150 .lhs = lhs,
...@@ -2190,7 +2190,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -2190,7 +2190,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
2190 },2190 },
21912191
2192 State.TypeExprEnd => |opt_ctx| {2192 State.TypeExprEnd => |opt_ctx| {
2193 const lhs = opt_ctx.get() ?? continue;2193 const lhs = opt_ctx.get() orelse continue;
21942194
2195 if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| {2195 if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| {
2196 const node = try arena.construct(ast.Node.InfixOp{2196 const node = try arena.construct(ast.Node.InfixOp{
...@@ -2270,7 +2270,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -2270,7 +2270,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
2270 },2270 },
22712271
2272 State.SuffixOpExpressionEnd => |opt_ctx| {2272 State.SuffixOpExpressionEnd => |opt_ctx| {
2273 const lhs = opt_ctx.get() ?? continue;2273 const lhs = opt_ctx.get() orelse continue;
22742274
2275 const token = nextToken(&tok_it, &tree);2275 const token = nextToken(&tok_it, &tree);
2276 const token_index = token.index;2276 const token_index = token.index;
...@@ -2326,6 +2326,17 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -2326,6 +2326,17 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
2326 stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;2326 stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2327 continue;2327 continue;
2328 }2328 }
2329 if (eatToken(&tok_it, &tree, Token.Id.QuestionMark)) |question_token| {
2330 const node = try arena.construct(ast.Node.SuffixOp{
2331 .base = ast.Node{ .id = ast.Node.Id.SuffixOp },
2332 .lhs = lhs,
2333 .op = ast.Node.SuffixOp.Op.UnwrapOptional,
2334 .rtoken = question_token,
2335 });
2336 opt_ctx.store(&node.base);
2337 stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2338 continue;
2339 }
2329 const node = try arena.construct(ast.Node.InfixOp{2340 const node = try arena.construct(ast.Node.InfixOp{
2330 .base = ast.Node{ .id = ast.Node.Id.InfixOp },2341 .base = ast.Node{ .id = ast.Node.Id.InfixOp },
2331 .lhs = lhs,2342 .lhs = lhs,
...@@ -2403,12 +2414,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -2403,12 +2414,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
2403 .arrow_token = next_token_index,2414 .arrow_token = next_token_index,
2404 .return_type = undefined,2415 .return_type = undefined,
2405 };2416 };
2406 const return_type_ptr = &((??node.result).return_type);2417 const return_type_ptr = &node.result.?.return_type;
2407 try stack.append(State{ .Expression = OptionalCtx{ .Required = return_type_ptr } });2418 try stack.append(State{ .Expression = OptionalCtx{ .Required = return_type_ptr } });
2408 continue;2419 continue;
2409 },2420 },
2410 Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {2421 Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
2411 opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) ?? unreachable);2422 opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) orelse unreachable);
2412 continue;2423 continue;
2413 },2424 },
2414 Token.Id.LParen => {2425 Token.Id.LParen => {
...@@ -2638,7 +2649,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {...@@ -2638,7 +2649,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
2638 const token = nextToken(&tok_it, &tree);2649 const token = nextToken(&tok_it, &tree);
2639 const token_index = token.index;2650 const token_index = token.index;
2640 const token_ptr = token.ptr;2651 const token_ptr = token.ptr;
2641 opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) ?? {2652 opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) orelse {
2642 prevToken(&tok_it, &tree);2653 prevToken(&tok_it, &tree);
2643 if (opt_ctx != OptionalCtx.Optional) {2654 if (opt_ctx != OptionalCtx.Optional) {
2644 ((try tree.errors.addOne())).* = Error{ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr{ .token = token_index } };2655 ((try tree.errors.addOne())).* = Error{ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr{ .token = token_index } };
...@@ -2875,7 +2886,7 @@ const OptionalCtx = union(enum) {...@@ -2875,7 +2886,7 @@ const OptionalCtx = union(enum) {
2875 pub fn get(self: *const OptionalCtx) ?*ast.Node {2886 pub fn get(self: *const OptionalCtx) ?*ast.Node {
2876 switch (self.*) {2887 switch (self.*) {
2877 OptionalCtx.Optional => |ptr| return ptr.*,2888 OptionalCtx.Optional => |ptr| return ptr.*,
2878 OptionalCtx.RequiredNull => |ptr| return ??ptr.*,2889 OptionalCtx.RequiredNull => |ptr| return ptr.*.?,
2879 OptionalCtx.Required => |ptr| return ptr.*,2890 OptionalCtx.Required => |ptr| return ptr.*,
2880 }2891 }
2881 }2892 }
...@@ -3237,7 +3248,7 @@ fn tokenIdToAssignment(id: *const Token.Id) ?ast.Node.InfixOp.Op {...@@ -3237,7 +3248,7 @@ fn tokenIdToAssignment(id: *const Token.Id) ?ast.Node.InfixOp.Op {
3237fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {3248fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
3238 return switch (id) {3249 return switch (id) {
3239 Token.Id.Keyword_catch => ast.Node.InfixOp.Op{ .Catch = null },3250 Token.Id.Keyword_catch => ast.Node.InfixOp.Op{ .Catch = null },
3240 Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op{ .UnwrapMaybe = void{} },3251 Token.Id.Keyword_orelse => ast.Node.InfixOp.Op{ .UnwrapOptional = void{} },
3241 else => null,3252 else => null,
3242 };3253 };
3243}3254}
...@@ -3299,8 +3310,7 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op {...@@ -3299,8 +3310,7 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op {
3299 .volatile_token = null,3310 .volatile_token = null,
3300 },3311 },
3301 },3312 },
3302 Token.Id.QuestionMark => ast.Node.PrefixOp.Op{ .MaybeType = void{} },3313 Token.Id.QuestionMark => ast.Node.PrefixOp.Op{ .OptionalType = void{} },
3303 Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op{ .UnwrapMaybe = void{} },
3304 Token.Id.Keyword_await => ast.Node.PrefixOp.Op{ .Await = void{} },3314 Token.Id.Keyword_await => ast.Node.PrefixOp.Op{ .Await = void{} },
3305 Token.Id.Keyword_try => ast.Node.PrefixOp.Op{ .Try = void{} },3315 Token.Id.Keyword_try => ast.Node.PrefixOp.Op{ .Try = void{} },
3306 else => null,3316 else => null,
...@@ -3322,7 +3332,7 @@ fn createToCtxLiteral(arena: *mem.Allocator, opt_ctx: *const OptionalCtx, compti...@@ -3322,7 +3332,7 @@ fn createToCtxLiteral(arena: *mem.Allocator, opt_ctx: *const OptionalCtx, compti
3322}3332}
33233333
3324fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType(Token.Id)) ?TokenIndex {3334fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType(Token.Id)) ?TokenIndex {
3325 const token = ??tok_it.peek();3335 const token = tok_it.peek().?;
33263336
3327 if (token.id == id) {3337 if (token.id == id) {
3328 return nextToken(tok_it, tree).index;3338 return nextToken(tok_it, tree).index;
...@@ -3334,12 +3344,12 @@ fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType(...@@ -3334,12 +3344,12 @@ fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType(
3334fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedToken {3344fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedToken {
3335 const result = AnnotatedToken{3345 const result = AnnotatedToken{
3336 .index = tok_it.index,3346 .index = tok_it.index,
3337 .ptr = ??tok_it.next(),3347 .ptr = tok_it.next().?,
3338 };3348 };
3339 assert(result.ptr.id != Token.Id.LineComment);3349 assert(result.ptr.id != Token.Id.LineComment);
33403350
3341 while (true) {3351 while (true) {
3342 const next_tok = tok_it.peek() ?? return result;3352 const next_tok = tok_it.peek() orelse return result;
3343 if (next_tok.id != Token.Id.LineComment) return result;3353 if (next_tok.id != Token.Id.LineComment) return result;
3344 _ = tok_it.next();3354 _ = tok_it.next();
3345 }3355 }
...@@ -3347,7 +3357,7 @@ fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedTok...@@ -3347,7 +3357,7 @@ fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedTok
33473357
3348fn prevToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) void {3358fn prevToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) void {
3349 while (true) {3359 while (true) {
3350 const prev_tok = tok_it.prev() ?? return;3360 const prev_tok = tok_it.prev() orelse return;
3351 if (prev_tok.id == Token.Id.LineComment) continue;3361 if (prev_tok.id == Token.Id.LineComment) continue;
3352 return;3362 return;
3353 }3363 }
std/zig/parser_test.zig+4-3
...@@ -650,9 +650,10 @@ test "zig fmt: statements with empty line between" {...@@ -650,9 +650,10 @@ test "zig fmt: statements with empty line between" {
650 );650 );
651}651}
652652
653test "zig fmt: ptr deref operator" {653test "zig fmt: ptr deref operator and unwrap optional operator" {
654 try testCanonical(654 try testCanonical(
655 \\const a = b.*;655 \\const a = b.*;
656 \\const a = b.?;
656 \\657 \\
657 );658 );
658}659}
...@@ -1150,7 +1151,7 @@ test "zig fmt: infix operators" {...@@ -1150,7 +1151,7 @@ test "zig fmt: infix operators" {
1150 \\ _ = i!i;1151 \\ _ = i!i;
1151 \\ _ = i ** i;1152 \\ _ = i ** i;
1152 \\ _ = i ++ i;1153 \\ _ = i ++ i;
1153 \\ _ = i ?? i;1154 \\ _ = i orelse i;
1154 \\ _ = i % i;1155 \\ _ = i % i;
1155 \\ _ = i / i;1156 \\ _ = i / i;
1156 \\ _ = i *% i;1157 \\ _ = i *% i;
...@@ -1209,7 +1210,7 @@ test "zig fmt: precedence" {...@@ -1209,7 +1210,7 @@ test "zig fmt: precedence" {
1209test "zig fmt: prefix operators" {1210test "zig fmt: prefix operators" {
1210 try testCanonical(1211 try testCanonical(
1211 \\test "prefix operators" {1212 \\test "prefix operators" {
1212 \\ try return --%~??!*&0;1213 \\ try return --%~!*&0;
1213 \\}1214 \\}
1214 \\1215 \\
1215 );1216 );
std/zig/render.zig+16-17
...@@ -83,7 +83,7 @@ fn renderRoot(...@@ -83,7 +83,7 @@ fn renderRoot(
83 var start_col: usize = 0;83 var start_col: usize = 0;
84 var it = tree.root_node.decls.iterator(0);84 var it = tree.root_node.decls.iterator(0);
85 while (true) {85 while (true) {
86 var decl = (it.next() ?? return).*;86 var decl = (it.next() orelse return).*;
87 // look for zig fmt: off comment87 // look for zig fmt: off comment
88 var start_token_index = decl.firstToken();88 var start_token_index = decl.firstToken();
89 zig_fmt_loop: while (start_token_index != 0) {89 zig_fmt_loop: while (start_token_index != 0) {
...@@ -112,7 +112,7 @@ fn renderRoot(...@@ -112,7 +112,7 @@ fn renderRoot(
112 const start = tree.tokens.at(start_token_index + 1).start;112 const start = tree.tokens.at(start_token_index + 1).start;
113 try stream.print("{}\n", tree.source[start..end_token.end]);113 try stream.print("{}\n", tree.source[start..end_token.end]);
114 while (tree.tokens.at(decl.firstToken()).start < end_token.end) {114 while (tree.tokens.at(decl.firstToken()).start < end_token.end) {
115 decl = (it.next() ?? return).*;115 decl = (it.next() orelse return).*;
116 }116 }
117 break :zig_fmt_loop;117 break :zig_fmt_loop;
118 }118 }
...@@ -222,7 +222,7 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i...@@ -222,7 +222,7 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i
222 }222 }
223 }223 }
224224
225 const value_expr = ??tag.value_expr;225 const value_expr = tag.value_expr.?;
226 try renderToken(tree, stream, tree.prevToken(value_expr.firstToken()), indent, start_col, Space.Space); // =226 try renderToken(tree, stream, tree.prevToken(value_expr.firstToken()), indent, start_col, Space.Space); // =
227 try renderExpression(allocator, stream, tree, indent, start_col, value_expr, Space.Comma); // value,227 try renderExpression(allocator, stream, tree, indent, start_col, value_expr, Space.Comma); // value,
228 },228 },
...@@ -465,8 +465,7 @@ fn renderExpression(...@@ -465,8 +465,7 @@ fn renderExpression(
465 ast.Node.PrefixOp.Op.BoolNot,465 ast.Node.PrefixOp.Op.BoolNot,
466 ast.Node.PrefixOp.Op.Negation,466 ast.Node.PrefixOp.Op.Negation,
467 ast.Node.PrefixOp.Op.NegationWrap,467 ast.Node.PrefixOp.Op.NegationWrap,
468 ast.Node.PrefixOp.Op.UnwrapMaybe,468 ast.Node.PrefixOp.Op.OptionalType,
469 ast.Node.PrefixOp.Op.MaybeType,
470 ast.Node.PrefixOp.Op.AddressOf,469 ast.Node.PrefixOp.Op.AddressOf,
471 => {470 => {
472 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None);471 try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None);
...@@ -513,7 +512,7 @@ fn renderExpression(...@@ -513,7 +512,7 @@ fn renderExpression(
513512
514 var it = call_info.params.iterator(0);513 var it = call_info.params.iterator(0);
515 while (true) {514 while (true) {
516 const param_node = ??it.next();515 const param_node = it.next().?;
517516
518 const param_node_new_indent = if (param_node.*.id == ast.Node.Id.MultilineStringLiteral) blk: {517 const param_node_new_indent = if (param_node.*.id == ast.Node.Id.MultilineStringLiteral) blk: {
519 break :blk indent;518 break :blk indent;
...@@ -559,10 +558,10 @@ fn renderExpression(...@@ -559,10 +558,10 @@ fn renderExpression(
559 return renderToken(tree, stream, rbracket, indent, start_col, space); // ]558 return renderToken(tree, stream, rbracket, indent, start_col, space); // ]
560 },559 },
561560
562 ast.Node.SuffixOp.Op.Deref => {561 ast.Node.SuffixOp.Op.Deref, ast.Node.SuffixOp.Op.UnwrapOptional => {
563 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);562 try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
564 try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // .563 try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // .
565 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // *564 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // * or ?
566 },565 },
567566
568 @TagType(ast.Node.SuffixOp.Op).Slice => |range| {567 @TagType(ast.Node.SuffixOp.Op).Slice => |range| {
...@@ -595,7 +594,7 @@ fn renderExpression(...@@ -595,7 +594,7 @@ fn renderExpression(
595 }594 }
596595
597 if (field_inits.len == 1) blk: {596 if (field_inits.len == 1) blk: {
598 const field_init = ??field_inits.at(0).*.cast(ast.Node.FieldInitializer);597 const field_init = field_inits.at(0).*.cast(ast.Node.FieldInitializer).?;
599598
600 if (field_init.expr.cast(ast.Node.SuffixOp)) |nested_suffix_op| {599 if (field_init.expr.cast(ast.Node.SuffixOp)) |nested_suffix_op| {
601 if (nested_suffix_op.op == ast.Node.SuffixOp.Op.StructInitializer) {600 if (nested_suffix_op.op == ast.Node.SuffixOp.Op.StructInitializer) {
...@@ -688,7 +687,7 @@ fn renderExpression(...@@ -688,7 +687,7 @@ fn renderExpression(
688 var count: usize = 1;687 var count: usize = 1;
689 var it = exprs.iterator(0);688 var it = exprs.iterator(0);
690 while (true) {689 while (true) {
691 const expr = (??it.next()).*;690 const expr = it.next().?.*;
692 if (it.peek()) |next_expr| {691 if (it.peek()) |next_expr| {
693 const expr_last_token = expr.*.lastToken() + 1;692 const expr_last_token = expr.*.lastToken() + 1;
694 const loc = tree.tokenLocation(tree.tokens.at(expr_last_token).end, next_expr.*.firstToken());693 const loc = tree.tokenLocation(tree.tokens.at(expr_last_token).end, next_expr.*.firstToken());
...@@ -806,7 +805,7 @@ fn renderExpression(...@@ -806,7 +805,7 @@ fn renderExpression(
806 },805 },
807 }806 }
808807
809 return renderExpression(allocator, stream, tree, indent, start_col, ??flow_expr.rhs, space);808 return renderExpression(allocator, stream, tree, indent, start_col, flow_expr.rhs.?, space);
810 },809 },
811810
812 ast.Node.Id.Payload => {811 ast.Node.Id.Payload => {
...@@ -1245,7 +1244,7 @@ fn renderExpression(...@@ -1245,7 +1244,7 @@ fn renderExpression(
1245 } else {1244 } else {
1246 var it = switch_case.items.iterator(0);1245 var it = switch_case.items.iterator(0);
1247 while (true) {1246 while (true) {
1248 const node = ??it.next();1247 const node = it.next().?;
1249 if (it.peek()) |next_node| {1248 if (it.peek()) |next_node| {
1250 try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None);1249 try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None);
12511250
...@@ -1550,7 +1549,7 @@ fn renderExpression(...@@ -1550,7 +1549,7 @@ fn renderExpression(
15501549
1551 var it = asm_node.outputs.iterator(0);1550 var it = asm_node.outputs.iterator(0);
1552 while (true) {1551 while (true) {
1553 const asm_output = ??it.next();1552 const asm_output = it.next().?;
1554 const node = &(asm_output.*).base;1553 const node = &(asm_output.*).base;
15551554
1556 if (it.peek()) |next_asm_output| {1555 if (it.peek()) |next_asm_output| {
...@@ -1588,7 +1587,7 @@ fn renderExpression(...@@ -1588,7 +1587,7 @@ fn renderExpression(
15881587
1589 var it = asm_node.inputs.iterator(0);1588 var it = asm_node.inputs.iterator(0);
1590 while (true) {1589 while (true) {
1591 const asm_input = ??it.next();1590 const asm_input = it.next().?;
1592 const node = &(asm_input.*).base;1591 const node = &(asm_input.*).base;
15931592
1594 if (it.peek()) |next_asm_input| {1593 if (it.peek()) |next_asm_input| {
...@@ -1620,7 +1619,7 @@ fn renderExpression(...@@ -1620,7 +1619,7 @@ fn renderExpression(
16201619
1621 var it = asm_node.clobbers.iterator(0);1620 var it = asm_node.clobbers.iterator(0);
1622 while (true) {1621 while (true) {
1623 const clobber_token = ??it.next();1622 const clobber_token = it.next().?;
16241623
1625 if (it.peek() == null) {1624 if (it.peek() == null) {
1626 try renderToken(tree, stream, clobber_token.*, indent_once, start_col, Space.Newline);1625 try renderToken(tree, stream, clobber_token.*, indent_once, start_col, Space.Newline);
...@@ -1994,7 +1993,7 @@ fn renderDocComments(...@@ -1994,7 +1993,7 @@ fn renderDocComments(
1994 indent: usize,1993 indent: usize,
1995 start_col: *usize,1994 start_col: *usize,
1996) (@typeOf(stream).Child.Error || Error)!void {1995) (@typeOf(stream).Child.Error || Error)!void {
1997 const comment = node.doc_comments ?? return;1996 const comment = node.doc_comments orelse return;
1998 var it = comment.lines.iterator(0);1997 var it = comment.lines.iterator(0);
1999 const first_token = node.firstToken();1998 const first_token = node.firstToken();
2000 while (it.next()) |line_token_index| {1999 while (it.next()) |line_token_index| {
...@@ -2022,7 +2021,7 @@ fn nodeIsBlock(base: *const ast.Node) bool {...@@ -2022,7 +2021,7 @@ fn nodeIsBlock(base: *const ast.Node) bool {
2022}2021}
20232022
2024fn nodeCausesSliceOpSpace(base: *ast.Node) bool {2023fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
2025 const infix_op = base.cast(ast.Node.InfixOp) ?? return false;2024 const infix_op = base.cast(ast.Node.InfixOp) orelse return false;
2026 return switch (infix_op.op) {2025 return switch (infix_op.op) {
2027 ast.Node.InfixOp.Op.Period => false,2026 ast.Node.InfixOp.Op.Period => false,
2028 else => true,2027 else => true,
std/zig/tokenizer.zig+7-20
...@@ -39,6 +39,7 @@ pub const Token = struct {...@@ -39,6 +39,7 @@ pub const Token = struct {
39 Keyword{ .bytes = "noalias", .id = Id.Keyword_noalias },39 Keyword{ .bytes = "noalias", .id = Id.Keyword_noalias },
40 Keyword{ .bytes = "null", .id = Id.Keyword_null },40 Keyword{ .bytes = "null", .id = Id.Keyword_null },
41 Keyword{ .bytes = "or", .id = Id.Keyword_or },41 Keyword{ .bytes = "or", .id = Id.Keyword_or },
42 Keyword{ .bytes = "orelse", .id = Id.Keyword_orelse },
42 Keyword{ .bytes = "packed", .id = Id.Keyword_packed },43 Keyword{ .bytes = "packed", .id = Id.Keyword_packed },
43 Keyword{ .bytes = "promise", .id = Id.Keyword_promise },44 Keyword{ .bytes = "promise", .id = Id.Keyword_promise },
44 Keyword{ .bytes = "pub", .id = Id.Keyword_pub },45 Keyword{ .bytes = "pub", .id = Id.Keyword_pub },
...@@ -129,7 +130,6 @@ pub const Token = struct {...@@ -129,7 +130,6 @@ pub const Token = struct {
129 Ampersand,130 Ampersand,
130 AmpersandEqual,131 AmpersandEqual,
131 QuestionMark,132 QuestionMark,
132 QuestionMarkQuestionMark,
133 AngleBracketLeft,133 AngleBracketLeft,
134 AngleBracketLeftEqual,134 AngleBracketLeftEqual,
135 AngleBracketAngleBracketLeft,135 AngleBracketAngleBracketLeft,
...@@ -171,6 +171,7 @@ pub const Token = struct {...@@ -171,6 +171,7 @@ pub const Token = struct {
171 Keyword_noalias,171 Keyword_noalias,
172 Keyword_null,172 Keyword_null,
173 Keyword_or,173 Keyword_or,
174 Keyword_orelse,
174 Keyword_packed,175 Keyword_packed,
175 Keyword_promise,176 Keyword_promise,
176 Keyword_pub,177 Keyword_pub,
...@@ -254,7 +255,6 @@ pub const Tokenizer = struct {...@@ -254,7 +255,6 @@ pub const Tokenizer = struct {
254 Ampersand,255 Ampersand,
255 Caret,256 Caret,
256 Percent,257 Percent,
257 QuestionMark,
258 Plus,258 Plus,
259 PlusPercent,259 PlusPercent,
260 AngleBracketLeft,260 AngleBracketLeft,
...@@ -345,6 +345,11 @@ pub const Tokenizer = struct {...@@ -345,6 +345,11 @@ pub const Tokenizer = struct {
345 self.index += 1;345 self.index += 1;
346 break;346 break;
347 },347 },
348 '?' => {
349 result.id = Token.Id.QuestionMark;
350 self.index += 1;
351 break;
352 },
348 ':' => {353 ':' => {
349 result.id = Token.Id.Colon;354 result.id = Token.Id.Colon;
350 self.index += 1;355 self.index += 1;
...@@ -359,9 +364,6 @@ pub const Tokenizer = struct {...@@ -359,9 +364,6 @@ pub const Tokenizer = struct {
359 '+' => {364 '+' => {
360 state = State.Plus;365 state = State.Plus;
361 },366 },
362 '?' => {
363 state = State.QuestionMark;
364 },
365 '<' => {367 '<' => {
366 state = State.AngleBracketLeft;368 state = State.AngleBracketLeft;
367 },369 },
...@@ -496,18 +498,6 @@ pub const Tokenizer = struct {...@@ -496,18 +498,6 @@ pub const Tokenizer = struct {
496 },498 },
497 },499 },
498500
499 State.QuestionMark => switch (c) {
500 '?' => {
501 result.id = Token.Id.QuestionMarkQuestionMark;
502 self.index += 1;
503 break;
504 },
505 else => {
506 result.id = Token.Id.QuestionMark;
507 break;
508 },
509 },
510
511 State.Percent => switch (c) {501 State.Percent => switch (c) {
512 '=' => {502 '=' => {
513 result.id = Token.Id.PercentEqual;503 result.id = Token.Id.PercentEqual;
...@@ -1084,9 +1074,6 @@ pub const Tokenizer = struct {...@@ -1084,9 +1074,6 @@ pub const Tokenizer = struct {
1084 State.Plus => {1074 State.Plus => {
1085 result.id = Token.Id.Plus;1075 result.id = Token.Id.Plus;
1086 },1076 },
1087 State.QuestionMark => {
1088 result.id = Token.Id.QuestionMark;
1089 },
1090 State.Percent => {1077 State.Percent => {
1091 result.id = Token.Id.Percent;1078 result.id = Token.Id.Percent;
1092 },1079 },
test/behavior.zig+1
...@@ -31,6 +31,7 @@ comptime {...@@ -31,6 +31,7 @@ comptime {
31 _ = @import("cases/incomplete_struct_param_tld.zig");31 _ = @import("cases/incomplete_struct_param_tld.zig");
32 _ = @import("cases/ir_block_deps.zig");32 _ = @import("cases/ir_block_deps.zig");
33 _ = @import("cases/math.zig");33 _ = @import("cases/math.zig");
34 _ = @import("cases/merge_error_sets.zig");
34 _ = @import("cases/misc.zig");35 _ = @import("cases/misc.zig");
35 _ = @import("cases/namespace_depends_on_compile_var/index.zig");36 _ = @import("cases/namespace_depends_on_compile_var/index.zig");
36 _ = @import("cases/new_stack_call.zig");37 _ = @import("cases/new_stack_call.zig");
test/cases/array.zig+9-1
...@@ -116,6 +116,15 @@ test "array len property" {...@@ -116,6 +116,15 @@ test "array len property" {
116 assert(@typeOf(x).len == 5);116 assert(@typeOf(x).len == 5);
117}117}
118118
119test "array len field" {
120 var arr = [4]u8{ 0, 0, 0, 0 };
121 var ptr = &arr;
122 assert(arr.len == 4);
123 comptime assert(arr.len == 4);
124 assert(ptr.len == 4);
125 comptime assert(ptr.len == 4);
126}
127
119test "single-item pointer to array indexing and slicing" {128test "single-item pointer to array indexing and slicing" {
120 testSingleItemPtrArrayIndexSlice();129 testSingleItemPtrArrayIndexSlice();
121 comptime testSingleItemPtrArrayIndexSlice();130 comptime testSingleItemPtrArrayIndexSlice();
...@@ -143,4 +152,3 @@ fn testImplicitCastSingleItemPtr() void {...@@ -143,4 +152,3 @@ fn testImplicitCastSingleItemPtr() void {
143 slice[0] += 1;152 slice[0] += 1;
144 assert(byte == 101);153 assert(byte == 101);
145}154}
146
test/cases/bugs/656.zig+1-1
...@@ -9,7 +9,7 @@ const Value = struct {...@@ -9,7 +9,7 @@ const Value = struct {
9 align_expr: ?u32,9 align_expr: ?u32,
10};10};
1111
12test "nullable if after an if in a switch prong of a switch with 2 prongs in an else" {12test "optional if after an if in a switch prong of a switch with 2 prongs in an else" {
13 foo(false, true);13 foo(false, true);
14}14}
1515
test/cases/cast.zig+51-29
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const assert = @import("std").debug.assert;1const std = @import("std");
2const mem = @import("std").mem;2const assert = std.debug.assert;
3const mem = std.mem;
34
4test "int to ptr cast" {5test "int to ptr cast" {
5 const x = usize(13);6 const x = usize(13);
...@@ -72,7 +73,7 @@ fn Struct(comptime T: type) type {...@@ -72,7 +73,7 @@ fn Struct(comptime T: type) type {
7273
73 fn maybePointer(self: ?*const Self) Self {74 fn maybePointer(self: ?*const Self) Self {
74 const none = Self{ .x = if (T == void) void{} else 0 };75 const none = Self{ .x = if (T == void) void{} else 0 };
75 return (self ?? &none).*;76 return (self orelse &none).*;
76 }77 }
77 };78 };
78}79}
...@@ -86,7 +87,7 @@ const Union = union {...@@ -86,7 +87,7 @@ const Union = union {
8687
87 fn maybePointer(self: ?*const Union) Union {88 fn maybePointer(self: ?*const Union) Union {
88 const none = Union{ .x = 0 };89 const none = Union{ .x = 0 };
89 return (self ?? &none).*;90 return (self orelse &none).*;
90 }91 }
91};92};
9293
...@@ -99,7 +100,7 @@ const Enum = enum {...@@ -99,7 +100,7 @@ const Enum = enum {
99 }100 }
100101
101 fn maybePointer(self: ?*const Enum) Enum {102 fn maybePointer(self: ?*const Enum) Enum {
102 return (self ?? &Enum.None).*;103 return (self orelse &Enum.None).*;
103 }104 }
104};105};
105106
...@@ -108,16 +109,16 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" {...@@ -108,16 +109,16 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" {
108 const Self = this;109 const Self = this;
109 x: u8,110 x: u8,
110 fn constConst(p: *const *const Self) u8 {111 fn constConst(p: *const *const Self) u8 {
111 return (p.*).x;112 return p.*.x;
112 }113 }
113 fn maybeConstConst(p: ?*const *const Self) u8 {114 fn maybeConstConst(p: ?*const *const Self) u8 {
114 return ((??p).*).x;115 return p.?.*.x;
115 }116 }
116 fn constConstConst(p: *const *const *const Self) u8 {117 fn constConstConst(p: *const *const *const Self) u8 {
117 return (p.*.*).x;118 return p.*.*.x;
118 }119 }
119 fn maybeConstConstConst(p: ?*const *const *const Self) u8 {120 fn maybeConstConstConst(p: ?*const *const *const Self) u8 {
120 return ((??p).*.*).x;121 return p.?.*.*.x;
121 }122 }
122 };123 };
123 const s = S{ .x = 42 };124 const s = S{ .x = 42 };
...@@ -176,56 +177,56 @@ test "string literal to &const []const u8" {...@@ -176,56 +177,56 @@ test "string literal to &const []const u8" {
176}177}
177178
178test "implicitly cast from T to error!?T" {179test "implicitly cast from T to error!?T" {
179 castToMaybeTypeError(1);180 castToOptionalTypeError(1);
180 comptime castToMaybeTypeError(1);181 comptime castToOptionalTypeError(1);
181}182}
182const A = struct {183const A = struct {
183 a: i32,184 a: i32,
184};185};
185fn castToMaybeTypeError(z: i32) void {186fn castToOptionalTypeError(z: i32) void {
186 const x = i32(1);187 const x = i32(1);
187 const y: error!?i32 = x;188 const y: error!?i32 = x;
188 assert(??(try y) == 1);189 assert((try y).? == 1);
189190
190 const f = z;191 const f = z;
191 const g: error!?i32 = f;192 const g: error!?i32 = f;
192193
193 const a = A{ .a = z };194 const a = A{ .a = z };
194 const b: error!?A = a;195 const b: error!?A = a;
195 assert((??(b catch unreachable)).a == 1);196 assert((b catch unreachable).?.a == 1);
196}197}
197198
198test "implicitly cast from int to error!?T" {199test "implicitly cast from int to error!?T" {
199 implicitIntLitToMaybe();200 implicitIntLitToOptional();
200 comptime implicitIntLitToMaybe();201 comptime implicitIntLitToOptional();
201}202}
202fn implicitIntLitToMaybe() void {203fn implicitIntLitToOptional() void {
203 const f: ?i32 = 1;204 const f: ?i32 = 1;
204 const g: error!?i32 = 1;205 const g: error!?i32 = 1;
205}206}
206207
207test "return null from fn() error!?&T" {208test "return null from fn() error!?&T" {
208 const a = returnNullFromMaybeTypeErrorRef();209 const a = returnNullFromOptionalTypeErrorRef();
209 const b = returnNullLitFromMaybeTypeErrorRef();210 const b = returnNullLitFromOptionalTypeErrorRef();
210 assert((try a) == null and (try b) == null);211 assert((try a) == null and (try b) == null);
211}212}
212fn returnNullFromMaybeTypeErrorRef() error!?*A {213fn returnNullFromOptionalTypeErrorRef() error!?*A {
213 const a: ?*A = null;214 const a: ?*A = null;
214 return a;215 return a;
215}216}
216fn returnNullLitFromMaybeTypeErrorRef() error!?*A {217fn returnNullLitFromOptionalTypeErrorRef() error!?*A {
217 return null;218 return null;
218}219}
219220
220test "peer type resolution: ?T and T" {221test "peer type resolution: ?T and T" {
221 assert(??peerTypeTAndMaybeT(true, false) == 0);222 assert(peerTypeTAndOptionalT(true, false).? == 0);
222 assert(??peerTypeTAndMaybeT(false, false) == 3);223 assert(peerTypeTAndOptionalT(false, false).? == 3);
223 comptime {224 comptime {
224 assert(??peerTypeTAndMaybeT(true, false) == 0);225 assert(peerTypeTAndOptionalT(true, false).? == 0);
225 assert(??peerTypeTAndMaybeT(false, false) == 3);226 assert(peerTypeTAndOptionalT(false, false).? == 3);
226 }227 }
227}228}
228fn peerTypeTAndMaybeT(c: bool, b: bool) ?usize {229fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize {
229 if (c) {230 if (c) {
230 return if (b) null else usize(0);231 return if (b) null else usize(0);
231 }232 }
...@@ -250,11 +251,11 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {...@@ -250,11 +251,11 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
250}251}
251252
252test "implicitly cast from [N]T to ?[]const T" {253test "implicitly cast from [N]T to ?[]const T" {
253 assert(mem.eql(u8, ??castToMaybeSlice(), "hi"));254 assert(mem.eql(u8, castToOptionalSlice().?, "hi"));
254 comptime assert(mem.eql(u8, ??castToMaybeSlice(), "hi"));255 comptime assert(mem.eql(u8, castToOptionalSlice().?, "hi"));
255}256}
256257
257fn castToMaybeSlice() ?[]const u8 {258fn castToOptionalSlice() ?[]const u8 {
258 return "hi";259 return "hi";
259}260}
260261
...@@ -384,3 +385,24 @@ test "const slice widen cast" {...@@ -384,3 +385,24 @@ test "const slice widen cast" {
384385
385 assert(@bitCast(u32, bytes) == 0x12121212);386 assert(@bitCast(u32, bytes) == 0x12121212);
386}387}
388
389test "single-item pointer of array to slice and to unknown length pointer" {
390 testCastPtrOfArrayToSliceAndPtr();
391 comptime testCastPtrOfArrayToSliceAndPtr();
392}
393
394fn testCastPtrOfArrayToSliceAndPtr() void {
395 var array = "ao" ++ "eu"; // TODO https://github.com/ziglang/zig/issues/1076
396 const x: [*]u8 = &array;
397 x[0] += 1;
398 assert(mem.eql(u8, array[0..], "boeu"));
399 const y: []u8 = &array;
400 y[0] += 1;
401 assert(mem.eql(u8, array[0..], "coeu"));
402}
403
404test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
405 const window_name = [1][*]const u8{c"window name"};
406 const x: [*]const ?[*]const u8 = &window_name;
407 assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name"));
408}
test/cases/enum.zig+9
...@@ -883,3 +883,12 @@ test "empty extern enum with members" {...@@ -883,3 +883,12 @@ test "empty extern enum with members" {
883 };883 };
884 assert(@sizeOf(E) == @sizeOf(c_int));884 assert(@sizeOf(E) == @sizeOf(c_int));
885}885}
886
887test "aoeu" {
888 const LocalFoo = enum {
889 A = 1,
890 B = 0,
891 };
892 var b = LocalFoo.B;
893 assert(mem.eql(u8, @tagName(b), "B"));
894}
test/cases/error.zig+1-1
...@@ -140,7 +140,7 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void {...@@ -140,7 +140,7 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void {
140 if (x) |v| assert(v == 1234) else |err| @compileError("bad");140 if (x) |v| assert(v == 1234) else |err| @compileError("bad");
141}141}
142142
143test "syntax: nullable operator in front of error union operator" {143test "syntax: optional operator in front of error union operator" {
144 comptime {144 comptime {
145 assert(?error!i32 == ?(error!i32));145 assert(?error!i32 == ?(error!i32));
146 }146 }
test/cases/eval.zig+14-1
...@@ -12,7 +12,7 @@ fn fibonacci(x: i32) i32 {...@@ -12,7 +12,7 @@ fn fibonacci(x: i32) i32 {
12}12}
1313
14fn unwrapAndAddOne(blah: ?i32) i32 {14fn unwrapAndAddOne(blah: ?i32) i32 {
15 return ??blah + 1;15 return blah.? + 1;
16}16}
17const should_be_1235 = unwrapAndAddOne(1234);17const should_be_1235 = unwrapAndAddOne(1234);
18test "static add one" {18test "static add one" {
...@@ -610,3 +610,16 @@ test "slice of type" {...@@ -610,3 +610,16 @@ test "slice of type" {
610 }610 }
611 }611 }
612}612}
613
614const Wrapper = struct {
615 T: type,
616};
617
618fn wrap(comptime T: type) Wrapper {
619 return Wrapper{ .T = T };
620}
621
622test "function which returns struct with type field causes implicit comptime" {
623 const ty = wrap(i32).T;
624 assert(ty == i32);
625}
test/cases/generics.zig+1-1
...@@ -127,7 +127,7 @@ test "generic fn with implicit cast" {...@@ -127,7 +127,7 @@ test "generic fn with implicit cast" {
127 }) == 0);127 }) == 0);
128}128}
129fn getByte(ptr: ?*const u8) u8 {129fn getByte(ptr: ?*const u8) u8 {
130 return (??ptr).*;130 return ptr.?.*;
131}131}
132fn getFirstByte(comptime T: type, mem: []const T) u8 {132fn getFirstByte(comptime T: type, mem: []const T) u8 {
133 return getByte(@ptrCast(*const u8, &mem[0]));133 return getByte(@ptrCast(*const u8, &mem[0]));
test/cases/merge_error_sets.zig created+21
...@@ -0,0 +1,21 @@
1const A = error{
2 PathNotFound,
3 NotDir,
4};
5const B = error{OutOfMemory};
6
7const C = A || B;
8
9fn foo() C!void {
10 return error.NotDir;
11}
12
13test "merge error sets" {
14 if (foo()) {
15 @panic("unexpected");
16 } else |err| switch (err) {
17 error.OutOfMemory => @panic("unexpected"),
18 error.PathNotFound => @panic("unexpected"),
19 error.NotDir => {},
20 }
21}
test/cases/misc.zig+1-9
...@@ -505,7 +505,7 @@ test "@typeId" {...@@ -505,7 +505,7 @@ test "@typeId" {
505 assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat);505 assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat);
506 assert(@typeId(@typeOf(undefined)) == Tid.Undefined);506 assert(@typeId(@typeOf(undefined)) == Tid.Undefined);
507 assert(@typeId(@typeOf(null)) == Tid.Null);507 assert(@typeId(@typeOf(null)) == Tid.Null);
508 assert(@typeId(?i32) == Tid.Nullable);508 assert(@typeId(?i32) == Tid.Optional);
509 assert(@typeId(error!i32) == Tid.ErrorUnion);509 assert(@typeId(error!i32) == Tid.ErrorUnion);
510 assert(@typeId(error) == Tid.ErrorSet);510 assert(@typeId(error) == Tid.ErrorSet);
511 assert(@typeId(AnEnum) == Tid.Enum);511 assert(@typeId(AnEnum) == Tid.Enum);
...@@ -523,14 +523,6 @@ test "@typeId" {...@@ -523,14 +523,6 @@ test "@typeId" {
523 }523 }
524}524}
525525
526test "@canImplicitCast" {
527 comptime {
528 assert(@canImplicitCast(i64, i32(3)));
529 assert(!@canImplicitCast(i32, f32(1.234)));
530 assert(@canImplicitCast([]const u8, "aoeu"));
531 }
532}
533
534test "@typeName" {526test "@typeName" {
535 const Struct = struct {};527 const Struct = struct {};
536 const Union = union {528 const Union = union {
test/cases/null.zig+31-20
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const assert = @import("std").debug.assert;1const assert = @import("std").debug.assert;
22
3test "nullable type" {3test "optional type" {
4 const x: ?bool = true;4 const x: ?bool = true;
55
6 if (x) |y| {6 if (x) |y| {
...@@ -15,13 +15,13 @@ test "nullable type" {...@@ -15,13 +15,13 @@ test "nullable type" {
1515
16 const next_x: ?i32 = null;16 const next_x: ?i32 = null;
1717
18 const z = next_x ?? 1234;18 const z = next_x orelse 1234;
1919
20 assert(z == 1234);20 assert(z == 1234);
2121
22 const final_x: ?i32 = 13;22 const final_x: ?i32 = 13;
2323
24 const num = final_x ?? unreachable;24 const num = final_x orelse unreachable;
2525
26 assert(num == 13);26 assert(num == 13);
27}27}
...@@ -33,12 +33,12 @@ test "test maybe object and get a pointer to the inner value" {...@@ -33,12 +33,12 @@ test "test maybe object and get a pointer to the inner value" {
33 b.* = false;33 b.* = false;
34 }34 }
3535
36 assert(??maybe_bool == false);36 assert(maybe_bool.? == false);
37}37}
3838
39test "rhs maybe unwrap return" {39test "rhs maybe unwrap return" {
40 const x: ?bool = true;40 const x: ?bool = true;
41 const y = x ?? return;41 const y = x orelse return;
42}42}
4343
44test "maybe return" {44test "maybe return" {
...@@ -47,13 +47,13 @@ test "maybe return" {...@@ -47,13 +47,13 @@ test "maybe return" {
47}47}
4848
49fn maybeReturnImpl() void {49fn maybeReturnImpl() void {
50 assert(??foo(1235));50 assert(foo(1235).?);
51 if (foo(null) != null) unreachable;51 if (foo(null) != null) unreachable;
52 assert(!??foo(1234));52 assert(!foo(1234).?);
53}53}
5454
55fn foo(x: ?i32) ?bool {55fn foo(x: ?i32) ?bool {
56 const value = x ?? return null;56 const value = x orelse return null;
57 return value > 1234;57 return value > 1234;
58}58}
5959
...@@ -102,12 +102,12 @@ fn testTestNullRuntime(x: ?i32) void {...@@ -102,12 +102,12 @@ fn testTestNullRuntime(x: ?i32) void {
102 assert(!(x != null));102 assert(!(x != null));
103}103}
104104
105test "nullable void" {105test "optional void" {
106 nullableVoidImpl();106 optionalVoidImpl();
107 comptime nullableVoidImpl();107 comptime optionalVoidImpl();
108}108}
109109
110fn nullableVoidImpl() void {110fn optionalVoidImpl() void {
111 assert(bar(null) == null);111 assert(bar(null) == null);
112 assert(bar({}) != null);112 assert(bar({}) != null);
113}113}
...@@ -120,19 +120,19 @@ fn bar(x: ?void) ?void {...@@ -120,19 +120,19 @@ fn bar(x: ?void) ?void {
120 }120 }
121}121}
122122
123const StructWithNullable = struct {123const StructWithOptional = struct {
124 field: ?i32,124 field: ?i32,
125};125};
126126
127var struct_with_nullable: StructWithNullable = undefined;127var struct_with_optional: StructWithOptional = undefined;
128128
129test "unwrap nullable which is field of global var" {129test "unwrap optional which is field of global var" {
130 struct_with_nullable.field = null;130 struct_with_optional.field = null;
131 if (struct_with_nullable.field) |payload| {131 if (struct_with_optional.field) |payload| {
132 unreachable;132 unreachable;
133 }133 }
134 struct_with_nullable.field = 1234;134 struct_with_optional.field = 1234;
135 if (struct_with_nullable.field) |payload| {135 if (struct_with_optional.field) |payload| {
136 assert(payload == 1234);136 assert(payload == 1234);
137 } else {137 } else {
138 unreachable;138 unreachable;
...@@ -140,6 +140,17 @@ test "unwrap nullable which is field of global var" {...@@ -140,6 +140,17 @@ test "unwrap nullable which is field of global var" {
140}140}
141141
142test "null with default unwrap" {142test "null with default unwrap" {
143 const x: i32 = null ?? 1;143 const x: i32 = null orelse 1;
144 assert(x == 1);144 assert(x == 1);
145}145}
146
147test "optional types" {
148 comptime {
149 const opt_type_struct = StructWithOptionalType { .t=u8, };
150 assert(opt_type_struct.t != null and opt_type_struct.t.? == u8);
151 }
152}
153
154const StructWithOptionalType = struct {
155 t: ?type,
156};
test/cases/reflection.zig+1-1
...@@ -2,7 +2,7 @@ const assert = @import("std").debug.assert;...@@ -2,7 +2,7 @@ const assert = @import("std").debug.assert;
2const mem = @import("std").mem;2const mem = @import("std").mem;
3const reflection = this;3const reflection = this;
44
5test "reflection: array, pointer, nullable, error union type child" {5test "reflection: array, pointer, optional, error union type child" {
6 comptime {6 comptime {
7 assert(([10]u8).Child == u8);7 assert(([10]u8).Child == u8);
8 assert((*u8).Child == u8);8 assert((*u8).Child == u8);
test/cases/struct.zig+17
...@@ -421,3 +421,20 @@ const Expr = union(enum) {...@@ -421,3 +421,20 @@ const Expr = union(enum) {
421fn alloc(comptime T: type) []T {421fn alloc(comptime T: type) []T {
422 return []T{};422 return []T{};
423}423}
424
425test "call method with mutable reference to struct with no fields" {
426 const S = struct {
427 fn doC(s: *const this) bool {
428 return true;
429 }
430 fn do(s: *this) bool {
431 return true;
432 }
433 };
434
435 var s = S{};
436 assert(S.doC(&s));
437 assert(s.doC());
438 assert(S.do(&s));
439 assert(s.do());
440}
test/cases/type_info.zig+32-15
...@@ -39,12 +39,28 @@ test "type info: pointer type info" {...@@ -39,12 +39,28 @@ test "type info: pointer type info" {
39fn testPointer() void {39fn testPointer() void {
40 const u32_ptr_info = @typeInfo(*u32);40 const u32_ptr_info = @typeInfo(*u32);
41 assert(TypeId(u32_ptr_info) == TypeId.Pointer);41 assert(TypeId(u32_ptr_info) == TypeId.Pointer);
42 assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One);
42 assert(u32_ptr_info.Pointer.is_const == false);43 assert(u32_ptr_info.Pointer.is_const == false);
43 assert(u32_ptr_info.Pointer.is_volatile == false);44 assert(u32_ptr_info.Pointer.is_volatile == false);
44 assert(u32_ptr_info.Pointer.alignment == 4);45 assert(u32_ptr_info.Pointer.alignment == @alignOf(u32));
45 assert(u32_ptr_info.Pointer.child == u32);46 assert(u32_ptr_info.Pointer.child == u32);
46}47}
4748
49test "type info: unknown length pointer type info" {
50 testUnknownLenPtr();
51 comptime testUnknownLenPtr();
52}
53
54fn testUnknownLenPtr() void {
55 const u32_ptr_info = @typeInfo([*]const volatile f64);
56 assert(TypeId(u32_ptr_info) == TypeId.Pointer);
57 assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many);
58 assert(u32_ptr_info.Pointer.is_const == true);
59 assert(u32_ptr_info.Pointer.is_volatile == true);
60 assert(u32_ptr_info.Pointer.alignment == @alignOf(f64));
61 assert(u32_ptr_info.Pointer.child == f64);
62}
63
48test "type info: slice type info" {64test "type info: slice type info" {
49 testSlice();65 testSlice();
50 comptime testSlice();66 comptime testSlice();
...@@ -52,11 +68,12 @@ test "type info: slice type info" {...@@ -52,11 +68,12 @@ test "type info: slice type info" {
5268
53fn testSlice() void {69fn testSlice() void {
54 const u32_slice_info = @typeInfo([]u32);70 const u32_slice_info = @typeInfo([]u32);
55 assert(TypeId(u32_slice_info) == TypeId.Slice);71 assert(TypeId(u32_slice_info) == TypeId.Pointer);
56 assert(u32_slice_info.Slice.is_const == false);72 assert(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice);
57 assert(u32_slice_info.Slice.is_volatile == false);73 assert(u32_slice_info.Pointer.is_const == false);
58 assert(u32_slice_info.Slice.alignment == 4);74 assert(u32_slice_info.Pointer.is_volatile == false);
59 assert(u32_slice_info.Slice.child == u32);75 assert(u32_slice_info.Pointer.alignment == 4);
76 assert(u32_slice_info.Pointer.child == u32);
60}77}
6178
62test "type info: array type info" {79test "type info: array type info" {
...@@ -71,15 +88,15 @@ fn testArray() void {...@@ -71,15 +88,15 @@ fn testArray() void {
71 assert(arr_info.Array.child == bool);88 assert(arr_info.Array.child == bool);
72}89}
7390
74test "type info: nullable type info" {91test "type info: optional type info" {
75 testNullable();92 testOptional();
76 comptime testNullable();93 comptime testOptional();
77}94}
7895
79fn testNullable() void {96fn testOptional() void {
80 const null_info = @typeInfo(?void);97 const null_info = @typeInfo(?void);
81 assert(TypeId(null_info) == TypeId.Nullable);98 assert(TypeId(null_info) == TypeId.Optional);
82 assert(null_info.Nullable.child == void);99 assert(null_info.Optional.child == void);
83}100}
84101
85test "type info: promise info" {102test "type info: promise info" {
...@@ -149,11 +166,11 @@ fn testUnion() void {...@@ -149,11 +166,11 @@ fn testUnion() void {
149 assert(TypeId(typeinfo_info) == TypeId.Union);166 assert(TypeId(typeinfo_info) == TypeId.Union);
150 assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto);167 assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto);
151 assert(typeinfo_info.Union.tag_type == TypeId);168 assert(typeinfo_info.Union.tag_type == TypeId);
152 assert(typeinfo_info.Union.fields.len == 26);169 assert(typeinfo_info.Union.fields.len == 25);
153 assert(typeinfo_info.Union.fields[4].enum_field != null);170 assert(typeinfo_info.Union.fields[4].enum_field != null);
154 assert((??typeinfo_info.Union.fields[4].enum_field).value == 4);171 assert(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
155 assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int));172 assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int));
156 assert(typeinfo_info.Union.defs.len == 21);173 assert(typeinfo_info.Union.defs.len == 20);
157174
158 const TestNoTagUnion = union {175 const TestNoTagUnion = union {
159 Foo: void,176 Foo: void,
test/cases/while.zig+6-6
...@@ -81,7 +81,7 @@ test "while with else" {...@@ -81,7 +81,7 @@ test "while with else" {
81 assert(got_else == 1);81 assert(got_else == 1);
82}82}
8383
84test "while with nullable as condition" {84test "while with optional as condition" {
85 numbers_left = 10;85 numbers_left = 10;
86 var sum: i32 = 0;86 var sum: i32 = 0;
87 while (getNumberOrNull()) |value| {87 while (getNumberOrNull()) |value| {
...@@ -90,7 +90,7 @@ test "while with nullable as condition" {...@@ -90,7 +90,7 @@ test "while with nullable as condition" {
90 assert(sum == 45);90 assert(sum == 45);
91}91}
9292
93test "while with nullable as condition with else" {93test "while with optional as condition with else" {
94 numbers_left = 10;94 numbers_left = 10;
95 var sum: i32 = 0;95 var sum: i32 = 0;
96 var got_else: i32 = 0;96 var got_else: i32 = 0;
...@@ -132,7 +132,7 @@ fn getNumberOrNull() ?i32 {...@@ -132,7 +132,7 @@ fn getNumberOrNull() ?i32 {
132 };132 };
133}133}
134134
135test "while on nullable with else result follow else prong" {135test "while on optional with else result follow else prong" {
136 const result = while (returnNull()) |value| {136 const result = while (returnNull()) |value| {
137 break value;137 break value;
138 } else138 } else
...@@ -140,8 +140,8 @@ test "while on nullable with else result follow else prong" {...@@ -140,8 +140,8 @@ test "while on nullable with else result follow else prong" {
140 assert(result == 2);140 assert(result == 2);
141}141}
142142
143test "while on nullable with else result follow break prong" {143test "while on optional with else result follow break prong" {
144 const result = while (returnMaybe(10)) |value| {144 const result = while (returnOptional(10)) |value| {
145 break value;145 break value;
146 } else146 } else
147 i32(2);147 i32(2);
...@@ -210,7 +210,7 @@ fn testContinueOuter() void {...@@ -210,7 +210,7 @@ fn testContinueOuter() void {
210fn returnNull() ?i32 {210fn returnNull() ?i32 {
211 return null;211 return null;
212}212}
213fn returnMaybe(x: i32) ?i32 {213fn returnOptional(x: i32) ?i32 {
214 return x;214 return x;
215}215}
216fn returnError() error!i32 {216fn returnError() error!i32 {
test/compare_output.zig+2-2
...@@ -284,7 +284,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -284,7 +284,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
284 cases.addC("expose function pointer to C land",284 cases.addC("expose function pointer to C land",
285 \\const c = @cImport(@cInclude("stdlib.h"));285 \\const c = @cImport(@cInclude("stdlib.h"));
286 \\286 \\
287 \\export fn compare_fn(a: ?[*]const c_void, b: ?[*]const c_void) c_int {287 \\export fn compare_fn(a: ?*const c_void, b: ?*const c_void) c_int {
288 \\ const a_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), a));288 \\ const a_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), a));
289 \\ const b_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), b));289 \\ const b_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), b));
290 \\ if (a_int.* < b_int.*) {290 \\ if (a_int.* < b_int.*) {
...@@ -299,7 +299,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -299,7 +299,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
299 \\export fn main() c_int {299 \\export fn main() c_int {
300 \\ var array = []u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 };300 \\ var array = []u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 };
301 \\301 \\
302 \\ c.qsort(@ptrCast(?[*]c_void, array[0..].ptr), c_ulong(array.len), @sizeOf(i32), compare_fn);302 \\ c.qsort(@ptrCast(?*c_void, array[0..].ptr), c_ulong(array.len), @sizeOf(i32), compare_fn);
303 \\303 \\
304 \\ for (array) |item, i| {304 \\ for (array) |item, i| {
305 \\ if (item != i) {305 \\ if (item != i) {
test/compile_errors.zig+61-10
...@@ -1,6 +1,57 @@...@@ -1,6 +1,57 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompileErrorContext) void {3pub fn addCases(cases: *tests.CompileErrorContext) void {
4 cases.add(
5 "use implicit casts to assign null to non-nullable pointer",
6 \\export fn entry() void {
7 \\ var x: i32 = 1234;
8 \\ var p: *i32 = &x;
9 \\ var pp: *?*i32 = &p;
10 \\ pp.* = null;
11 \\ var y = p.*;
12 \\}
13 ,
14 ".tmp_source.zig:4:23: error: expected type '*?*i32', found '**i32'",
15 );
16
17 cases.add(
18 "attempted implicit cast from T to [*]const T",
19 \\export fn entry() void {
20 \\ const x: [*]const bool = true;
21 \\}
22 ,
23 ".tmp_source.zig:2:30: error: expected type '[*]const bool', found 'bool'",
24 );
25
26 cases.add(
27 "dereference unknown length pointer",
28 \\export fn entry(x: [*]i32) i32 {
29 \\ return x.*;
30 \\}
31 ,
32 ".tmp_source.zig:2:13: error: index syntax required for unknown-length pointer type '[*]i32'",
33 );
34
35 cases.add(
36 "field access of unknown length pointer",
37 \\const Foo = extern struct {
38 \\ a: i32,
39 \\};
40 \\
41 \\export fn entry(foo: [*]Foo) void {
42 \\ foo.a += 1;
43 \\}
44 ,
45 ".tmp_source.zig:6:8: error: type '[*]Foo' does not support field access",
46 );
47
48 cases.add(
49 "unknown length pointer to opaque",
50 \\export const T = [*]@OpaqueType();
51 ,
52 ".tmp_source.zig:1:18: error: unknown-length pointer to opaque",
53 );
54
4 cases.add(55 cases.add(
5 "error when evaluating return type",56 "error when evaluating return type",
6 \\const Foo = struct {57 \\const Foo = struct {
...@@ -1303,7 +1354,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1303,7 +1354,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1303 \\ if (true) |x| { }1354 \\ if (true) |x| { }
1304 \\}1355 \\}
1305 ,1356 ,
1306 ".tmp_source.zig:2:9: error: expected nullable type, found 'bool'",1357 ".tmp_source.zig:2:9: error: expected optional type, found 'bool'",
1307 );1358 );
13081359
1309 cases.add(1360 cases.add(
...@@ -1742,7 +1793,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -1742,7 +1793,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
1742 );1793 );
17431794
1744 cases.add(1795 cases.add(
1745 "assign null to non-nullable pointer",1796 "assign null to non-optional pointer",
1746 \\const a: *u8 = null;1797 \\const a: *u8 = null;
1747 \\1798 \\
1748 \\export fn entry() usize { return @sizeOf(@typeOf(a)); }1799 \\export fn entry() usize { return @sizeOf(@typeOf(a)); }
...@@ -2258,7 +2309,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -2258,7 +2309,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2258 \\2309 \\
2259 \\ defer try canFail();2310 \\ defer try canFail();
2260 \\2311 \\
2261 \\ const a = maybeInt() ?? return;2312 \\ const a = maybeInt() orelse return;
2262 \\}2313 \\}
2263 \\2314 \\
2264 \\fn canFail() error!void { }2315 \\fn canFail() error!void { }
...@@ -2779,7 +2830,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -2779,7 +2830,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2779 );2830 );
27802831
2781 cases.add(2832 cases.add(
2782 "while expected bool, got nullable",2833 "while expected bool, got optional",
2783 \\export fn foo() void {2834 \\export fn foo() void {
2784 \\ while (bar()) {}2835 \\ while (bar()) {}
2785 \\}2836 \\}
...@@ -2799,23 +2850,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -2799,23 +2850,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2799 );2850 );
28002851
2801 cases.add(2852 cases.add(
2802 "while expected nullable, got bool",2853 "while expected optional, got bool",
2803 \\export fn foo() void {2854 \\export fn foo() void {
2804 \\ while (bar()) |x| {}2855 \\ while (bar()) |x| {}
2805 \\}2856 \\}
2806 \\fn bar() bool { return true; }2857 \\fn bar() bool { return true; }
2807 ,2858 ,
2808 ".tmp_source.zig:2:15: error: expected nullable type, found 'bool'",2859 ".tmp_source.zig:2:15: error: expected optional type, found 'bool'",
2809 );2860 );
28102861
2811 cases.add(2862 cases.add(
2812 "while expected nullable, got error union",2863 "while expected optional, got error union",
2813 \\export fn foo() void {2864 \\export fn foo() void {
2814 \\ while (bar()) |x| {}2865 \\ while (bar()) |x| {}
2815 \\}2866 \\}
2816 \\fn bar() error!i32 { return 1; }2867 \\fn bar() error!i32 { return 1; }
2817 ,2868 ,
2818 ".tmp_source.zig:2:15: error: expected nullable type, found 'error!i32'",2869 ".tmp_source.zig:2:15: error: expected optional type, found 'error!i32'",
2819 );2870 );
28202871
2821 cases.add(2872 cases.add(
...@@ -2829,7 +2880,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -2829,7 +2880,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2829 );2880 );
28302881
2831 cases.add(2882 cases.add(
2832 "while expected error union, got nullable",2883 "while expected error union, got optional",
2833 \\export fn foo() void {2884 \\export fn foo() void {
2834 \\ while (bar()) |x| {} else |err| {}2885 \\ while (bar()) |x| {} else |err| {}
2835 \\}2886 \\}
...@@ -3291,7 +3342,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -3291,7 +3342,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
3291 ".tmp_source.zig:9:4: error: variable of type 'comptime_float' must be const or comptime",3342 ".tmp_source.zig:9:4: error: variable of type 'comptime_float' must be const or comptime",
3292 ".tmp_source.zig:10:4: error: variable of type '(block)' must be const or comptime",3343 ".tmp_source.zig:10:4: error: variable of type '(block)' must be const or comptime",
3293 ".tmp_source.zig:11:4: error: variable of type '(null)' must be const or comptime",3344 ".tmp_source.zig:11:4: error: variable of type '(null)' must be const or comptime",
3294 ".tmp_source.zig:12:4: error: variable of type 'Opaque' must be const or comptime",3345 ".tmp_source.zig:12:4: error: variable of type 'Opaque' not allowed",
3295 ".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime",3346 ".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime",
3296 ".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime",3347 ".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime",
3297 ".tmp_source.zig:15:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime",3348 ".tmp_source.zig:15:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime",
test/tests.zig+6-6
...@@ -282,8 +282,8 @@ pub const CompareOutputContext = struct {...@@ -282,8 +282,8 @@ pub const CompareOutputContext = struct {
282 var stdout = Buffer.initNull(b.allocator);282 var stdout = Buffer.initNull(b.allocator);
283 var stderr = Buffer.initNull(b.allocator);283 var stderr = Buffer.initNull(b.allocator);
284284
285 var stdout_file_in_stream = io.FileInStream.init(&??child.stdout);285 var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
286 var stderr_file_in_stream = io.FileInStream.init(&??child.stderr);286 var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
287287
288 stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable;288 stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable;
289 stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable;289 stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable;
...@@ -601,8 +601,8 @@ pub const CompileErrorContext = struct {...@@ -601,8 +601,8 @@ pub const CompileErrorContext = struct {
601 var stdout_buf = Buffer.initNull(b.allocator);601 var stdout_buf = Buffer.initNull(b.allocator);
602 var stderr_buf = Buffer.initNull(b.allocator);602 var stderr_buf = Buffer.initNull(b.allocator);
603603
604 var stdout_file_in_stream = io.FileInStream.init(&??child.stdout);604 var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
605 var stderr_file_in_stream = io.FileInStream.init(&??child.stderr);605 var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
606606
607 stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;607 stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
608 stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;608 stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;
...@@ -872,8 +872,8 @@ pub const TranslateCContext = struct {...@@ -872,8 +872,8 @@ pub const TranslateCContext = struct {
872 var stdout_buf = Buffer.initNull(b.allocator);872 var stdout_buf = Buffer.initNull(b.allocator);
873 var stderr_buf = Buffer.initNull(b.allocator);873 var stderr_buf = Buffer.initNull(b.allocator);
874874
875 var stdout_file_in_stream = io.FileInStream.init(&??child.stdout);875 var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
876 var stderr_file_in_stream = io.FileInStream.init(&??child.stderr);876 var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
877877
878 stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;878 stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
879 stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;879 stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;
test/translate_c.zig+20-20
...@@ -99,7 +99,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -99,7 +99,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
99 cases.add("restrict -> noalias",99 cases.add("restrict -> noalias",
100 \\void foo(void *restrict bar, void *restrict);100 \\void foo(void *restrict bar, void *restrict);
101 ,101 ,
102 \\pub extern fn foo(noalias bar: ?[*]c_void, noalias arg1: ?[*]c_void) void;102 \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void;
103 );103 );
104104
105 cases.add("simple struct",105 cases.add("simple struct",
...@@ -172,7 +172,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -172,7 +172,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
172 ,172 ,
173 \\pub const struct_Foo = @OpaqueType();173 \\pub const struct_Foo = @OpaqueType();
174 ,174 ,
175 \\pub extern fn some_func(foo: ?[*]struct_Foo, x: c_int) ?[*]struct_Foo;175 \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo;
176 ,176 ,
177 \\pub const Foo = struct_Foo;177 \\pub const Foo = struct_Foo;
178 );178 );
...@@ -233,7 +233,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -233,7 +233,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
233 ,233 ,
234 \\pub const Foo = c_void;234 \\pub const Foo = c_void;
235 ,235 ,
236 \\pub extern fn fun(a: ?[*]Foo) Foo;236 \\pub extern fn fun(a: ?*Foo) Foo;
237 );237 );
238238
239 cases.add("generate inline func for #define global extern fn",239 cases.add("generate inline func for #define global extern fn",
...@@ -246,13 +246,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -246,13 +246,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
246 \\pub extern var fn_ptr: ?extern fn() void;246 \\pub extern var fn_ptr: ?extern fn() void;
247 ,247 ,
248 \\pub inline fn foo() void {248 \\pub inline fn foo() void {
249 \\ return (??fn_ptr)();249 \\ return fn_ptr.?();
250 \\}250 \\}
251 ,251 ,
252 \\pub extern var fn_ptr2: ?extern fn(c_int, f32) u8;252 \\pub extern var fn_ptr2: ?extern fn(c_int, f32) u8;
253 ,253 ,
254 \\pub inline fn bar(arg0: c_int, arg1: f32) u8 {254 \\pub inline fn bar(arg0: c_int, arg1: f32) u8 {
255 \\ return (??fn_ptr2)(arg0, arg1);255 \\ return fn_ptr2.?(arg0, arg1);
256 \\}256 \\}
257 );257 );
258258
...@@ -505,7 +505,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -505,7 +505,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
505 \\ return 6;505 \\ return 6;
506 \\}506 \\}
507 ,507 ,
508 \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int {508 \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
509 \\ if ((a != 0) and (b != 0)) return 0;509 \\ if ((a != 0) and (b != 0)) return 0;
510 \\ if ((b != 0) and (c != null)) return 1;510 \\ if ((b != 0) and (c != null)) return 1;
511 \\ if ((a != 0) and (c != null)) return 2;511 \\ if ((a != 0) and (c != null)) return 2;
...@@ -608,7 +608,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -608,7 +608,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
608 \\ field: c_int,608 \\ field: c_int,
609 \\};609 \\};
610 \\pub export fn read_field(foo: ?[*]struct_Foo) c_int {610 \\pub export fn read_field(foo: ?[*]struct_Foo) c_int {
611 \\ return (??foo).field;611 \\ return foo.?.field;
612 \\}612 \\}
613 );613 );
614614
...@@ -653,8 +653,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -653,8 +653,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
653 \\ return x;653 \\ return x;
654 \\}654 \\}
655 ,655 ,
656 \\pub export fn foo(x: ?[*]c_ushort) ?[*]c_void {656 \\pub export fn foo(x: ?[*]c_ushort) ?*c_void {
657 \\ return @ptrCast(?[*]c_void, x);657 \\ return @ptrCast(?*c_void, x);
658 \\}658 \\}
659 );659 );
660660
...@@ -969,11 +969,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -969,11 +969,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
969 \\pub export fn bar() void {969 \\pub export fn bar() void {
970 \\ var f: ?extern fn() void = foo;970 \\ var f: ?extern fn() void = foo;
971 \\ var b: ?extern fn() c_int = baz;971 \\ var b: ?extern fn() c_int = baz;
972 \\ (??f)();972 \\ f.?();
973 \\ (??f)();973 \\ f.?();
974 \\ foo();974 \\ foo();
975 \\ _ = (??b)();975 \\ _ = b.?();
976 \\ _ = (??b)();976 \\ _ = b.?();
977 \\ _ = baz();977 \\ _ = baz();
978 \\}978 \\}
979 );979 );
...@@ -984,7 +984,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -984,7 +984,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
984 \\}984 \\}
985 ,985 ,
986 \\pub export fn foo(x: ?[*]c_int) void {986 \\pub export fn foo(x: ?[*]c_int) void {
987 \\ (??x).* = 1;987 \\ x.?.* = 1;
988 \\}988 \\}
989 );989 );
990990
...@@ -1012,7 +1012,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1012,7 +1012,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1012 \\pub fn foo() c_int {1012 \\pub fn foo() c_int {
1013 \\ var x: c_int = 1234;1013 \\ var x: c_int = 1234;
1014 \\ var ptr: ?[*]c_int = &x;1014 \\ var ptr: ?[*]c_int = &x;
1015 \\ return (??ptr).*;1015 \\ return ptr.?.*;
1016 \\}1016 \\}
1017 );1017 );
10181018
...@@ -1119,7 +1119,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1119,7 +1119,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1119 \\pub const glClearPFN = PFNGLCLEARPROC;1119 \\pub const glClearPFN = PFNGLCLEARPROC;
1120 ,1120 ,
1121 \\pub inline fn glClearUnion(arg0: GLbitfield) void {1121 \\pub inline fn glClearUnion(arg0: GLbitfield) void {
1122 \\ return (??glProcs.gl.Clear)(arg0);1122 \\ return glProcs.gl.Clear.?(arg0);
1123 \\}1123 \\}
1124 ,1124 ,
1125 \\pub const OpenGLProcs = union_OpenGLProcs;1125 \\pub const OpenGLProcs = union_OpenGLProcs;
...@@ -1173,7 +1173,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1173,7 +1173,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1173 \\ return !c;1173 \\ return !c;
1174 \\}1174 \\}
1175 ,1175 ,
1176 \\pub fn foo(a: c_int, b: f32, c: ?[*]c_void) c_int {1176 \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int {
1177 \\ return !(a == 0);1177 \\ return !(a == 0);
1178 \\ return !(a != 0);1178 \\ return !(a != 0);
1179 \\ return !(b != 0);1179 \\ return !(b != 0);
...@@ -1231,7 +1231,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1231,7 +1231,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1231 \\ B,1231 \\ B,
1232 \\ C,1232 \\ C,
1233 \\};1233 \\};
1234 \\pub fn if_none_bool(a: c_int, b: f32, c: ?[*]c_void, d: enum_SomeEnum) c_int {1234 \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int {
1235 \\ if (a != 0) return 0;1235 \\ if (a != 0) return 0;
1236 \\ if (b != 0) return 1;1236 \\ if (b != 0) return 1;
1237 \\ if (c != null) return 2;1237 \\ if (c != null) return 2;
...@@ -1248,7 +1248,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1248,7 +1248,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1248 \\ return 3;1248 \\ return 3;
1249 \\}1249 \\}
1250 ,1250 ,
1251 \\pub fn while_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int {1251 \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
1252 \\ while (a != 0) return 0;1252 \\ while (a != 0) return 0;
1253 \\ while (b != 0) return 1;1253 \\ while (b != 0) return 1;
1254 \\ while (c != null) return 2;1254 \\ while (c != null) return 2;
...@@ -1264,7 +1264,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1264,7 +1264,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1264 \\ return 3;1264 \\ return 3;
1265 \\}1265 \\}
1266 ,1266 ,
1267 \\pub fn for_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int {1267 \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
1268 \\ while (a != 0) return 0;1268 \\ while (a != 0) return 0;
1269 \\ while (b != 0) return 1;1269 \\ while (b != 0) return 1;
1270 \\ while (c != null) return 2;1270 \\ while (c != null) return 2;