| author | |
| committer | |
| log | 32dd98b19fe3cc384df32704dac0ff3e377dbe0c |
| tree | 2eddf3618d80313bdd24c25bd589bd474f3d82fc |
| parent | ef7f69d14a017c6c2065e4a376bb8e1f05ace04b |
| parent | f0697c28f80d64c544302aea576e41ebc443b41c |
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 | |
| 70 | 70 | ||
| 71 | ## Community | 71 | ## Community |
| 72 | 72 |
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++.a | 81 | \\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 { |
| 101 | 102 | ||
| 102 | b.default_step.dependOn(&exe.step); | 103 | b.default_step.dependOn(&exe.step); |
| 103 | 104 | ||
| 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); |
| 110 | 111 | ||
| 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); |
| 114 | 115 | ||
| 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; |
| 117 | 118 | ||
| 118 | test_step.dependOn(docs_step); | 119 | test_step.dependOn(docs_step); |
| 119 | 120 |
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 |
| 6 | f64, the handle is "by value", meaning that we pass around the value itself when | 6 | f64, the handle is "by value", meaning that we pass around the value itself when |
| 7 | we refer to a value of that type. | 7 | we refer to a value of that type. |
| 8 | 8 | ||
| 9 | If a type is a container, error union, maybe type, slice, or array, then its | 9 | If a type is a container, error union, optional type, slice, or array, then its |
| 10 | handle is a pointer, and everywhere we refer to a value of this type we refer to | 10 | handle is a pointer, and everywhere we refer to a value of this type we refer to |
| 11 | a pointer. | 11 | a pointer. |
| 12 | 12 | ||
| ... | @@ -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 | } |
| 21 | 21 | ||
| 22 | Maybe types are represented as: | 22 | Optional types are represented as: |
| 23 | 23 | ||
| 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: |
| 28 | 28 | ||
| 29 | ## Data Optimizations | 29 | ## Data Optimizations |
| 30 | 30 | ||
| 31 | Maybe pointer types are special: the 0x0 pointer value is used to represent a | 31 | Optional pointer types are special: the 0x0 pointer value is used to represent a |
| 32 | null pointer. Thus, instead of the struct above, maybe pointer types are | 32 | null pointer. Thus, instead of the struct above, optional pointer types are |
| 33 | represented as a `usize` in codegen and the handle is by value. | 33 | represented 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 { |
| 25 | 25 | ||
| 26 | if (!args_it.skip()) @panic("expected self arg"); | 26 | if (!args_it.skip()) @panic("expected self arg"); |
| 27 | 27 | ||
| 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); |
| 30 | 30 | ||
| 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); |
| 33 | 33 | ||
| 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); |
| 36 | 36 | ||
| 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); |
| 52 | 52 | ||
| 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 #709 | 55 | |
| 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 { |
| 300 | const Node = union(enum) { | 294 | const 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); |
| 357 | 352 | ||
| 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 { |
| 690 | 688 | ||
| 691 | fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void { | 689 | fn 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(); |
| 956 | 960 | ||
| 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 | }); |
| 966 | 975 | ||
| 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 | |||
| 1073 | fn 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 | // nullable | 159 | // 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); |
| 162 | 162 | ||
| 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); |
| 165 | 165 | ||
| 166 | nullable_value = "hi"; | 166 | optional_value = "hi"; |
| 167 | assert(nullable_value != null); | 167 | assert(optional_value != null); |
| 168 | 168 | ||
| 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); |
| 171 | 171 | ||
| 172 | // error union | 172 | // 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#} |
| 595 | const assert = @import("std").debug.assert; | 596 | const 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; |
| 1001 | const unwrapped = value ?? 1234; | 1003 | const unwrapped = value orelse 1234; |
| 1002 | unwrapped == 1234</code></pre> | 1004 | unwrapped == 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> | 1020 | value.? == 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; |
| 1261 | x.* == 1234</code></pre> | 1263 | x.* == 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}; | ||
| 1278 | const 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.y | 1286 | <pre><code>x() x[] x.y |
| 1269 | a!b | 1287 | a!b |
| 1270 | !x -x -%x ~x &amp;x ?x ??x | 1288 | !x -x -%x ~x &amp;x ?x |
| 1271 | x{} x.* | 1289 | x{} 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;= |
| 1279 | and | 1297 | and |
| 1280 | or | 1298 | or |
| 1281 | ?? catch | 1299 | orelse 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 | } |
| 1485 | 1503 | ||
| 1486 | test "nullable pointers" { | 1504 | test "optional pointers" { |
| 1487 | // Pointers cannot be null. If you want a null pointer, use the nullable | 1505 | // 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; |
| 1490 | 1508 | ||
| 1491 | var x: i32 = 1; | 1509 | var x: i32 = 1; |
| 1492 | ptr = &x; | 1510 | ptr = &x; |
| 1493 | 1511 | ||
| 1494 | assert((??ptr).* == 1); | 1512 | assert(ptr.?.* == 1); |
| 1495 | 1513 | ||
| 1496 | // Nullable pointers are the same size as normal pointers, because pointer | 1514 | // 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 | } |
| 2271 | 2289 | ||
| 2272 | test "while null capture" { | 2290 | test "while null capture" { |
| 2273 | // Just like if expressions, while loops can take a nullable as the | 2291 | // Just like if expressions, while loops can take an optional as the |
| 2274 | // condition and capture the payload. When null is encountered the loop | 2292 | // 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); |
| 2282 | 2300 | ||
| 2283 | // The else branch is allowed on nullable iteration. In this case, it will | 2301 | // 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 | } |
| 2463 | 2481 | ||
| 2464 | test "if nullable" { | 2482 | test "if optional" { |
| 2465 | // If expressions test for null. | 2483 | // If expressions test for null. |
| 2466 | 2484 | ||
| 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#} |
| 2988 | fn doAThing(str: []u8) void { | 3007 | fn 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 must | 3014 | 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 the | 3019 | <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 in | 3038 | 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 combine | 3055 | 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#} |
| 3037 | fn doAThing(str: []u8) void { | 3059 | fn 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 if | 3075 | 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); |
| 3064 | 3087 | ||
| 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 up | 3089 | // tmp_buf is truly a temporary resource, and we for sure want to clean it up |
| 3067 | // before this block leaves scope | 3090 | // 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 path | 3102 | 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#} | ||
| 3158 | const A = error{ | ||
| 3159 | NotDir, | ||
| 3160 | |||
| 3161 | /// A doc comment | ||
| 3162 | PathNotFound, | ||
| 3163 | }; | ||
| 3164 | const B = error{ | ||
| 3165 | OutOfMemory, | ||
| 3166 | |||
| 3167 | /// B doc comment | ||
| 3168 | PathNotFound, | ||
| 3169 | }; | ||
| 3170 | |||
| 3171 | const C = A || B; | ||
| 3172 | |||
| 3173 | fn foo() C!void { | ||
| 3174 | return error.NotDir; | ||
| 3175 | } | ||
| 3176 | |||
| 3177 | test "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#} | ||
| 3238 | pub fn main() !void { | ||
| 3239 | try foo(12); | ||
| 3240 | } | ||
| 3241 | |||
| 3242 | fn foo(x: i32) !void { | ||
| 3243 | if (x >= 5) { | ||
| 3244 | try bar(); | ||
| 3245 | } else { | ||
| 3246 | try bang2(); | ||
| 3247 | } | ||
| 3248 | } | ||
| 3249 | |||
| 3250 | fn 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 | |||
| 3259 | fn baz() !void { | ||
| 3260 | try bang1(); | ||
| 3261 | } | ||
| 3262 | |||
| 3263 | fn quux() !void { | ||
| 3264 | try bang2(); | ||
| 3265 | } | ||
| 3266 | |||
| 3267 | fn hello() !void { | ||
| 3268 | try bang2(); | ||
| 3269 | } | ||
| 3270 | |||
| 3271 | fn another() !void { | ||
| 3272 | try bang1(); | ||
| 3273 | } | ||
| 3274 | |||
| 3275 | fn bang1() !void { | ||
| 3276 | return error.FileNotFound; | ||
| 3277 | } | ||
| 3278 | |||
| 3279 | fn 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#} | ||
| 3292 | pub fn main() void { | ||
| 3293 | foo(12); | ||
| 3294 | } | ||
| 3295 | |||
| 3296 | fn foo(x: i32) void { | ||
| 3297 | if (x >= 5) { | ||
| 3298 | bar(); | ||
| 3299 | } else { | ||
| 3300 | bang2(); | ||
| 3301 | } | ||
| 3302 | } | ||
| 3303 | |||
| 3304 | fn bar() void { | ||
| 3305 | if (baz()) { | ||
| 3306 | quux(); | ||
| 3307 | } else { | ||
| 3308 | hello(); | ||
| 3309 | } | ||
| 3310 | } | ||
| 3311 | |||
| 3312 | fn baz() bool { | ||
| 3313 | return bang1(); | ||
| 3314 | } | ||
| 3315 | |||
| 3316 | fn quux() void { | ||
| 3317 | bang2(); | ||
| 3318 | } | ||
| 3319 | |||
| 3320 | fn hello() void { | ||
| 3321 | bang2(); | ||
| 3322 | } | ||
| 3323 | |||
| 3324 | fn bang1() bool { | ||
| 3325 | return false; | ||
| 3326 | } | ||
| 3327 | |||
| 3328 | fn 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#} | ||
| 3369 | pub 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 | ||
| 3388 | fn __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 or | 3406 | 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 nullable | 3410 | 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 integer | 3414 | // normal integer |
| 3181 | const normal_int: i32 = 1234; | 3415 | const normal_int: i32 = 1234; |
| 3182 | 3416 | ||
| 3183 | // nullable integer | 3417 | // optional integer |
| 3184 | const nullable_int: ?i32 = 5678; | 3418 | const 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 runtime | 3424 | 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 compiler | 3431 | 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) { |
| 3219 | extern fn malloc(size: size_t) ?*u8; | 3453 | extern fn malloc(size: size_t) ?*u8; |
| 3220 | 3454 | ||
| 3221 | fn doAThing() ?*Foo { | 3455 | fn 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> operator | 3462 | 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 everywhere | 3463 | 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#} |
| 3248 | fn doAThing(nullable_foo: ?*Foo) void { | 3482 | fn doAThing(optional_foo: ?*Foo) void { |
| 3249 | // do some stuff | 3483 | // do some stuff |
| 3250 | 3484 | ||
| 3251 | if (nullable_foo) |foo| { | 3485 | if (optional_foo) |foo| { |
| 3252 | doSomethingWithFoo(foo); | 3486 | doSomethingWithFoo(foo); |
| 3253 | } | 3487 | } |
| 3254 | 3488 | ||
| ... | @@ -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, which | 3494 | <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 arguments | 3501 | 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-time | 3505 | <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#} |
| 3274 | const assert = @import("std").debug.assert; | 3508 | const assert = @import("std").debug.assert; |
| 3275 | 3509 | ||
| 3276 | test "nullable type" { | 3510 | test "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; |
| 3279 | 3513 | ||
| 3280 | // Implicitly cast from child type of a nullable | 3514 | // Implicitly cast from child type of an optional |
| 3281 | foo = 1234; | 3515 | foo = 1234; |
| 3282 | 3516 | ||
| 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#} | ||
| 3528 | const 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#} | ||
| 4206 | const std = @import("std"); | ||
| 4207 | const assert = std.debug.assert; | ||
| 4208 | |||
| 4209 | var x: i32 = 1; | ||
| 4210 | |||
| 4211 | test "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 | } | ||
| 4217 | async<*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#} | ||
| 4229 | const std = @import("std"); | ||
| 4230 | const assert = std.debug.assert; | ||
| 4231 | |||
| 4232 | test "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 | } | ||
| 4243 | async fn testAsyncSeq() void { | ||
| 4244 | defer seq('e'); | ||
| 4245 | |||
| 4246 | seq('b'); | ||
| 4247 | suspend; | ||
| 4248 | seq('d'); | ||
| 4249 | } | ||
| 4250 | var points = []u8{0} ** "abcdefg".len; | ||
| 4251 | var index: usize = 0; | ||
| 4252 | |||
| 4253 | fn 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#} | ||
| 4265 | const std = @import("std"); | ||
| 4266 | const assert = std.debug.assert; | ||
| 4267 | |||
| 4268 | test "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 | |||
| 4276 | var a_promise: promise = undefined; | ||
| 4277 | var result = false; | ||
| 4278 | async 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#} | ||
| 4310 | const std = @import("std"); | ||
| 4311 | const assert = std.debug.assert; | ||
| 4312 | |||
| 4313 | test "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 | } | ||
| 4321 | async 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#} | ||
| 4356 | const std = @import("std"); | ||
| 4357 | const assert = std.debug.assert; | ||
| 4358 | |||
| 4359 | var a_promise: promise = undefined; | ||
| 4360 | var final_result: i32 = 0; | ||
| 4361 | |||
| 4362 | test "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 | } | ||
| 4371 | async 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 | } | ||
| 4378 | async 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 | |||
| 4388 | var seq_points = []u8{0} ** "abcdefghi".len; | ||
| 4389 | var seq_index: usize = 0; | ||
| 4390 | |||
| 4391 | fn 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 | }; |
| 4986 | 5491 | ||
| 4987 | pub const Nullable = struct { | 5492 | pub const Optional = struct { |
| 4988 | child: type, | 5493 | child: type, |
| 4989 | }; | 5494 | }; |
| 4990 | 5495 | ||
| ... | @@ -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 on | 5636 | <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 that | 5680 | 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 basis | 5682 | 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#} |
| 5377 | comptime { | 5896 | comptime { |
| 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#} |
| 5386 | const warn = @import("std").debug.warn; | 5905 | const warn = @import("std").debug.warn; |
| 5387 | pub fn main() void { | 5906 | pub fn main() void { |
| 5388 | const nullable_number: ?i32 = null; | 5907 | const optional_number: ?i32 = null; |
| 5389 | 5908 | ||
| 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#} |
| 5478 | pub const StackTrace = struct { | ||
| 5479 | index: usize, | ||
| 5480 | instruction_addresses: []usize, | ||
| 5481 | }; | ||
| 5482 | |||
| 5483 | pub 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 | |||
| 5518 | pub 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 | |||
| 5611 | pub 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 | |||
| 5635 | pub const ObjectFormat = enum { | ||
| 5636 | unknown, | ||
| 5637 | coff, | ||
| 5638 | elf, | ||
| 5639 | macho, | ||
| 5640 | wasm, | ||
| 5641 | }; | ||
| 5642 | |||
| 5643 | pub const GlobalLinkage = enum { | ||
| 5644 | Internal, | ||
| 5645 | Strong, | ||
| 5646 | Weak, | ||
| 5647 | LinkOnce, | ||
| 5648 | }; | ||
| 5649 | |||
| 5650 | pub const AtomicOrder = enum { | ||
| 5651 | Unordered, | ||
| 5652 | Monotonic, | ||
| 5653 | Acquire, | ||
| 5654 | Release, | ||
| 5655 | AcqRel, | ||
| 5656 | SeqCst, | ||
| 5657 | }; | ||
| 5658 | |||
| 5659 | pub const AtomicRmwOp = enum { | ||
| 5660 | Xchg, | ||
| 5661 | Add, | ||
| 5662 | Sub, | ||
| 5663 | And, | ||
| 5664 | Nand, | ||
| 5665 | Or, | ||
| 5666 | Xor, | ||
| 5667 | Max, | ||
| 5668 | Min, | ||
| 5669 | }; | ||
| 5670 | |||
| 5671 | pub const Mode = enum { | ||
| 5672 | Debug, | ||
| 5673 | ReleaseSafe, | ||
| 5674 | ReleaseFast, | ||
| 5675 | ReleaseSmall, | ||
| 5676 | }; | ||
| 5677 | |||
| 5678 | pub 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 | |||
| 5706 | pub 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 | |||
| 5875 | pub const FloatMode = enum { | ||
| 5876 | Optimized, | ||
| 5877 | Strict, | ||
| 5878 | }; | ||
| 5879 | |||
| 5880 | pub const Endian = enum { | ||
| 5881 | Big, | ||
| 5882 | Little, | ||
| 5883 | }; | ||
| 5884 | |||
| 5885 | pub const endian = Endian.Little; | ||
| 5886 | pub const is_test = true; | ||
| 5887 | pub const os = Os.linux; | ||
| 5888 | pub const arch = Arch.x86_64; | ||
| 5889 | pub const environ = Environ.gnu; | ||
| 5890 | pub const object_format = ObjectFormat.elf; | ||
| 5891 | pub const mode = Mode.Debug; | ||
| 5892 | pub const link_libc = false; | ||
| 5893 | pub const have_error_return_tracing = true; | ||
| 5894 | pub 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 build | 6158 | <pre><code class="shell">$ zig build |
| 6059 | $ ./test | 6159 | $ ./test |
| 6060 | all your base are belong to us</code></pre> | 6160 | all your base are belong to us</code></pre> |
| ... | @@ -6367,9 +6467,9 @@ AsmInputItem = "[" Symbol "]" String "(" Expression ")" | ... | @@ -6367,9 +6467,9 @@ AsmInputItem = "[" Symbol "]" String "(" Expression ")" |
| 6367 | 6467 | ||
| 6368 | AsmClobbers= ":" list(String, ",") | 6468 | AsmClobbers= ":" list(String, ",") |
| 6369 | 6469 | ||
| 6370 | UnwrapExpression = BoolOrExpression (UnwrapNullable | UnwrapError) | BoolOrExpression | 6470 | UnwrapExpression = BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression |
| 6371 | 6471 | ||
| 6372 | UnwrapNullable = "??" Expression | 6472 | UnwrapOptional = "orelse" Expression |
| 6373 | 6473 | ||
| 6374 | UnwrapError = "catch" option("|" Symbol "|") Expression | 6474 | UnwrapError = "catch" option("|" Symbol "|") Expression |
| 6375 | 6475 | ||
| ... | @@ -6443,12 +6543,10 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" | ... | @@ -6443,12 +6543,10 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%" |
| 6443 | 6543 | ||
| 6444 | PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression | 6544 | PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression |
| 6445 | 6545 | ||
| 6446 | SuffixOpExpression = ("async" option("&lt;" SuffixOpExpression "&gt;") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | PtrDerefExpression) | 6546 | SuffixOpExpression = ("async" option("&lt;" SuffixOpExpression "&gt;") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ".*" | ".?") |
| 6447 | 6547 | ||
| 6448 | FieldAccessExpression = "." Symbol | 6548 | FieldAccessExpression = "." Symbol |
| 6449 | 6549 | ||
| 6450 | PtrDerefExpression = ".*" | ||
| 6451 | |||
| 6452 | FnCallExpression = "(" list(Expression, ",") ")" | 6550 | FnCallExpression = "(" list(Expression, ",") ")" |
| 6453 | 6551 | ||
| 6454 | ArrayAccessExpression = "[" Expression "]" | 6552 | ArrayAccessExpression = "[" Expression "]" |
| ... | @@ -6461,7 +6559,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",") | ... | @@ -6461,7 +6559,7 @@ ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",") |
| 6461 | 6559 | ||
| 6462 | StructLiteralField = "." Symbol "=" Expression | 6560 | StructLiteralField = "." Symbol "=" Expression |
| 6463 | 6561 | ||
| 6464 | PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await" | 6562 | PrefixOp = "!" | "-" | "~" | (("*" | "[*]") option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "-%" | "try" | "await" |
| 6465 | 6563 | ||
| 6466 | PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType | 6564 | PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType |
| 6467 | 6565 | ||
| ... | @@ -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; |
| 7 | 7 | ||
| 8 | pub fn main() !void { | 8 | pub 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(); |
| 13 | 13 |
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")); |
| 278 | 278 | ||
| 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")); |
| 281 | 281 | ||
| 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")); |
| 285 | 285 | ||
| 286 | debug.assert(mem.eql(u8, ??args.single("library"), "lib2")); | 286 | debug.assert(mem.eql(u8, args.single("library").?, "lib2")); |
| 287 | 287 | ||
| 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 { |
| 27 | 27 | ||
| 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 "."; |
| 31 | 31 | ||
| 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); |
| 8 | pub const BuilderRef = removeNullability(c.LLVMBuilderRef); | 8 | pub const BuilderRef = removeNullability(c.LLVMBuilderRef); |
| 9 | 9 | ||
| 10 | fn removeNullability(comptime T: type) type { | 10 | fn 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); |
| 214 | 214 | ||
| 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); |
| 218 | 218 | ||
| ... | @@ -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(); |
| 250 | 250 | ||
| 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 "."; |
| 253 | 253 | ||
| 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 | } |
| 496 | 496 | ||
| ... | @@ -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 | } |
| 516 | 516 | ||
| 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 | }; |
| 523 | 523 | ||
| 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 | } |
| 530 | 530 | ||
| 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 | } |
| 535 | 535 | ||
| 536 | const zig_root_source_file = in_file; | 536 | const zig_root_source_file = in_file; |
| 537 | 537 | ||
| 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(); |
| 557 | 557 | ||
| 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); |
| 561 | 561 | ||
| 562 | module.is_test = false; | 562 | module.is_test = false; |
| 563 | 563 | ||
| ... | @@ -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 | } |
| 653 | 653 | ||
| 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); |
| 656 | 656 | ||
| 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(); |
| 735 | 735 | ||
| 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(); |
| 132 | 132 | ||
| 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); |
| 135 | 135 | ||
| 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); |
| 138 | 138 | ||
| 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); |
| 141 | 141 | ||
| 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 | } |
| 225 | 225 | ||
| 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 still | 144 | // 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 | }; |
| 221 | 224 | ||
| 222 | enum RuntimeHintMaybe { | 225 | enum 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 | }; |
| 227 | 230 | ||
| 228 | enum RuntimeHintPtr { | 231 | enum 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 { |
| 265 | 268 | ||
| 266 | // populated if special == ConstValSpecialRuntime | 269 | // 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 | }; |
| 574 | 578 | ||
| 579 | struct AstNodeUnwrapOptional { | ||
| 580 | AstNode *expr; | ||
| 581 | }; | ||
| 582 | |||
| 575 | enum CastOp { | 583 | enum CastOp { |
| 576 | CastOpNoCast, // signifies the function call expression is not a cast | 584 | CastOpNoCast, // signifies the function call expression is not a cast |
| 577 | CastOpNoop, // fn call expr is a cast, but does nothing | 585 | 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 | }; |
| 587 | 596 | ||
| 588 | struct AstNodeFnCallExpr { | 597 | struct 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 | }; |
| 626 | 634 | ||
| ... | @@ -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 it | 1046 | // whether we've finished resolving it |
| 1038 | bool complete; | 1047 | bool complete; |
| 1039 | 1048 | ||
| 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 pass | 1055 | 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 | }; |
| 1046 | 1059 | ||
| 1047 | struct TypeTableEntryMaybe { | 1060 | struct TypeTableEntryOptional { |
| 1048 | TypeTableEntry *child_type; | 1061 | TypeTableEntry *child_type; |
| 1049 | }; | 1062 | }; |
| 1050 | 1063 | ||
| ... | @@ -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; |
| 1080 | 1093 | ||
| 1081 | bool generate_name_table; | 1094 | LLVMValueRef name_function; |
| 1082 | LLVMValueRef name_table; | ||
| 1083 | 1095 | ||
| 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 it | 1117 | // whether we've finished resolving it |
| 1106 | bool complete; | 1118 | bool complete; |
| 1107 | 1119 | ||
| 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 pass | 1126 | 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, | ||
| 1398 | 1414 | ||
| 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; |
| 1714 | 1730 | ||
| 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; |
| 1719 | 1733 | ||
| ... | @@ -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 | }; |
| 2177 | 2190 | ||
| 2178 | struct IrInstructionUnOp { | 2191 | struct IrInstructionUnOp { |
| ... | @@ -2475,7 +2488,7 @@ struct IrInstructionTestNonNull { | ... | @@ -2475,7 +2488,7 @@ struct IrInstructionTestNonNull { |
| 2475 | IrInstruction *value; | 2488 | IrInstruction *value; |
| 2476 | }; | 2489 | }; |
| 2477 | 2490 | ||
| 2478 | struct IrInstructionUnwrapMaybe { | 2491 | struct IrInstructionUnwrapOptional { |
| 2479 | IrInstruction base; | 2492 | IrInstruction base; |
| 2480 | 2493 | ||
| 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 | }; |
| 2735 | 2748 | ||
| 2736 | struct IrInstructionMaybeWrap { | 2749 | struct IrInstructionOptionalWrap { |
| 2737 | IrInstruction base; | 2750 | IrInstruction base; |
| 2738 | 2751 | ||
| 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 | }; |
| 2850 | 2863 | ||
| 2851 | struct IrInstructionCanImplicitCast { | ||
| 2852 | IrInstruction base; | ||
| 2853 | |||
| 2854 | IrInstruction *type_value; | ||
| 2855 | IrInstruction *target_value; | ||
| 2856 | }; | ||
| 2857 | |||
| 2858 | struct IrInstructionDeclRef { | 2864 | struct IrInstructionDeclRef { |
| 2859 | IrInstruction base; | 2865 | IrInstruction base; |
| 2860 | 2866 | ||
| ... | @@ -2949,10 +2955,10 @@ struct IrInstructionExport { | ... | @@ -2949,10 +2955,10 @@ struct IrInstructionExport { |
| 2949 | struct IrInstructionErrorReturnTrace { | 2955 | struct IrInstructionErrorReturnTrace { |
| 2950 | IrInstruction base; | 2956 | IrInstruction base; |
| 2951 | 2957 | ||
| 2952 | enum Nullable { | 2958 | enum Optional { |
| 2953 | Null, | 2959 | Null, |
| 2954 | NonNull, | 2960 | NonNull, |
| 2955 | } nullable; | 2961 | } optional; |
| 2956 | }; | 2962 | }; |
| 2957 | 2963 | ||
| 2958 | struct IrInstructionErrorUnion { | 2964 | struct 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); | ||
| 387 | 388 | ||
| 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); |
| 521 | 522 | ||
| 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); |
| 526 | 526 | ||
| 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 C | 535 | // this is an optimization but also is necessary for calling C |
| 535 | // functions where all pointers are maybe pointers | 536 | // functions where all pointers are maybe pointers |
| 536 | // function types are technically pointers | 537 | // 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 value | 542 | // 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 | } |
| 2534 | 2536 | ||
| 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; |
| 2537 | 2543 | ||
| ... | @@ -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; |
| 2725 | 2731 | ||
| 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 | } |
| 2977 | 2988 | ||
| 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 | } |
| 2982 | 2993 | ||
| ... | @@ -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 | } |
| 3753 | 3765 | ||
| 3766 | bool is_ref(TypeTableEntry *type_entry) { | ||
| 3767 | return type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle; | ||
| 3768 | } | ||
| 3769 | |||
| 3770 | bool 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 | |||
| 3754 | bool is_container_ref(TypeTableEntry *type_entry) { | 3776 | bool 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 | } |
| 3758 | 3780 | ||
| 3759 | TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) { | 3781 | TypeTableEntry *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 | } |
| 3764 | 3786 | ||
| ... | @@ -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 | } |
| 4560 | 4582 | ||
| 4583 | static 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 | |||
| 4561 | static uint32_t hash_const_val(ConstExprValue *const_val) { | 4629 | static 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 algorithm | 4699 | // 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 algorithm | 4712 | // 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 algorithm | 4725 | // 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; |
| 4793 | 4821 | ||
| 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); |
| 4798 | 4828 | ||
| 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; |
| 4843 | 4873 | ||
| 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); |
| 4846 | 4876 | ||
| 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 | } |
| 5310 | 5352 | ||
| 5353 | bool 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 | |||
| 5311 | bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { | 5399 | bool 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 | } |
| 5495 | 5543 | ||
| 5544 | void 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 | |||
| 5496 | void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { | 5579 | void 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); |
| 70 | TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag); | 70 | TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag); |
| 71 | TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag); | 71 | TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag); |
| 72 | 72 | ||
| 73 | bool is_ref(TypeTableEntry *type_entry); | ||
| 74 | bool is_array_ref(TypeTableEntry *type_entry); | ||
| 73 | bool is_container_ref(TypeTableEntry *type_entry); | 75 | bool is_container_ref(TypeTableEntry *type_entry); |
| 74 | void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); | 76 | void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); |
| 75 | void scan_import(CodeGen *g, ImportTableEntry *import); | 77 | void 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); |
| 2499 | 2501 | ||
| 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; |
| 2502 | 2504 | ||
| 2503 | 2505 | ||
| ... | @@ -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 |
| 2678 | 2703 | ||
| 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 | } |
| 3250 | 3275 | ||
| 3251 | static LLVMValueRef gen_non_null_bit(CodeGen *g, TypeTableEntry *maybe_type, LLVMValueRef maybe_handle) { | 3276 | static 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 | } |
| 3272 | 3297 | ||
| 3273 | static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, | 3298 | static 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); |
| 3288 | 3313 | ||
| 3289 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | 3314 | LLVMPositionBuilderAtEnd(g->builder, fail_block); |
| 3290 | gen_safety_crash(g, PanicMsgIdUnwrapMaybeFail); | 3315 | gen_safety_crash(g, PanicMsgIdUnwrapOptionalFail); |
| 3291 | 3316 | ||
| 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 | } |
| 3434 | 3459 | ||
| 3460 | static 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 | |||
| 3435 | static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable, | 3555 | static 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); | ||
| 3441 | 3560 | ||
| 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 | } | ||
| 3456 | 3562 | ||
| 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 | } |
| 3464 | 3567 | ||
| 3465 | static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executable, | 3568 | static 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); |
| 3622 | 3725 | ||
| 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; |
| 3626 | 3729 | ||
| 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 | } |
| 4010 | 4112 | ||
| 4011 | static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, IrInstructionMaybeWrap *instruction) { | 4113 | static 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; |
| 4013 | 4115 | ||
| 4014 | assert(wanted_type->id == TypeTableEntryIdMaybe); | 4116 | assert(wanted_type->id == TypeTableEntryIdOptional); |
| 4015 | 4117 | ||
| 4016 | TypeTableEntry *child_type = wanted_type->data.maybe.child_type; | 4118 | TypeTableEntry *child_type = wanted_type->data.maybe.child_type; |
| 4017 | 4119 | ||
| ... | @@ -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 | } |
| 4916 | 5017 | ||
| 5018 | static 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 | |||
| 4917 | static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) { | 5091 | static 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()); |
| 4979 | 5149 | ||
| 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 | } |
| 5369 | 5468 | ||
| 5370 | static 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 | |||
| 5419 | static void build_all_basic_blocks(CodeGen *g, FnTableEntry *fn) { | 5469 | static 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 | } |
| 5513 | 5563 | ||
| 5514 | generate_error_name_table(g); | 5564 | generate_error_name_table(g); |
| 5515 | generate_enum_name_tables(g); | ||
| 5516 | 5565 | ||
| 5517 | // Generate module level variables | 5566 | // 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 | } |
| 6252 | 6300 | ||
| 6253 | static void define_builtin_compile_vars(CodeGen *g) { | 6301 | Buf *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(); |
| 6261 | 6303 | ||
| 6262 | // Modifications to this struct must be coordinated with code that does anything with | 6304 | // 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) { |
| 6619 | 6664 | ||
| 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"); |
| 6621 | 6666 | ||
| 6667 | |||
| 6668 | return contents; | ||
| 6669 | } | ||
| 6670 | |||
| 6671 | static 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); |
| 6624 | 6682 | ||
| ... | @@ -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); |
| 59 | 59 | ||
| 60 | void codegen_translate_c(CodeGen *g, Buf *path); | 60 | void codegen_translate_c(CodeGen *g, Buf *path); |
| 61 | 61 | ||
| 62 | Buf *codegen_generate_builtin_source(CodeGen *g); | ||
| 63 | |||
| 62 | 64 | ||
| 63 | #endif | 65 | #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 | }; |
| 66 | 67 | ||
| 67 | struct ConstCastErrSetMismatch { | 68 | struct 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, |
| 108 | static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); | 110 | static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); |
| 109 | static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); | 111 | static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); |
| 110 | static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, uint32_t new_align); | 112 | static TypeTableEntry *adjust_ptr_align(CodeGen *g, TypeTableEntry *ptr_type, uint32_t new_align); |
| 113 | static TypeTableEntry *adjust_slice_align(CodeGen *g, TypeTableEntry *slice_type, uint32_t new_align); | ||
| 111 | 114 | ||
| 112 | ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { | 115 | ConstExprValue *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 | } |
| 371 | 374 | ||
| 372 | static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapMaybe *) { | 375 | static constexpr IrInstructionId ir_instruction_id(IrInstructionUnwrapOptional *) { |
| 373 | return IrInstructionIdUnwrapMaybe; | 376 | return IrInstructionIdUnwrapOptional; |
| 374 | } | 377 | } |
| 375 | 378 | ||
| 376 | static constexpr IrInstructionId ir_instruction_id(IrInstructionClz *) { | 379 | static 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 | } |
| 523 | 526 | ||
| 524 | static constexpr IrInstructionId ir_instruction_id(IrInstructionMaybeWrap *) { | 527 | static constexpr IrInstructionId ir_instruction_id(IrInstructionOptionalWrap *) { |
| 525 | return IrInstructionIdMaybeWrap; | 528 | return IrInstructionIdOptionalWrap; |
| 526 | } | 529 | } |
| 527 | 530 | ||
| 528 | static constexpr IrInstructionId ir_instruction_id(IrInstructionErrWrapPayload *) { | 531 | static 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 | } |
| 587 | 590 | ||
| 588 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCanImplicitCast *) { | ||
| 589 | return IrInstructionIdCanImplicitCast; | ||
| 590 | } | ||
| 591 | |||
| 592 | static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclRef *) { | 591 | static 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 * |
| 1572 | static IrInstruction *ir_build_unwrap_maybe(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value, | 1571 | static 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; |
| 1578 | 1577 | ||
| ... | @@ -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 | } |
| 1592 | 1591 | ||
| 1593 | static IrInstruction *ir_build_maybe_wrap(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { | 1592 | static 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; |
| 1596 | 1595 | ||
| 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 | } |
| 2350 | 2349 | ||
| 2351 | static 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 | |||
| 2365 | static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node, | 2350 | static 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 | } |
| 2513 | 2498 | ||
| 2514 | static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Nullable nullable) { | 2499 | static 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; |
| 2517 | 2502 | ||
| 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 | } |
| 3312 | 3297 | ||
| 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); |
| 3317 | 3302 | ||
| 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 * |
| 4620 | 4590 | ||
| 4621 | static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { | 4591 | static 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 potentially | 4593 | 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 | } |
| 4696 | 4663 | ||
| 4697 | static 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 | |||
| 4712 | static IrInstruction *ir_gen_bool_not(IrBuilder *irb, Scope *scope, AstNode *node) { | 4664 | static 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); |
| 5405 | 5355 | ||
| 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"); |
| 5409 | 5359 | ||
| 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 |
| 6582 | 6531 | ||
| 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 | } |
| 7686 | 7648 | ||
| 7687 | 7649 | ||
| 7688 | static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry *expected_type, | 7650 | static 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; |
| 7694 | 7656 | ||
| 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 | } | ||
| 7697 | 7675 | ||
| 7698 | // pointer const | 7676 | // 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 | } |
| 7716 | 7695 | ||
| 7717 | // slice const | 7696 | // 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 | } |
| 7737 | 7716 | ||
| 7738 | // maybe | 7717 | // 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 | } |
| 7748 | 7728 | ||
| 7749 | // error union | 7729 | // 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 | } |
| 7767 | 7749 | ||
| 7768 | // error set | 7750 | // 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; |
| 7772 | 7754 | ||
| 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 | } |
| 7811 | 7793 | ||
| 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 | } |
| 7817 | 7799 | ||
| 7818 | // fn | 7800 | // 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 parameters | 7855 | // 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]; |
| 7873 | 7858 | ||
| 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 | }; |
| 7901 | 7887 | ||
| 7902 | static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, TypeTableEntry *expected_type, | 7888 | static 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 | } |
| 7944 | 7931 | ||
| 7945 | // implicit conversion from ?T to ?U | 7932 | // 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 | } |
| 7952 | 7939 | ||
| 7953 | // implicit conversion from non maybe type to maybe type | 7940 | // 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 | } |
| 7960 | 7947 | ||
| 7961 | // implicit conversion from null literal to maybe type | 7948 | // 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 | } |
| 7967 | 7954 | ||
| 7968 | // implicit T to U!T | 7955 | // 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 | } |
| 7974 | 7961 | ||
| 7975 | // implicit conversion from error set to error union type | 7962 | // 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 | } |
| 7981 | 7968 | ||
| 7982 | // implicit conversion from T to U!?T | 7969 | // 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 | } |
| 7991 | 7978 | ||
| 7992 | // implicit widening conversion | 7979 | // 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 | } |
| 8000 | 7987 | ||
| 8001 | // small enough unsigned ints can get casted to large enough signed ints | 7988 | // 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 | } |
| 8008 | 7995 | ||
| 8009 | // implicit float widening conversion | 7996 | // 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 | } |
| 8016 | 8003 | ||
| 8017 | // implicit [N]T to []const T | 8004 | // 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); |
| 8021 | 8008 | ||
| 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 | } |
| 8028 | 8016 | ||
| 8029 | // implicit &const [N]T to []const T | 8017 | // 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); |
| 8037 | 8026 | ||
| 8038 | TypeTableEntry *array_type = actual_type->data.pointer.child_type; | 8027 | TypeTableEntry *array_type = actual_type->data.pointer.child_type; |
| 8039 | 8028 | ||
| 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 | } |
| 8046 | 8036 | ||
| 8047 | // implicit [N]T to &const []const T | 8037 | // 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 | } |
| 8062 | 8083 | ||
| 8063 | // implicit [N]T to ?[]const T | 8084 | // 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 known | 8123 | // 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 | } |
| 8108 | 8131 | ||
| 8109 | // implicit union to its enum tag type | 8132 | // 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 | } |
| 8119 | 8142 | ||
| 8120 | // implicit enum to union which has the enum as the tag type | 8143 | // 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 | } |
| 8130 | 8153 | ||
| 8131 | // implicit enum to &const union which has the enum as the tag type | 8154 | // 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 | } |
| 8143 | 8169 | ||
| 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 anything | 8181 | // 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 something | 8186 | // 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; |
| 8391 | 8432 | ||
| 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; |
| 8396 | 8437 | ||
| 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 | } |
| 8481 | 8522 | ||
| 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 | } |
| 8485 | 8526 | ||
| 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 | } |
| 8507 | 8548 | ||
| 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 | } |
| 8513 | 8555 | ||
| 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 | } |
| 8535 | 8578 | ||
| 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 | } |
| 8541 | 8585 | ||
| 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 | } |
| 8578 | 8623 | ||
| 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 | } |
| 8587 | 8633 | ||
| 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, |
| 8804 | static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, | 8855 | static 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 | } |
| 8824 | 8875 | ||
| 8876 | static 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 | |||
| 8903 | static 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 | |||
| 8825 | static bool is_container(TypeTableEntry *type) { | 8933 | static 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 | } |
| 9116 | 9224 | ||
| 9117 | static IrInstruction *ir_analyze_maybe_wrap(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) { | 9225 | static 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); |
| 9119 | 9227 | ||
| 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 |
| 9129 | 9237 | ||
| 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 | } |
| 9137 | 9249 | ||
| 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 | } |
| 9280 | 9392 | ||
| 9281 | static IrInstruction *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value, TypeTableEntry *wanted_type) { | 9393 | static 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)); |
| 9284 | 9396 | ||
| 9285 | ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); | 9397 | ConstExprValue *val = ir_resolve_const(ira, value, UndefBad); |
| 9286 | assert(val); | 9398 | assert(val); |
| 9287 | 9399 | ||
| 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 | } |
| 9294 | 9411 | ||
| ... | @@ -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 |
| 9300 | 9417 | ||
| 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 | } |
| 9307 | 9434 | ||
| 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 | } |
| 9811 | 9938 | ||
| 9812 | // explicit match or non-const to const | 9939 | // 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 | } |
| 9816 | 9943 | ||
| ... | @@ -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; |
| 9875 | 10003 | ||
| 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 | } |
| 9906 | 10036 | ||
| 9907 | // explicit cast from [N]T to ?[]const N | 10037 | // 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 | } |
| 9975 | 10106 | ||
| 9976 | // explicit cast from child type of maybe type to maybe type | 10107 | // 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 | } |
| 10004 | 10169 | ||
| 10005 | // explicit cast from null literal to maybe type | 10170 | // 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 |
| 10011 | 10176 | ||
| 10012 | // explicit cast from child type of error type to error type | 10177 | // 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 | } |
| 10026 | 10193 | ||
| 10027 | // explicit cast from [N]T to %[]const T | 10194 | // 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 | } |
| 10056 | 10224 | ||
| 10057 | // explicit cast from T to %?T | 10225 | // 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 | } |
| 10079 | 10247 | ||
| 10080 | // explicit cast from number literal to another type | 10248 | // explicit cast from number literal to another type |
| 10081 | // explicit cast from number literal to &const integer | 10249 | // 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 | } |
| 10230 | 10399 | ||
| 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 | |||
| 10231 | 10414 | ||
| 10232 | // explicit cast from undefined to anything | 10415 | // 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 it | 10420 | // 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 | } |
| 10621 | 10805 | ||
| 10806 | static 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 | |||
| 10622 | static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { | 10816 | static 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 * |
| 11135 | 11329 | ||
| 11136 | static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp *bin_op_instruction) { | 11330 | static 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; |
| 11140 | 11340 | ||
| 11141 | // look for pointer math | 11341 | // 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 | } |
| 11623 | 11823 | ||
| 11624 | enum VarClassRequired { | ||
| 11625 | VarClassRequiredAny, | ||
| 11626 | VarClassRequiredConst, | ||
| 11627 | VarClassRequiredIllegal, | ||
| 11628 | }; | ||
| 11629 | |||
| 11630 | static 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 | |||
| 11679 | static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstructionDeclVar *decl_var_instruction) { | 11824 | static 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; |
| 11681 | 11826 | ||
| ... | @@ -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 | } |
| 11745 | 11895 | ||
| ... | @@ -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) { |
| 11965 | static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, | 12115 | static 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 | } |
| 12622 | 12774 | ||
| 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 | } |
| 13222 | 13384 | ||
| 13385 | static 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 | |||
| 13223 | static TypeTableEntry *adjust_ptr_len(CodeGen *g, TypeTableEntry *ptr_type, PtrLen ptr_len) { | 13392 | static 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 | } | ||
| 13801 | 13974 | ||
| 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 |
| 14141 | 14314 | ||
| 14142 | static TypeTableEntry *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstructionLoadPtr *load_ptr_instruction) { | 14315 | static 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 |
| 14700 | 14876 | ||
| 14701 | TypeTableEntry *type_entry = value->value.type; | 14877 | TypeTableEntry *type_entry = value->value.type; |
| 14702 | 14878 | ||
| 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; |
| 14708 | 14884 | ||
| 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 | } |
| 14713 | 14889 | ||
| ... | @@ -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 | } |
| 14726 | 14902 | ||
| 14727 | static TypeTableEntry *ir_analyze_instruction_unwrap_maybe(IrAnalyze *ira, | 14903 | static 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); |
| 14772 | 14932 | ||
| 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 | } |
| 15745 | 15910 | ||
| 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: type | 16309 | // 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 | } |
| 16215 | 16375 | ||
| 16216 | static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry) | 16376 | static 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)); |
| 16220 | 16379 | ||
| ... | @@ -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 | }; |
| 16241 | 16400 | ||
| 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; |
| 16246 | 16421 | ||
| 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; |
| 16249 | 16424 | ||
| 16250 | // is_const: bool | 16425 | // 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: bool | 16433 | |
| 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: u32 | 16439 | // 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: type | 16444 | // 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; | ||
| 16270 | 16454 | ||
| 16271 | return result; | 16455 | return result; |
| 16272 | }; | 16456 | }; |
| 16273 | 16457 | ||
| 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 |
| 16364 | 16552 | ||
| 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"); |
| 16372 | 16560 | ||
| 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 |
| 16570 | 16758 | ||
| 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"); |
| 16572 | 16760 | ||
| 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]; |
| 16577 | 16764 | ||
| ... | @@ -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); |
| 16584 | 16771 | ||
| 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 | else | 16774 | } 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 | } |
| 16592 | 16778 | ||
| 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 | } |
| 16626 | 16804 | ||
| ... | @@ -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 |
| 16651 | 16829 | ||
| 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); |
| 16653 | 16831 | ||
| 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]; |
| 16658 | 16835 | ||
| ... | @@ -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); |
| 16665 | 16842 | ||
| 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 | else | 16845 | } 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 | } |
| 16676 | 16852 | ||
| 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 | } |
| 18424 | 18600 | ||
| 18425 | static 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 | |||
| 18449 | static TypeTableEntry *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstructionPanic *instruction) { | 18601 | static 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); |
| 18493 | 18645 | ||
| 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 | } |
| 18977 | 19129 | ||
| 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 |
| 19873 | static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction) { | 20023 | static 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 | } |
| 483 | 483 | ||
| 484 | static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapMaybe *instruction) { | 484 | static 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 | } |
| 779 | 779 | ||
| 780 | static void ir_print_maybe_wrap(IrPrint *irp, IrInstructionMaybeWrap *instruction) { | 780 | static 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 | } |
| 915 | 915 | ||
| 916 | static 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 | |||
| 924 | static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) { | 916 | static 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) { |
| 1040 | 1032 | ||
| 1041 | static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) { | 1033 | static 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 | } |
| 393 | 393 | ||
| 394 | static 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 | } |
| 930 | 943 |
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) { |
| 214 | enum Cmd { | 215 | enum 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 | } |
| 728 | 733 | ||
| 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 | } |
| 1047 | 1047 | ||
| 1048 | /* | 1048 | /* |
| 1049 | SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | PtrDerefExpression | SliceExpression) | 1049 | SuffixOpExpression = ("async" option("&lt;" SuffixOpExpression "&gt;") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ".*" | ".?") |
| 1050 | FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) | 1050 | FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) |
| 1051 | ArrayAccessExpression : token(LBracket) Expression token(RBracket) | 1051 | ArrayAccessExpression : token(LBracket) Expression token(RBracket) |
| 1052 | SliceExpression = "[" Expression ".." option(Expression) "]" | 1052 | SliceExpression = "[" Expression ".." option(Expression) "]" |
| 1053 | FieldAccessExpression : token(Dot) token(Symbol) | 1053 | FieldAccessExpression : token(Dot) token(Symbol) |
| 1054 | PtrDerefExpression = ".*" | ||
| 1055 | StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression | 1054 | StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression |
| 1056 | */ | 1055 | */ |
| 1057 | static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { | 1056 | static 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; |
| 1150 | 1149 | ||
| 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 | } |
| 2305 | 2310 | ||
| 2306 | /* | 2311 | /* |
| 2307 | UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression | 2312 | UnwrapExpression : BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression |
| 2308 | UnwrapMaybe : "??" BoolOrExpression | 2313 | UnwrapOptional = "orelse" Expression |
| 2309 | UnwrapError = "catch" option("|" Symbol "|") Expression | 2314 | UnwrapError = "catch" option("|" Symbol "|") Expression |
| 2310 | */ | 2315 | */ |
| 2311 | static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) { | 2316 | static 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 |
| 2315 | 2320 | ||
| 2316 | Token *token = &pc->tokens->at(*token_index); | 2321 | Token *token = &pc->tokens->at(*token_index); |
| 2317 | 2322 | ||
| 2318 | if (token->id == TokenIdDoubleQuestion) { | 2323 | if (token->id == TokenIdKeywordOrElse) { |
| 2319 | *token_index += 1; | 2324 | *token_index += 1; |
| 2320 | 2325 | ||
| 2321 | AstNode *rhs = ast_parse_expression(pc, token_index, true); | 2326 | AstNode *rhs = ast_parse_expression(pc, token_index, true); |
| 2322 | 2327 | ||
| 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; |
| 2327 | 2332 | ||
| 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; |
| 604 | 602 | ||
| 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 | ||
| 173 | typedef Token ZigToken; | ||
| 173 | 174 | ||
| 174 | struct Tokenization { | 175 | struct 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 | } |
| 262 | 262 | ||
| 263 | static 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 | |||
| 263 | static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpType op, AstNode *rhs_node) { | 269 | static 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 | } |
| 278 | 284 | ||
| 279 | static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node) { | 285 | static 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; |
| 381 | 390 | ||
| 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; |
| 385 | 394 | ||
| ... | @@ -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 | } |
| 408 | 417 | ||
| 409 | static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child) { | ||
| 410 | return trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, child); | ||
| 411 | } | ||
| 412 | |||
| 413 | static AstNode *get_global(Context *c, Buf *name) { | 418 | static 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 | } |
| 733 | 738 | ||
| 739 | static 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 | |||
| 734 | static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) { | 763 | static 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 | } |
| 860 | 889 | ||
| 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 | } |
| 864 | 893 | ||
| 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; |
| 4291 | 4322 | ||
| 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; |
| 4464 | 4495 | ||
| 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); |
| 854 | 854 | ||
| 855 | case ZigLLVM_Wasm: | 855 | case ZigLLVM_Wasm: |
| 856 | assert(false); // TODO ZigLLDLink for Wasm | 856 | return lld::wasm::link(array_ref_args, false, diag); |
| 857 | } | 857 | } |
| 858 | assert(false); // unreachable | 858 | assert(false); // unreachable |
| 859 | abort(); | 859 | abort(); |
std/array_list.zig+70-40| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("index.zig"); |
| 2 | const debug = std.debug; | 2 | const debug = std.debug; |
| 3 | const assert = debug.assert; | 3 | const assert = debug.assert; |
| 4 | const assertError = debug.assertError; | ||
| 4 | const mem = std.mem; | 5 | const mem = std.mem; |
| 5 | const Allocator = mem.Allocator; | 6 | const Allocator = mem.Allocator; |
| 6 | 7 | ||
| ... | @@ -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 | } |
| 30 | 31 | ||
| 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 | } |
| 34 | 35 | ||
| 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 | } |
| 38 | 39 | ||
| 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 | } |
| 42 | 43 | ||
| 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 | } |
| 46 | 60 | ||
| 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 | } |
| 69 | 83 | ||
| 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; |
| 73 | 87 | ||
| 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 | } |
| 77 | 91 | ||
| 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; |
| 81 | 95 | ||
| 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 | } |
| 85 | 99 | ||
| 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 | } |
| 90 | 104 | ||
| 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 | } |
| 96 | 110 | ||
| 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 | } |
| 101 | 115 | ||
| 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 | } |
| 106 | 120 | ||
| 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 | } |
| 116 | 130 | ||
| 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 | } |
| 124 | 138 | ||
| ... | @@ -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 | } |
| 160 | 174 | ||
| 161 | test "basic ArrayList test" { | 175 | test "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(); |
| 164 | 181 | ||
| 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" { |
| 200 | 220 | ||
| 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 | } |
| 204 | 234 | ||
| 205 | test "iterator ArrayList test" { | 235 | test "iterator ArrayList test" { |
| ... | @@ -228,7 +258,7 @@ test "iterator ArrayList test" { | ... | @@ -228,7 +258,7 @@ test "iterator ArrayList test" { |
| 228 | } | 258 | } |
| 229 | 259 | ||
| 230 | it.reset(); | 260 | it.reset(); |
| 231 | assert(??it.next() == 1); | 261 | assert(it.next().? == 1); |
| 232 | } | 262 | } |
| 233 | 263 | ||
| 234 | test "insert ArrayList test" { | 264 | test "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(); |
| 96 | 96 | ||
| 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 | } |
| 100 | 110 | ||
| 101 | fn startPuts(ctx: *Context) u8 { | 111 | fn startPuts(ctx: *Context) u8 { |
| ... | @@ -114,15 +124,14 @@ fn startPuts(ctx: *Context) u8 { | ... | @@ -114,15 +124,14 @@ fn startPuts(ctx: *Context) u8 { |
| 114 | 124 | ||
| 115 | fn startGets(ctx: *Context) u8 { | 125 | fn 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 fuzz | 130 | 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 | } |
| 122 | 134 | ||
| 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 | } |
| 34 | 34 | ||
| 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 | } |
| 41 | 41 | ||
| ... | @@ -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(); |
| 99 | 99 | ||
| 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 | } |
| 103 | 113 | ||
| 104 | fn startPuts(ctx: *Context) u8 { | 114 | fn startPuts(ctx: *Context) u8 { |
| ... | @@ -117,15 +127,14 @@ fn startPuts(ctx: *Context) u8 { | ... | @@ -117,15 +127,14 @@ fn startPuts(ctx: *Context) u8 { |
| 117 | 127 | ||
| 118 | fn startGets(ctx: *Context) u8 { | 128 | fn 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 fuzz | 133 | 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 | } |
| 125 | 137 | ||
| 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 | } |
| 38 | 38 | ||
| 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 | } |
| 43 | 43 | ||
| 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(); |
| 73 | 73 | ||
| 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()); |
| 77 | 77 | ||
| 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()); |
| 81 | 81 | ||
| 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()); |
| 85 | 85 | ||
| 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 | } |
| 23 | 23 | ||
| ... | @@ -33,7 +33,7 @@ pub const BufSet = struct { | ... | @@ -33,7 +33,7 @@ pub const BufSet = struct { |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 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 | } |
| 39 | 39 |
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 | /// * ::replaceContents | 30 | /// * ::replaceContents |
| 31 | /// * ::replaceContentsBuffer | ||
| 32 | /// * ::resize | 31 | /// * ::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 been | 41 | /// 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 | } |
| 50 | 49 | ||
| ... | @@ -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 | } |
| 118 | 117 | ||
| 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 | } |
| 137 | 137 | ||
| 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 default | 139 | 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; |
| 364 | 364 | ||
| 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; |
| 418 | 418 | ||
| 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; |
| 422 | 422 | ||
| 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 used | 518 | // 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 | } |
| 619 | 619 | ||
| 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; |
| 1396 | 1396 | ||
| 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; |
| 1510 | 1511 | ||
| 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 | }; |
| 1946 | 1948 | ||
| 1947 | fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { | 1949 | fn 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.3 | 1952 | // 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; |
| 20 | pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; | 20 | pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; |
| 21 | pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; | 21 | pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; |
| 22 | pub extern "c" fn raise(sig: c_int) c_int; | 22 | pub extern "c" fn raise(sig: c_int) c_int; |
| 23 | pub extern "c" fn read(fd: c_int, buf: [*]c_void, nbyte: usize) isize; | 23 | pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; |
| 24 | pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; | 24 | pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; |
| 25 | pub extern "c" fn write(fd: c_int, buf: [*]const c_void, nbyte: usize) isize; | 25 | pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; |
| 26 | pub extern "c" fn mmap(addr: ?[*]c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?[*]c_void; | 26 | pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; |
| 27 | pub extern "c" fn munmap(addr: [*]c_void, len: usize) c_int; | 27 | pub extern "c" fn munmap(addr: *c_void, len: usize) c_int; |
| 28 | pub extern "c" fn unlink(path: [*]const u8) c_int; | 28 | pub extern "c" fn unlink(path: [*]const u8) c_int; |
| 29 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; | 29 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; |
| 30 | pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; | 30 | pub 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; |
| 48 | pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; | 48 | pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; |
| 49 | pub extern "c" fn rmdir(path: [*]const u8) c_int; | 49 | pub extern "c" fn rmdir(path: [*]const u8) c_int; |
| 50 | 50 | ||
| 51 | pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?[*]c_void; | 51 | pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; |
| 52 | pub extern "c" fn malloc(usize) ?[*]c_void; | 52 | pub extern "c" fn malloc(usize) ?*c_void; |
| 53 | pub extern "c" fn realloc([*]c_void, usize) ?[*]c_void; | 53 | pub extern "c" fn realloc(*c_void, usize) ?*c_void; |
| 54 | pub extern "c" fn free([*]c_void) void; | 54 | pub extern "c" fn free(*c_void) void; |
| 55 | pub extern "c" fn posix_memalign(memptr: *[*]c_void, alignment: usize, size: usize) c_int; | 55 | pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; |
| 56 | 56 | ||
| 57 | pub 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; | 57 | pub 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; |
| 58 | pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; | 58 | pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; |
| 59 | pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: [*]c_void, stacksize: usize) c_int; | 59 | pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; |
| 60 | pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; | 60 | pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; |
| 61 | pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; | 61 | pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; |
| 62 | 62 |
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 | } |
| 90 | 90 | ||
| 91 | /// TODO: add `==` operator for `error_union == error_set`, and then | ||
| 92 | /// remove this function | ||
| 93 | pub 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. |
| 93 | pub fn assertOrPanic(ok: bool) void { | 103 | pub 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(); |
| 260 | 270 | ||
| 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 | } |
| 434 | 444 | ||
| 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 | } |
| 442 | 452 | ||
| 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 | } |
| 451 | 461 | ||
| 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 | } |
| 459 | 469 | ||
| 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; |
| 742 | 752 | ||
| 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)); |
| 41 | 41 | ||
| 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.?; |
| 44 | 44 | ||
| 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 | } |
| 48 | 48 |
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 | } |
| 167 | 182 | ||
| ... | @@ -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 | } |
| 299 | 314 | ||
| ... | @@ -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 | } |
| 554 | 569 | ||
| 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 | }; | ||
| 563 | 583 | ||
| 564 | try formatFloatDecimal(new_value, width, context, Errors, output); | 584 | try formatFloatDecimal(new_value, width, context, Errors, output); |
| 565 | 585 | ||
| ... | @@ -807,11 +827,11 @@ test "parse unsigned comptime" { | ... | @@ -807,11 +827,11 @@ test "parse unsigned comptime" { |
| 807 | test "fmt.format" { | 827 | test "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); |
| 267 | 267 | ||
| 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); |
| 270 | 270 | ||
| 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 | } |
| 318 | 318 | ||
| 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 | } |
| 23 | 23 | ||
| 24 | fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { | 24 | fn 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 | } |
| 34 | 34 | ||
| 35 | fn cFree(self: *Allocator, old_mem: []u8) void { | 35 | fn 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 | } |
| 39 | 39 | ||
| ... | @@ -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 | } |
| 349 | 349 |
std/json.zig+177-88| ... | @@ -3,6 +3,7 @@ | ... | @@ -3,6 +3,7 @@ |
| 3 | // https://tools.ietf.org/html/rfc8259 | 3 | // https://tools.ietf.org/html/rfc8259 |
| 4 | 4 | ||
| 5 | const std = @import("index.zig"); | 5 | const std = @import("index.zig"); |
| 6 | const debug = std.debug; | ||
| 6 | const mem = std.mem; | 7 | const mem = std.mem; |
| 7 | 8 | ||
| 8 | const u1 = @IntType(false, 1); | 9 | const 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. |
| 89 | pub const StreamingJsonParser = struct { | 90 | // |
| 91 | // For a non-byte based wrapper, consider using TokenStream instead. | ||
| 92 | pub const StreamingParser = struct { | ||
| 90 | // Current state | 93 | // Current state |
| 91 | state: State, | 94 | state: State, |
| 92 | // How many bytes we have counted for the current token | 95 | // 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); |
| 111 | 114 | ||
| 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 | } |
| 117 | 120 | ||
| 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 function | 124 | // Set before ever read in main transition function |
| ... | @@ -175,7 +178,7 @@ pub const StreamingJsonParser = struct { | ... | @@ -175,7 +178,7 @@ pub const StreamingJsonParser = struct { |
| 175 | 178 | ||
| 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 | } |
| 218 | 221 | ||
| 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 | } |
| 326 | 331 | ||
| 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 | } |
| 350 | 357 | ||
| 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 | }; |
| 854 | 861 | ||
| 862 | // A small wrapper over a StreamingParser for full slices. Returns a stream of json Tokens. | ||
| 863 | pub 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 | |||
| 910 | fn checkNext(p: *TokenStream, id: Token.Id) void { | ||
| 911 | const token = (p.next() catch unreachable).?; | ||
| 912 | debug.assert(token.id == id); | ||
| 913 | } | ||
| 914 | |||
| 915 | test "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 necessarily | 968 | // 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. |
| 857 | pub fn validate(s: []const u8) bool { | 970 | pub fn validate(s: []const u8) bool { |
| 858 | var p = StreamingJsonParser.init(); | 971 | var p = StreamingParser.init(); |
| 859 | 972 | ||
| 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; |
| 863 | 976 | ||
| 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 | } |
| 871 | 984 | ||
| 985 | test "json validate" { | ||
| 986 | debug.assert(validate("{}")); | ||
| 987 | } | ||
| 988 | |||
| 872 | const Allocator = std.mem.Allocator; | 989 | const Allocator = std.mem.Allocator; |
| 873 | const ArenaAllocator = std.heap.ArenaAllocator; | 990 | const ArenaAllocator = std.heap.ArenaAllocator; |
| 874 | const ArrayList = std.ArrayList; | 991 | const 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(); |
| 930 | 1047 | ||
| 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"); |
| 972 | 1089 | ||
| 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(); |
| 989 | 1106 | ||
| 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 | }; |
| 1013 | 1130 | ||
| 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. |
| 1015 | pub const JsonParser = struct { | 1132 | pub 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 | }; |
| 1028 | 1145 | ||
| 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 | } |
| 1037 | 1154 | ||
| 1038 | pub fn deinit(p: *JsonParser) void { | 1155 | pub fn deinit(p: *Parser) void { |
| 1039 | p.stack.deinit(); | 1156 | p.stack.deinit(); |
| 1040 | } | 1157 | } |
| 1041 | 1158 | ||
| 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 | } |
| 1046 | 1163 | ||
| 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); |
| 1049 | 1166 | ||
| 1050 | var arena = ArenaAllocator.init(p.allocator); | 1167 | var arena = ArenaAllocator.init(p.allocator); |
| 1051 | errdefer arena.deinit(); | 1168 | errdefer arena.deinit(); |
| 1052 | 1169 | ||
| 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 | } |
| 1082 | 1173 | ||
| 1083 | std.debug.assert(p.stack.len == 1); | 1174 | debug.assert(p.stack.len == 1); |
| 1084 | 1175 | ||
| 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 { |
| 1090 | 1181 | ||
| 1091 | // Even though p.allocator exists, we take an explicit allocator so that allocation state | 1182 | // 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 | } |
| 1225 | 1316 | ||
| 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 | } |
| 1246 | 1337 | ||
| 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 escape | 1339 | // 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 | } |
| 1253 | 1344 | ||
| 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 | else | 1348 | else |
| ... | @@ -1259,10 +1350,8 @@ pub const JsonParser = struct { | ... | @@ -1259,10 +1350,8 @@ pub const JsonParser = struct { |
| 1259 | } | 1350 | } |
| 1260 | }; | 1351 | }; |
| 1261 | 1352 | ||
| 1262 | const debug = std.debug; | ||
| 1263 | |||
| 1264 | test "json parser dynamic" { | 1353 | test "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(); |
| 1267 | 1356 | ||
| 1268 | const s = | 1357 | const s = |
| ... | @@ -1287,17 +1376,17 @@ test "json parser dynamic" { | ... | @@ -1287,17 +1376,17 @@ test "json parser dynamic" { |
| 1287 | 1376 | ||
| 1288 | var root = tree.root; | 1377 | var root = tree.root; |
| 1289 | 1378 | ||
| 1290 | var image = (??root.Object.get("Image")).value; | 1379 | var image = root.Object.get("Image").?.value; |
| 1291 | 1380 | ||
| 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); |
| 1294 | 1383 | ||
| 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); |
| 1297 | 1386 | ||
| 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")); |
| 1300 | 1389 | ||
| 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 | } |
| 19 | 19 | ||
| 20 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 21 | // | ||
| 22 | // Additional tests not part of test JSONTestSuite. | ||
| 23 | |||
| 24 | test "y_trailing_comma_after_empty" { | ||
| 25 | ok( | ||
| 26 | \\{"1":[],"2":{},"3":"4"} | ||
| 27 | ); | ||
| 28 | } | ||
| 29 | |||
| 20 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 30 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 21 | 31 | ||
| 22 | test "y_array_arraysWithSpaces" { | 32 | test "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} |
| 272 | 272 | ||
| 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 | } |
| 277 | 277 | ||
| ... | @@ -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} |
| 338 | 338 | ||
| 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 @@ | ||
| 1 | pub use @import("int.zig"); | ||
| 2 | |||
| 3 | test "math.big" { | ||
| 4 | _ = @import("int.zig"); | ||
| 5 | } | ||
std/math/big/int.zig created+2023| ... | @@ -0,0 +1,2023 @@ | ||
| 1 | const std = @import("../../index.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const debug = std.debug; | ||
| 4 | const math = std.math; | ||
| 5 | const mem = std.mem; | ||
| 6 | const Allocator = mem.Allocator; | ||
| 7 | const ArrayList = std.ArrayList; | ||
| 8 | |||
| 9 | const TypeId = builtin.TypeId; | ||
| 10 | |||
| 11 | pub const Limb = usize; | ||
| 12 | pub const DoubleLimb = @IntType(false, 2 * Limb.bit_count); | ||
| 13 | pub const Log2Limb = math.Log2Int(Limb); | ||
| 14 | |||
| 15 | comptime { | ||
| 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 | |||
| 21 | const 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. | ||
| 26 | fn 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 | |||
| 54 | pub 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 | |||
| 1067 | const u256 = @IntType(false, 256); | ||
| 1068 | var al = debug.global_allocator; | ||
| 1069 | |||
| 1070 | test "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 | |||
| 1085 | test "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 | |||
| 1092 | test "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 | |||
| 1099 | test "big.int comptime_int to" { | ||
| 1100 | const a = try Int.initSet(al, 0xefffffff00000001eeeeeeefaaaaaaab); | ||
| 1101 | |||
| 1102 | debug.assert((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab); | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | test "big.int sub-limb to" { | ||
| 1106 | const a = try Int.initSet(al, 10); | ||
| 1107 | |||
| 1108 | debug.assert((try a.to(u8)) == 10); | ||
| 1109 | } | ||
| 1110 | |||
| 1111 | test "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 | |||
| 1121 | test "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 | |||
| 1148 | test "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 | |||
| 1177 | test "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 | |||
| 1188 | test "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 | |||
| 1210 | test "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 | |||
| 1217 | test "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 | |||
| 1223 | test "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 | |||
| 1228 | test "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 | |||
| 1233 | test "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 | |||
| 1242 | test "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 | |||
| 1252 | test "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 | |||
| 1261 | test "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 | |||
| 1270 | test "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 | |||
| 1279 | test "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 | |||
| 1288 | test "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 | |||
| 1300 | test "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 | |||
| 1313 | test "big.int to negative" { | ||
| 1314 | var a = try Int.initSet(al, -10); | ||
| 1315 | |||
| 1316 | debug.assert((try a.to(i32)) == -10); | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | test "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 | |||
| 1327 | test "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 | |||
| 1335 | test "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 | |||
| 1343 | test "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 | |||
| 1351 | test "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 | |||
| 1359 | test "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 | |||
| 1369 | test "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 | |||
| 1379 | test "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 | |||
| 1389 | test "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 | |||
| 1402 | test "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 | |||
| 1414 | test "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 | |||
| 1424 | test "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 | |||
| 1434 | test "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 | |||
| 1450 | test "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 | |||
| 1460 | test "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 | |||
| 1470 | test "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 | |||
| 1483 | test "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 | |||
| 1493 | test "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 | |||
| 1512 | test "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 | |||
| 1522 | test "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 | |||
| 1532 | test "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 | |||
| 1544 | test "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 | |||
| 1553 | test "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 | |||
| 1562 | test "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 | |||
| 1570 | test "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 | |||
| 1580 | test "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 | |||
| 1590 | test "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 | |||
| 1602 | test "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 | |||
| 1614 | test "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 | |||
| 1629 | test "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 | |||
| 1644 | test "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 | |||
| 1659 | test "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 | |||
| 1671 | test "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 | |||
| 1683 | test "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 | |||
| 1693 | test "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 | |||
| 1707 | test "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 | |||
| 1727 | test "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 | |||
| 1747 | test "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 | |||
| 1767 | test "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 | |||
| 1787 | test "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 | |||
| 1807 | test "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 | |||
| 1827 | test "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 | |||
| 1847 | test "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 | |||
| 1867 | test "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 | |||
| 1879 | test "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 | |||
| 1891 | test "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 | |||
| 1903 | test "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 | |||
| 1915 | test "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 | |||
| 1922 | test "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 | |||
| 1929 | test "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 | |||
| 1936 | test "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 | |||
| 1943 | test "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 | |||
| 1953 | test "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 | |||
| 1960 | test "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 | |||
| 1969 | test "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 | |||
| 1978 | test "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 | |||
| 1987 | test "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 | |||
| 1996 | test "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 | |||
| 2005 | test "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 | |||
| 2015 | test "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; |
| 132 | pub const complex = @import("complex/index.zig"); | 132 | pub const complex = @import("complex/index.zig"); |
| 133 | pub const Complex = complex.Complex; | 133 | pub const Complex = complex.Complex; |
| 134 | 134 | ||
| 135 | pub const big = @import("big/index.zig"); | ||
| 136 | |||
| 135 | test "math" { | 137 | test "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"); |
| 178 | 180 | ||
| 179 | _ = @import("complex/index.zig"); | 181 | _ = @import("complex/index.zig"); |
| 182 | |||
| 183 | _ = @import("big/index.zig"); | ||
| 180 | } | 184 | } |
| 181 | 185 | ||
| 182 | pub fn min(x: var, y: var) @typeOf(x + y) { | 186 | pub 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 | } |
| 307 | 311 | ||
| 308 | pub fn Log2Int(comptime T: type) type { | 312 | pub 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 | } |
| 311 | 322 | ||
| 312 | test "math overflow functions" { | 323 | test "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 | } |
| 305 | 305 | ||
| 306 | test "mem.indexOf" { | 306 | test "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); |
| 316 | 316 | ||
| 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 | } |
| 322 | 322 | ||
| 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 { |
| 432 | 432 | ||
| 433 | test "mem.split" { | 433 | test "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 | } |
| 440 | 440 |
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 | } |
| 161 | 161 | ||
| 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 | } |
| 180 | 180 | ||
| 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); |
| 214 | 214 | ||
| 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.?); |
| 217 | 217 | ||
| 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 | } |
| 233 | 233 | ||
| 234 | try self.waitUnwrappedWindows(); | 234 | try self.waitUnwrappedWindows(); |
| 235 | return ??self.term; | 235 | return self.term.?; |
| 236 | } | 236 | } |
| 237 | 237 | ||
| 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 | } |
| 243 | 243 | ||
| 244 | self.waitUnwrapped(); | 244 | self.waitUnwrapped(); |
| 245 | return ??self.term; | 245 | return self.term.?; |
| 246 | } | 246 | } |
| 247 | 247 | ||
| 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; |
| 620 | 620 | ||
| 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 | } |
| 631 | 631 |
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 | } |
| 328 | 328 | ||
| 329 | pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { | 329 | pub 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 | } |
| 332 | 332 | ||
| 333 | pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize { | 333 | 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,17 +335,17 @@ pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize { |
| 335 | } | 335 | } |
| 336 | 336 | ||
| 337 | pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { | 337 | pub 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 | } |
| 340 | 340 | ||
| 341 | pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { | 341 | pub 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 | } |
| 346 | 346 | ||
| 347 | pub fn munmap(address: usize, length: usize) usize { | 347 | pub 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 | } |
| 350 | 350 | ||
| 351 | pub fn unlink(path: [*]const u8) usize { | 351 | pub 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 | } |
| 98 | 98 | ||
| 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); |
| 102 | 115 | ||
| ... | @@ -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 | } |
| 129 | 142 | ||
| ... | @@ -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: |
| 422 | 422 | ||
| 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 | } |
| 427 | 427 | ||
| 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_path | 429 | // PATH.len because it is >= the largest search_path |
| 430 | // +1 for the / to join the search path and exe_path | 430 | // +1 for the / to join the search path and exe_path |
| 431 | // +1 for the null terminating byte | 431 | // +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(); |
| 491 | 491 | ||
| 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); |
| 495 | 495 | ||
| 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 PathAlreadyExists | 714 | else => return err, // TODO zig should know this set does not include PathAlreadyExists |
| 715 | } | 715 | } |
| 716 | 716 | ||
| 717 | const dirname = os.path.dirname(new_path); | 717 | const dirname = os.path.dirname(new_path) orelse "."; |
| 718 | 718 | ||
| 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 | } |
| 736 | 736 | ||
| 737 | pub fn deleteFile(allocator: *Allocator, file_path: []const u8) !void { | 737 | pub 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 | |||
| 753 | pub 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 { |
| 844 | 860 | ||
| 845 | var rand_buf: [12]u8 = undefined; | 861 | var rand_buf: [12]u8 = undefined; |
| 846 | 862 | ||
| 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); |
| 851 | 867 | ||
| 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 | } |
| 856 | 872 | ||
| 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 | } |
| 1021 | 1037 | ||
| 1038 | pub 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 ::deleteTree | 1054 | /// To delete a directory recursively, see ::deleteTree |
| 1024 | pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) !void { | 1055 | pub 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); |
| 1027 | 1058 | ||
| 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; |
| 1030 | 1061 | ||
| 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 | } |
| 1048 | 1094 | ||
| 1049 | /// Whether ::full_path describes a symlink, file, or directory, this function | 1095 | /// 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 | ||
| 1053 | const DeleteTreeError = error{ | 1098 | const 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); |
| 1133 | 1178 | ||
| 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 | } |
| 1140 | 1185 | ||
| 1141 | pub const Dir = struct { | 1186 | pub 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, | ||
| 1148 | 1189 | ||
| 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 | }; |
| 1153 | 1211 | ||
| 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 | }; |
| 1170 | 1228 | ||
| 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 | } |
| 1196 | 1288 | ||
| 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 | } |
| 1201 | 1301 | ||
| 1202 | /// Memory such as file names referenced in this returned entry becomes invalid | 1302 | /// 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 | } |
| 1212 | 1312 | ||
| 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 | } |
| 1219 | 1319 | ||
| 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; |
| 1242 | 1342 | ||
| 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]; |
| 1244 | 1344 | ||
| 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 | } |
| 1267 | 1366 | ||
| 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 | } |
| 1271 | 1391 | ||
| 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 | } |
| 1278 | 1398 | ||
| 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; |
| 1301 | 1421 | ||
| 1302 | const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name)); | 1422 | const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name)); |
| 1303 | 1423 | ||
| ... | @@ -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 | } |
| 1308 | 1428 | ||
| 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 | } |
| 1647 | 1767 | ||
| ... | @@ -1729,7 +1849,7 @@ test "windows arg parsing" { | ... | @@ -1729,7 +1849,7 @@ test "windows arg parsing" { |
| 1729 | fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void { | 1849 | fn 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 | }; |
| 2459 | 2579 | ||
| 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; |
| 2469 | 2589 | ||
| 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 |
| 2533 | 2653 | ||
| 2534 | // align to page | 2654 | // 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); |
| 2537 | 2657 | ||
| 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; |
| 33 | 33 | ||
| 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 | } |
| 54 | 54 | ||
| 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; |
| 59 | 59 | ||
| 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 | } |
| 183 | 183 | ||
| 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 | } |
| 201 | 201 | ||
| 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}); |
| 266 | 266 | ||
| 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 | } |
| 270 | 270 | ||
| 271 | fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8) bool { | 271 | fn 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}); |
| 287 | 287 | ||
| 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().?; |
| 419 | 419 | ||
| 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 | } |
| 649 | 649 | ||
| 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 |
| 652 | pub fn dirname(path: []const u8) []const u8 { | 652 | pub 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 | } |
| 659 | 659 | ||
| 660 | pub fn dirnameWindows(path: []const u8) []const u8 { | 660 | pub 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; |
| 663 | 663 | ||
| 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 { |
| 671 | 671 | ||
| 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 | } |
| 677 | 677 | ||
| 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 | } |
| 683 | 683 | ||
| ... | @@ -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 | } |
| 687 | 687 | ||
| 688 | if (end_index == 0) | ||
| 689 | return null; | ||
| 690 | |||
| 688 | return path[0..end_index]; | 691 | return path[0..end_index]; |
| 689 | } | 692 | } |
| 690 | 693 | ||
| 691 | pub fn dirnamePosix(path: []const u8) []const u8 { | 694 | pub 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; |
| 694 | 697 | ||
| 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 { |
| 701 | 704 | ||
| 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 | } |
| 707 | 710 | ||
| 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]; |
| 710 | 713 | ||
| 714 | if (end_index == 0) | ||
| 715 | return null; | ||
| 716 | |||
| 711 | return path[0..end_index]; | 717 | return path[0..end_index]; |
| 712 | } | 718 | } |
| 713 | 719 | ||
| ... | @@ -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 | } |
| 725 | 731 | ||
| 726 | test "os.path.dirnameWindows" { | 732 | test "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 | } |
| 761 | 767 | ||
| 762 | fn testDirnamePosix(input: []const u8, expected_output: []const u8) void { | 768 | fn 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 | } |
| 765 | 775 | ||
| 766 | fn testDirnameWindows(input: []const u8, expected_output: []const u8) void { | 776 | fn 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 | } |
| 769 | 783 | ||
| 770 | pub fn basename(path: []const u8) []const u8 { | 784 | pub 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; |
| 10 | const AtomicOrder = builtin.AtomicOrder; | 10 | const AtomicOrder = builtin.AtomicOrder; |
| 11 | 11 | ||
| 12 | test "makePath, put some files in it, deleteTree" { | 12 | test "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 | } |
| 28 | 23 | ||
| 29 | test "access file" { | 24 | test "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) { |
| 68 | fn milliTimestampWindows() u64 { | 68 | fn milliTimestampWindows() u64 { |
| 69 | //FileTime has a granularity of 100 nanoseconds | 69 | //FileTime has a granularity of 100 nanoseconds |
| 70 | // and uses the NTFS/Windows epoch | 70 | // 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 | } |
| 77 | 79 | ||
| 78 | fn milliTimestampDarwin() u64 { | 80 | fn milliTimestampDarwin() u64 { |
std/os/windows/index.zig+52-14| ... | @@ -1,3 +1,7 @@ | ... | @@ -1,3 +1,7 @@ |
| 1 | test "import" { | ||
| 2 | _ = @import("util.zig"); | ||
| 3 | } | ||
| 4 | |||
| 1 | pub const ERROR = @import("error.zig"); | 5 | pub const ERROR = @import("error.zig"); |
| 2 | 6 | ||
| 3 | pub extern "advapi32" stdcallcc fn CryptAcquireContextA( | 7 | pub 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; |
| 61 | 65 | ||
| 62 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; | 66 | pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; |
| 63 | 67 | ||
| 68 | pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE; | ||
| 69 | pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL; | ||
| 70 | pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL; | ||
| 71 | |||
| 64 | pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; | 72 | pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; |
| 65 | 73 | ||
| 66 | pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; | 74 | pub 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 |
| 77 | 85 | ||
| 78 | pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; | 86 | pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; |
| 79 | 87 | ||
| 88 | pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD; | ||
| 89 | |||
| 80 | pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; | 90 | pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; |
| 81 | 91 | ||
| 82 | pub extern "kernel32" stdcallcc fn GetLastError() DWORD; | 92 | pub 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( |
| 97 | 107 | ||
| 98 | pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; | 108 | pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; |
| 99 | 109 | ||
| 100 | pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?*FILETIME) void; | 110 | pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void; |
| 101 | 111 | ||
| 102 | pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; | 112 | pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; |
| 103 | pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; | 113 | pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; |
| 104 | pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void, dwBytes: SIZE_T) ?[*]c_void; | 114 | pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void; |
| 105 | pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) SIZE_T; | 115 | pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T; |
| 106 | pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) BOOL; | 116 | pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL; |
| 107 | pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; | 117 | pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; |
| 108 | pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; | 118 | pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; |
| 109 | 119 | ||
| 110 | pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; | 120 | pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; |
| 111 | 121 | ||
| 112 | pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?[*]c_void; | 122 | pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void; |
| 113 | 123 | ||
| 114 | pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void) BOOL; | 124 | pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL; |
| 115 | 125 | ||
| 116 | pub extern "kernel32" stdcallcc fn MoveFileExA( | 126 | pub 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: * |
| 123 | 133 | ||
| 124 | pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; | 134 | pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; |
| 125 | 135 | ||
| 126 | pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL; | ||
| 127 | |||
| 128 | pub extern "kernel32" stdcallcc fn ReadFile( | 136 | pub 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; |
| 135 | 143 | ||
| 144 | pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL; | ||
| 145 | |||
| 136 | pub extern "kernel32" stdcallcc fn SetFilePointerEx( | 146 | pub 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 |
| 150 | 160 | ||
| 151 | pub extern "kernel32" stdcallcc fn WriteFile( | 161 | pub 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; |
| 163 | 173 | ||
| 164 | pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; | 174 | pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; |
| 165 | 175 | ||
| 176 | pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL; | ||
| 177 | |||
| 166 | pub const PROV_RSA_FULL = 1; | 178 | pub const PROV_RSA_FULL = 1; |
| 167 | 179 | ||
| 168 | pub const BOOL = c_int; | 180 | pub const BOOL = c_int; |
| ... | @@ -196,7 +208,6 @@ pub const UNICODE = false; | ... | @@ -196,7 +208,6 @@ pub const UNICODE = false; |
| 196 | pub const WCHAR = u16; | 208 | pub const WCHAR = u16; |
| 197 | pub const WORD = u16; | 209 | pub const WORD = u16; |
| 198 | pub const LARGE_INTEGER = i64; | 210 | pub const LARGE_INTEGER = i64; |
| 199 | pub const FILETIME = i64; | ||
| 200 | 211 | ||
| 201 | pub const TRUE = 1; | 212 | pub const TRUE = 1; |
| 202 | pub const FALSE = 0; | 213 | pub 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; |
| 212 | 223 | ||
| 213 | pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, @maxValue(usize)); | 224 | pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, @maxValue(usize)); |
| 214 | 225 | ||
| 226 | pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD)); | ||
| 227 | |||
| 215 | pub const OVERLAPPED = extern struct { | 228 | pub 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; |
| 293 | pub const TRUNCATE_EXISTING = 5; | 306 | pub const TRUNCATE_EXISTING = 5; |
| 294 | 307 | ||
| 295 | pub const FILE_ATTRIBUTE_ARCHIVE = 0x20; | 308 | pub const FILE_ATTRIBUTE_ARCHIVE = 0x20; |
| 309 | pub const FILE_ATTRIBUTE_COMPRESSED = 0x800; | ||
| 310 | pub const FILE_ATTRIBUTE_DEVICE = 0x40; | ||
| 311 | pub const FILE_ATTRIBUTE_DIRECTORY = 0x10; | ||
| 296 | pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000; | 312 | pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000; |
| 297 | pub const FILE_ATTRIBUTE_HIDDEN = 0x2; | 313 | pub const FILE_ATTRIBUTE_HIDDEN = 0x2; |
| 314 | pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000; | ||
| 298 | pub const FILE_ATTRIBUTE_NORMAL = 0x80; | 315 | pub const FILE_ATTRIBUTE_NORMAL = 0x80; |
| 316 | pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000; | ||
| 317 | pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000; | ||
| 299 | pub const FILE_ATTRIBUTE_OFFLINE = 0x1000; | 318 | pub const FILE_ATTRIBUTE_OFFLINE = 0x1000; |
| 300 | pub const FILE_ATTRIBUTE_READONLY = 0x1; | 319 | pub const FILE_ATTRIBUTE_READONLY = 0x1; |
| 320 | pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000; | ||
| 321 | pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000; | ||
| 322 | pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400; | ||
| 323 | pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200; | ||
| 301 | pub const FILE_ATTRIBUTE_SYSTEM = 0x4; | 324 | pub const FILE_ATTRIBUTE_SYSTEM = 0x4; |
| 302 | pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; | 325 | pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; |
| 326 | pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000; | ||
| 303 | 327 | ||
| 304 | pub const PROCESS_INFORMATION = extern struct { | 328 | pub 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; |
| 372 | pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; | 396 | pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; |
| 373 | pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; | 397 | pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; |
| 374 | 398 | ||
| 375 | test "import" { | 399 | pub 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 | |||
| 412 | pub 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 | }; |
| 43 | 43 | ||
| 44 | pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { | 44 | pub 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) |
| 153 | pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE { | 153 | pub 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 | } |
| 158 | 158 | ||
| 159 | pub fn windowsUnloadDll(hModule: windows.HMODULE) void { | 159 | pub 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 | |||
| 174 | pub 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. | ||
| 202 | pub 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 | } |
| 366 | 366 | ||
| 367 | assert(??list.pop() == 100); | 367 | assert(list.pop().? == 100); |
| 368 | assert(list.len == 99); | 368 | assert(list.len == 99); |
| 369 | 369 | ||
| 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); |
| 380 | 380 | ||
| 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 { |
| 51 | 51 | ||
| 52 | // TODO https://github.com/ziglang/zig/issues/265 | 52 | // TODO https://github.com/ziglang/zig/issues/265 |
| 53 | fn posixCallMainAndExit() noreturn { | 53 | fn 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); |
| 56 | 56 | ||
| 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 name | 27 | // skip my own exe name |
| 28 | _ = arg_it.skip(); | 28 | _ = arg_it.skip(); |
| 29 | 29 | ||
| 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 { |
| 19 | 19 | ||
| 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; |
| 23 | 23 | ||
| 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) ?[*] |
| 29 | 29 | ||
| 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]; |
| 33 | 33 | ||
| 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 | } |
| 52 | 52 |
std/special/compiler_rt/divti3.zig created+26| ... | @@ -0,0 +1,26 @@ | ||
| 1 | const udivmod = @import("udivmod.zig").udivmod; | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const compiler_rt = @import("index.zig"); | ||
| 4 | |||
| 5 | pub 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 | |||
| 19 | pub 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 | |||
| 24 | test "import divti3" { | ||
| 25 | _ = @import("divti3_test.zig"); | ||
| 26 | } | ||
std/special/compiler_rt/divti3_test.zig created+21| ... | @@ -0,0 +1,21 @@ | ||
| 1 | const __divti3 = @import("divti3.zig").__divti3; | ||
| 2 | const assert = @import("std").debug.assert; | ||
| 3 | |||
| 4 | fn test__divti3(a: i128, b: i128, expected: i128) void { | ||
| 5 | const x = __divti3(a, b); | ||
| 6 | assert(x == expected); | ||
| 7 | } | ||
| 8 | |||
| 9 | test "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 @@ | ||
| 1 | const udivmod = @import("udivmod.zig").udivmod; | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const compiler_rt = @import("index.zig"); | ||
| 4 | |||
| 5 | pub 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 | |||
| 48 | pub 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 | |||
| 53 | test "import muloti4" { | ||
| 54 | _ = @import("muloti4_test.zig"); | ||
| 55 | } | ||
std/special/compiler_rt/muloti4_test.zig created+76| ... | @@ -0,0 +1,76 @@ | ||
| 1 | const __muloti4 = @import("muloti4.zig").__muloti4; | ||
| 2 | const assert = @import("std").debug.assert; | ||
| 3 | |||
| 4 | fn 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 | |||
| 10 | test "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 | } |
| 221 | 221 | ||
| 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; |
| 224 | 224 | ||
| 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"); |
| 287 | 287 | ||
| 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); |
| 293 | 293 | ||
| 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 | } |
| 300 | 300 | ||
| ... | @@ -321,15 +321,15 @@ fn testUtf8ViewOk() void { | ... | @@ -321,15 +321,15 @@ fn testUtf8ViewOk() void { |
| 321 | const s = Utf8View.initComptime("東京市"); | 321 | const s = Utf8View.initComptime("東京市"); |
| 322 | 322 | ||
| 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); |
| 328 | 328 | ||
| 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 | } |
| 335 | 335 |
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; |
| 735 | 735 | ||
| 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 | } |
| 740 | 740 | ||
| ... | @@ -1243,7 +1243,7 @@ pub const Node = struct { | ... | @@ -1243,7 +1243,7 @@ pub const Node = struct { |
| 1243 | i -= 1; | 1243 | i -= 1; |
| 1244 | 1244 | ||
| 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 | } |
| 1249 | 1249 | ||
| ... | @@ -1296,7 +1296,7 @@ pub const Node = struct { | ... | @@ -1296,7 +1296,7 @@ pub const Node = struct { |
| 1296 | i -= 1; | 1296 | i -= 1; |
| 1297 | 1297 | ||
| 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 | } |
| 1302 | 1302 | ||
| ... | @@ -1347,7 +1347,7 @@ pub const Node = struct { | ... | @@ -1347,7 +1347,7 @@ pub const Node = struct { |
| 1347 | i -= 1; | 1347 | i -= 1; |
| 1348 | 1348 | ||
| 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 | } |
| 1353 | 1353 | ||
| ... | @@ -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 | }; |
| 1422 | 1422 | ||
| 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 | } |
| 1481 | 1481 | ||
| ... | @@ -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 | }; |
| 1519 | 1518 | ||
| 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; |
| 1538 | 1537 | ||
| 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 | } |
| 1569 | 1571 | ||
| ... | @@ -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, | ||
| 1622 | 1625 | ||
| 1623 | pub const InitList = SegmentedList(*Node, 2); | 1626 | pub const InitList = SegmentedList(*Node, 2); |
| 1624 | 1627 | ||
| ... | @@ -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 | } |
| 1672 | 1677 | ||
| 1673 | return null; | 1678 | return null; |
| ... | @@ -2022,7 +2027,7 @@ pub const Node = struct { | ... | @@ -2022,7 +2027,7 @@ pub const Node = struct { |
| 2022 | 2027 | ||
| 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; |
| 2094 | 2099 | ||
| 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; |
| 2097 | 2102 | ||
| 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; |
| 2100 | 2105 | ||
| 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 | |||
| 2214 | test "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 { |
| 43 | 43 | ||
| 44 | // skip over line comments at the top of the file | 44 | // 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 fixed | 712 | // 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; |
| 1445 | 1445 | ||
| 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/1022 | 1569 | // 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.?; |
| 1571 | 1571 | ||
| 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.?; |
| 1608 | 1608 | ||
| 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; |
| 1818 | 1818 | ||
| 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 | }, |
| 1837 | 1837 | ||
| 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; |
| 1840 | 1840 | ||
| 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 | }, |
| 1867 | 1867 | ||
| 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; |
| 1870 | 1870 | ||
| 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 | }, |
| 1902 | 1902 | ||
| 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; |
| 1905 | 1905 | ||
| 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 | }, |
| 1926 | 1926 | ||
| 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; |
| 1929 | 1929 | ||
| 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 | }, |
| 1950 | 1950 | ||
| 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; |
| 1953 | 1953 | ||
| 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 | }, |
| 1980 | 1980 | ||
| 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; |
| 1983 | 1983 | ||
| 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 | }, |
| 2004 | 2004 | ||
| 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; |
| 2007 | 2007 | ||
| 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 | }, |
| 2028 | 2028 | ||
| 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; |
| 2031 | 2031 | ||
| 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 | }, |
| 2052 | 2052 | ||
| 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; |
| 2055 | 2055 | ||
| 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 | }, |
| 2082 | 2082 | ||
| 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; |
| 2085 | 2085 | ||
| 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 | }, |
| 2112 | 2112 | ||
| 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; |
| 2115 | 2115 | ||
| 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 | }, |
| 2143 | 2143 | ||
| 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; |
| 2146 | 2146 | ||
| 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 | }, |
| 2191 | 2191 | ||
| 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; |
| 2194 | 2194 | ||
| 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 | }, |
| 2271 | 2271 | ||
| 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; |
| 2274 | 2274 | ||
| 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 { |
| 3237 | fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { | 3248 | fn 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 | } |
| 3323 | 3333 | ||
| 3324 | fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType(Token.Id)) ?TokenIndex { | 3334 | fn 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().?; |
| 3326 | 3336 | ||
| 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( |
| 3334 | fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedToken { | 3344 | fn 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); |
| 3340 | 3350 | ||
| 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 |
| 3347 | 3357 | ||
| 3348 | fn prevToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) void { | 3358 | fn 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 | } |
| 652 | 652 | ||
| 653 | test "zig fmt: ptr deref operator" { | 653 | test "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" { |
| 1209 | test "zig fmt: prefix operators" { | 1210 | test "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 comment | 87 | // 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 | } |
| 224 | 224 | ||
| 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( |
| 513 | 512 | ||
| 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().?; |
| 517 | 516 | ||
| 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 | }, |
| 561 | 560 | ||
| 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 | }, |
| 567 | 566 | ||
| 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 | } |
| 596 | 595 | ||
| 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).?; |
| 599 | 598 | ||
| 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 | } |
| 808 | 807 | ||
| 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 | }, |
| 811 | 810 | ||
| 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); |
| 1251 | 1250 | ||
| ... | @@ -1550,7 +1549,7 @@ fn renderExpression( | ... | @@ -1550,7 +1549,7 @@ fn renderExpression( |
| 1550 | 1549 | ||
| 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; |
| 1555 | 1554 | ||
| 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( |
| 1588 | 1587 | ||
| 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; |
| 1593 | 1592 | ||
| 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( |
| 1620 | 1619 | ||
| 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().?; |
| 1624 | 1623 | ||
| 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 | } |
| 2023 | 2022 | ||
| 2024 | fn nodeCausesSliceOpSpace(base: *ast.Node) bool { | 2023 | fn 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 | }, |
| 498 | 500 | ||
| 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 | } |
| 118 | 118 | ||
| 119 | test "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 | |||
| 119 | test "single-item pointer to array indexing and slicing" { | 128 | test "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 | }; |
| 11 | 11 | ||
| 12 | test "nullable if after an if in a switch prong of a switch with 2 prongs in an else" { | 12 | test "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 | } |
| 15 | 15 |
test/cases/cast.zig+51-29| ... | @@ -1,5 +1,6 @@ | ... | @@ -1,5 +1,6 @@ |
| 1 | const assert = @import("std").debug.assert; | 1 | const std = @import("std"); |
| 2 | const mem = @import("std").mem; | 2 | const assert = std.debug.assert; |
| 3 | const mem = std.mem; | ||
| 3 | 4 | ||
| 4 | test "int to ptr cast" { | 5 | test "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 { |
| 72 | 73 | ||
| 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 { |
| 86 | 87 | ||
| 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 | }; |
| 92 | 93 | ||
| ... | @@ -99,7 +100,7 @@ const Enum = enum { | ... | @@ -99,7 +100,7 @@ const Enum = enum { |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 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 | }; |
| 105 | 106 | ||
| ... | @@ -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 | } |
| 177 | 178 | ||
| 178 | test "implicitly cast from T to error!?T" { | 179 | test "implicitly cast from T to error!?T" { |
| 179 | castToMaybeTypeError(1); | 180 | castToOptionalTypeError(1); |
| 180 | comptime castToMaybeTypeError(1); | 181 | comptime castToOptionalTypeError(1); |
| 181 | } | 182 | } |
| 182 | const A = struct { | 183 | const A = struct { |
| 183 | a: i32, | 184 | a: i32, |
| 184 | }; | 185 | }; |
| 185 | fn castToMaybeTypeError(z: i32) void { | 186 | fn 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); |
| 189 | 190 | ||
| 190 | const f = z; | 191 | const f = z; |
| 191 | const g: error!?i32 = f; | 192 | const g: error!?i32 = f; |
| 192 | 193 | ||
| 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 | } |
| 197 | 198 | ||
| 198 | test "implicitly cast from int to error!?T" { | 199 | test "implicitly cast from int to error!?T" { |
| 199 | implicitIntLitToMaybe(); | 200 | implicitIntLitToOptional(); |
| 200 | comptime implicitIntLitToMaybe(); | 201 | comptime implicitIntLitToOptional(); |
| 201 | } | 202 | } |
| 202 | fn implicitIntLitToMaybe() void { | 203 | fn 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 | } |
| 206 | 207 | ||
| 207 | test "return null from fn() error!?&T" { | 208 | test "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 | } |
| 212 | fn returnNullFromMaybeTypeErrorRef() error!?*A { | 213 | fn returnNullFromOptionalTypeErrorRef() error!?*A { |
| 213 | const a: ?*A = null; | 214 | const a: ?*A = null; |
| 214 | return a; | 215 | return a; |
| 215 | } | 216 | } |
| 216 | fn returnNullLitFromMaybeTypeErrorRef() error!?*A { | 217 | fn returnNullLitFromOptionalTypeErrorRef() error!?*A { |
| 217 | return null; | 218 | return null; |
| 218 | } | 219 | } |
| 219 | 220 | ||
| 220 | test "peer type resolution: ?T and T" { | 221 | test "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 | } |
| 228 | fn peerTypeTAndMaybeT(c: bool, b: bool) ?usize { | 229 | fn 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 | } |
| 251 | 252 | ||
| 252 | test "implicitly cast from [N]T to ?[]const T" { | 253 | test "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 | } |
| 256 | 257 | ||
| 257 | fn castToMaybeSlice() ?[]const u8 { | 258 | fn castToOptionalSlice() ?[]const u8 { |
| 258 | return "hi"; | 259 | return "hi"; |
| 259 | } | 260 | } |
| 260 | 261 | ||
| ... | @@ -384,3 +385,24 @@ test "const slice widen cast" { | ... | @@ -384,3 +385,24 @@ test "const slice widen cast" { |
| 384 | 385 | ||
| 385 | assert(@bitCast(u32, bytes) == 0x12121212); | 386 | assert(@bitCast(u32, bytes) == 0x12121212); |
| 386 | } | 387 | } |
| 388 | |||
| 389 | test "single-item pointer of array to slice and to unknown length pointer" { | ||
| 390 | testCastPtrOfArrayToSliceAndPtr(); | ||
| 391 | comptime testCastPtrOfArrayToSliceAndPtr(); | ||
| 392 | } | ||
| 393 | |||
| 394 | fn 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 | |||
| 404 | test "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 | |||
| 887 | test "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 | } |
| 142 | 142 | ||
| 143 | test "syntax: nullable operator in front of error union operator" { | 143 | test "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 | } |
| 13 | 13 | ||
| 14 | fn unwrapAndAddOne(blah: ?i32) i32 { | 14 | fn unwrapAndAddOne(blah: ?i32) i32 { |
| 15 | return ??blah + 1; | 15 | return blah.? + 1; |
| 16 | } | 16 | } |
| 17 | const should_be_1235 = unwrapAndAddOne(1234); | 17 | const should_be_1235 = unwrapAndAddOne(1234); |
| 18 | test "static add one" { | 18 | test "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 | |||
| 614 | const Wrapper = struct { | ||
| 615 | T: type, | ||
| 616 | }; | ||
| 617 | |||
| 618 | fn wrap(comptime T: type) Wrapper { | ||
| 619 | return Wrapper{ .T = T }; | ||
| 620 | } | ||
| 621 | |||
| 622 | test "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 | } |
| 129 | fn getByte(ptr: ?*const u8) u8 { | 129 | fn getByte(ptr: ?*const u8) u8 { |
| 130 | return (??ptr).*; | 130 | return ptr.?.*; |
| 131 | } | 131 | } |
| 132 | fn getFirstByte(comptime T: type, mem: []const T) u8 { | 132 | fn 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 @@ | ||
| 1 | const A = error{ | ||
| 2 | PathNotFound, | ||
| 3 | NotDir, | ||
| 4 | }; | ||
| 5 | const B = error{OutOfMemory}; | ||
| 6 | |||
| 7 | const C = A || B; | ||
| 8 | |||
| 9 | fn foo() C!void { | ||
| 10 | return error.NotDir; | ||
| 11 | } | ||
| 12 | |||
| 13 | test "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 | } |
| 525 | 525 | ||
| 526 | test "@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 | |||
| 534 | test "@typeName" { | 526 | test "@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 @@ |
| 1 | const assert = @import("std").debug.assert; | 1 | const assert = @import("std").debug.assert; |
| 2 | 2 | ||
| 3 | test "nullable type" { | 3 | test "optional type" { |
| 4 | const x: ?bool = true; | 4 | const x: ?bool = true; |
| 5 | 5 | ||
| 6 | if (x) |y| { | 6 | if (x) |y| { |
| ... | @@ -15,13 +15,13 @@ test "nullable type" { | ... | @@ -15,13 +15,13 @@ test "nullable type" { |
| 15 | 15 | ||
| 16 | const next_x: ?i32 = null; | 16 | const next_x: ?i32 = null; |
| 17 | 17 | ||
| 18 | const z = next_x ?? 1234; | 18 | const z = next_x orelse 1234; |
| 19 | 19 | ||
| 20 | assert(z == 1234); | 20 | assert(z == 1234); |
| 21 | 21 | ||
| 22 | const final_x: ?i32 = 13; | 22 | const final_x: ?i32 = 13; |
| 23 | 23 | ||
| 24 | const num = final_x ?? unreachable; | 24 | const num = final_x orelse unreachable; |
| 25 | 25 | ||
| 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 | } |
| 35 | 35 | ||
| 36 | assert(??maybe_bool == false); | 36 | assert(maybe_bool.? == false); |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | test "rhs maybe unwrap return" { | 39 | test "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 | } |
| 43 | 43 | ||
| 44 | test "maybe return" { | 44 | test "maybe return" { |
| ... | @@ -47,13 +47,13 @@ test "maybe return" { | ... | @@ -47,13 +47,13 @@ test "maybe return" { |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | fn maybeReturnImpl() void { | 49 | fn 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 | } |
| 54 | 54 | ||
| 55 | fn foo(x: ?i32) ?bool { | 55 | fn 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 | } |
| 59 | 59 | ||
| ... | @@ -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 | } |
| 104 | 104 | ||
| 105 | test "nullable void" { | 105 | test "optional void" { |
| 106 | nullableVoidImpl(); | 106 | optionalVoidImpl(); |
| 107 | comptime nullableVoidImpl(); | 107 | comptime optionalVoidImpl(); |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | fn nullableVoidImpl() void { | 110 | fn 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 | } |
| 122 | 122 | ||
| 123 | const StructWithNullable = struct { | 123 | const StructWithOptional = struct { |
| 124 | field: ?i32, | 124 | field: ?i32, |
| 125 | }; | 125 | }; |
| 126 | 126 | ||
| 127 | var struct_with_nullable: StructWithNullable = undefined; | 127 | var struct_with_optional: StructWithOptional = undefined; |
| 128 | 128 | ||
| 129 | test "unwrap nullable which is field of global var" { | 129 | test "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 | } |
| 141 | 141 | ||
| 142 | test "null with default unwrap" { | 142 | test "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 | |||
| 147 | test "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 | |||
| 154 | const 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; |
| 2 | const mem = @import("std").mem; | 2 | const mem = @import("std").mem; |
| 3 | const reflection = this; | 3 | const reflection = this; |
| 4 | 4 | ||
| 5 | test "reflection: array, pointer, nullable, error union type child" { | 5 | test "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) { |
| 421 | fn alloc(comptime T: type) []T { | 421 | fn alloc(comptime T: type) []T { |
| 422 | return []T{}; | 422 | return []T{}; |
| 423 | } | 423 | } |
| 424 | |||
| 425 | test "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" { |
| 39 | fn testPointer() void { | 39 | fn 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 | } |
| 47 | 48 | ||
| 49 | test "type info: unknown length pointer type info" { | ||
| 50 | testUnknownLenPtr(); | ||
| 51 | comptime testUnknownLenPtr(); | ||
| 52 | } | ||
| 53 | |||
| 54 | fn 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 | |||
| 48 | test "type info: slice type info" { | 64 | test "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" { |
| 52 | 68 | ||
| 53 | fn testSlice() void { | 69 | fn 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 | } |
| 61 | 78 | ||
| 62 | test "type info: array type info" { | 79 | test "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 | } |
| 73 | 90 | ||
| 74 | test "type info: nullable type info" { | 91 | test "type info: optional type info" { |
| 75 | testNullable(); | 92 | testOptional(); |
| 76 | comptime testNullable(); | 93 | comptime testOptional(); |
| 77 | } | 94 | } |
| 78 | 95 | ||
| 79 | fn testNullable() void { | 96 | fn 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 | } |
| 84 | 101 | ||
| 85 | test "type info: promise info" { | 102 | test "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); |
| 157 | 174 | ||
| 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 | } |
| 83 | 83 | ||
| 84 | test "while with nullable as condition" { | 84 | test "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 | } |
| 92 | 92 | ||
| 93 | test "while with nullable as condition with else" { | 93 | test "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 | } |
| 134 | 134 | ||
| 135 | test "while on nullable with else result follow else prong" { | 135 | test "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 | } else | 138 | } 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 | } |
| 142 | 142 | ||
| 143 | test "while on nullable with else result follow break prong" { | 143 | test "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 | } else | 146 | } else |
| 147 | i32(2); | 147 | i32(2); |
| ... | @@ -210,7 +210,7 @@ fn testContinueOuter() void { | ... | @@ -210,7 +210,7 @@ fn testContinueOuter() void { |
| 210 | fn returnNull() ?i32 { | 210 | fn returnNull() ?i32 { |
| 211 | return null; | 211 | return null; |
| 212 | } | 212 | } |
| 213 | fn returnMaybe(x: i32) ?i32 { | 213 | fn returnOptional(x: i32) ?i32 { |
| 214 | return x; | 214 | return x; |
| 215 | } | 215 | } |
| 216 | fn returnError() error!i32 { | 216 | fn 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 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 3 | pub 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 | ); |
| 1308 | 1359 | ||
| 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 | ); |
| 1743 | 1794 | ||
| 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 | ); |
| 2780 | 2831 | ||
| 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 | ); |
| 2800 | 2851 | ||
| 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 | ); |
| 2810 | 2861 | ||
| 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 | ); |
| 2820 | 2871 | ||
| 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 | ); |
| 2830 | 2881 | ||
| 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); |
| 284 | 284 | ||
| 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.?); |
| 287 | 287 | ||
| 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); |
| 603 | 603 | ||
| 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.?); |
| 606 | 606 | ||
| 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); |
| 874 | 874 | ||
| 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.?); |
| 877 | 877 | ||
| 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 | ); |
| 104 | 104 | ||
| 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 | ); |
| 238 | 238 | ||
| 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 | ); |
| 258 | 258 | ||
| ... | @@ -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 | ); |
| 614 | 614 | ||
| ... | @@ -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 | ); |
| 660 | 660 | ||
| ... | @@ -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 | ); |
| 990 | 990 | ||
| ... | @@ -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 | ); |
| 1018 | 1018 | ||
| ... | @@ -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; |