| author | |
| committer | |
| log | 93cf9560b13619159efb3ca12eeeb13d6031ad85 |
| tree | 4b5857db65268b6e3a41fc436082da7b100647b6 |
| parent | 86b31394c9d73b0f753918cea4f08ce8d7a26119 |
| parent | 82a31aac9b955213f47ff3b2a2c7eb932fdbe294 |
| signature | Commit is signed but in an unrecognized format. |
78 files changed, 6701 insertions(+), 4013 deletions(-)
ci/azure/macos_arm64_script+1-2| ... | ... | @@ -114,13 +114,12 @@ if [ "${BUILD_REASON}" != "PullRequest" ]; then |
| 114 | 114 | SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1) |
| 115 | 115 | BYTESIZE=$(wc -c < $TARBALL) |
| 116 | 116 | |
| 117 | JSONFILE="macos-$GITBRANCH.json" | |
| 117 | JSONFILE="tarball.json" | |
| 118 | 118 | touch $JSONFILE |
| 119 | 119 | echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE |
| 120 | 120 | echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE |
| 121 | 121 | echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE |
| 122 | 122 | |
| 123 | s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE" | |
| 124 | 123 | s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$ARCH-macos-$VERSION.json" |
| 125 | 124 | |
| 126 | 125 | # `set -x` causes these variables to be mangled. |
ci/azure/pipelines.yml+2-1| ... | ... | @@ -24,7 +24,7 @@ jobs: |
| 24 | 24 | secureFile: s3cfg |
| 25 | 25 | - script: ci/azure/macos_arm64_script |
| 26 | 26 | name: main |
| 27 | displayName: 'Build and cross-compile' | |
| 27 | displayName: 'Build' | |
| 28 | 28 | - job: BuildLinux |
| 29 | 29 | pool: |
| 30 | 30 | vmImage: 'ubuntu-18.04' |
| ... | ... | @@ -66,6 +66,7 @@ jobs: |
| 66 | 66 | - job: OnMasterSuccess |
| 67 | 67 | dependsOn: |
| 68 | 68 | - BuildMacOS |
| 69 | - BuildMacOS_arm64 | |
| 69 | 70 | - BuildLinux |
| 70 | 71 | - BuildWindows |
| 71 | 72 | condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) |
ci/srht/index.html+6| ... | ... | @@ -199,6 +199,12 @@ |
| 199 | 199 | <td>{{X86_64_MACOS_BYTESIZE}}</td> |
| 200 | 200 | <td class="code">{{X86_64_MACOS_SHASUM}}</td> |
| 201 | 201 | </tr> |
| 202 | <tr> | |
| 203 | <td><a href="https://ziglang.org/builds/{{AARCH64_MACOS_TARBALL}}">{{AARCH64_MACOS_TARBALL}}</a></td> | |
| 204 | <td>Binary</td> | |
| 205 | <td>{{AARCH64_MACOS_BYTESIZE}}</td> | |
| 206 | <td class="code">{{AARCH64_MACOS_SHASUM}}</td> | |
| 207 | </tr> | |
| 202 | 208 | <tr> |
| 203 | 209 | <td><a href="https://ziglang.org/builds/{{X86_64_FREEBSD_TARBALL}}">{{X86_64_FREEBSD_TARBALL}}</a></td> |
| 204 | 210 | <td>Binary</td> |
ci/srht/index.json+5| ... | ... | @@ -19,6 +19,11 @@ |
| 19 | 19 | "shasum": "{{X86_64_MACOS_SHASUM}}", |
| 20 | 20 | "size": "{{X86_64_MACOS_BYTESIZE}}" |
| 21 | 21 | }, |
| 22 | "aarch64-macos": { | |
| 23 | "tarball": "https://ziglang.org/builds/{{AARCH64_MACOS_TARBALL}}", | |
| 24 | "shasum": "{{AARCH64_MACOS_SHASUM}}", | |
| 25 | "size": "{{AARCH64_MACOS_BYTESIZE}}" | |
| 26 | }, | |
| 22 | 27 | "x86_64-windows": { |
| 23 | 28 | "tarball": "https://ziglang.org/builds/{{X86_64_WINDOWS_TARBALL}}", |
| 24 | 29 | "shasum": "{{X86_64_WINDOWS_SHASUM}}", |
ci/srht/update_download_page+7| ... | ... | @@ -12,6 +12,7 @@ NATIVE_TARBALL="zig-linux-$(uname -m)-$VERSION.tar.xz" |
| 12 | 12 | AARCH64_LINUX_JSON_URL="https://ziglang.org/builds/aarch64-linux-$VERSION.json" |
| 13 | 13 | X86_64_LINUX_JSON_URL="https://ziglang.org/builds/x86_64-linux-$VERSION.json" |
| 14 | 14 | X86_64_WINDOWS_JSON_URL="https://ziglang.org/builds/x86_64-windows-$VERSION.json" |
| 15 | AARCH64_MACOS_JSON_URL="https://ziglang.org/builds/aarch64-macos-$VERSION.json" | |
| 15 | 16 | X86_64_MACOS_JSON_URL="https://ziglang.org/builds/x86_64-macos-$VERSION.json" |
| 16 | 17 | X86_64_FREEBSD_JSON_URL="https://ziglang.org/builds/x86_64-freebsd-$VERSION.json" |
| 17 | 18 | |
| ... | ... | @@ -20,6 +21,7 @@ X86_64_FREEBSD_JSON_URL="https://ziglang.org/builds/x86_64-freebsd-$VERSION.json |
| 20 | 21 | curl --fail -I "$AARCH64_LINUX_JSON_URL" >/dev/null || exit 0 |
| 21 | 22 | curl --fail -I "$X86_64_LINUX_JSON_URL" >/dev/null || exit 0 |
| 22 | 23 | curl --fail -I "$X86_64_WINDOWS_JSON_URL" >/dev/null || exit 0 |
| 24 | curl --fail -I "$AARCH64_MACOS_JSON_URL" >/dev/null || exit 0 | |
| 23 | 25 | curl --fail -I "$X86_64_MACOS_JSON_URL" >/dev/null || exit 0 |
| 24 | 26 | curl --fail -I "$X86_64_FREEBSD_JSON_URL" >/dev/null || exit 0 |
| 25 | 27 | |
| ... | ... | @@ -57,6 +59,11 @@ export X86_64_WINDOWS_TARBALL="$(echo "$X86_64_WINDOWS_JSON" | jq .tarball -r)" |
| 57 | 59 | export X86_64_WINDOWS_BYTESIZE="$(echo "$X86_64_WINDOWS_JSON" | jq .size -r)" |
| 58 | 60 | export X86_64_WINDOWS_SHASUM="$(echo "$X86_64_WINDOWS_JSON" | jq .shasum -r)" |
| 59 | 61 | |
| 62 | AARCH64_MACOS_JSON=$(curl --fail "$AARCH64_MACOS_JSON_URL" || exit 1) | |
| 63 | export AARCH64_MACOS_TARBALL="$(echo "$AARCH64_MACOS_JSON" | jq .tarball -r)" | |
| 64 | export AARCH64_MACOS_BYTESIZE="$(echo "$AARCH64_MACOS_JSON" | jq .size -r)" | |
| 65 | export AARCH64_MACOS_SHASUM="$(echo "$AARCH64_MACOS_JSON" | jq .shasum -r)" | |
| 66 | ||
| 60 | 67 | X86_64_MACOS_JSON=$(curl --fail "$X86_64_MACOS_JSON_URL" || exit 1) |
| 61 | 68 | export X86_64_MACOS_TARBALL="$(echo "$X86_64_MACOS_JSON" | jq .tarball -r)" |
| 62 | 69 | export X86_64_MACOS_BYTESIZE="$(echo "$X86_64_MACOS_JSON" | jq .size -r)" |
doc/docgen.zig+1-2| ... | ... | @@ -1023,6 +1023,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any |
| 1023 | 1023 | const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe); |
| 1024 | 1024 | |
| 1025 | 1025 | for (toc.nodes) |node| { |
| 1026 | defer root_node.completeOne(); | |
| 1026 | 1027 | switch (node) { |
| 1027 | 1028 | .Content => |data| { |
| 1028 | 1029 | try out.writeAll(data); |
| ... | ... | @@ -1062,8 +1063,6 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: any |
| 1062 | 1063 | try tokenizeAndPrint(tokenizer, out, content_tok); |
| 1063 | 1064 | }, |
| 1064 | 1065 | .Code => |code| { |
| 1065 | root_node.completeOne(); | |
| 1066 | ||
| 1067 | 1066 | const raw_source = tokenizer.buffer[code.source_token.start..code.source_token.end]; |
| 1068 | 1067 | const trimmed_raw_source = mem.trim(u8, raw_source, " \n"); |
| 1069 | 1068 | if (!code.is_inline) { |
doc/langref.html.in+5-1| ... | ... | @@ -85,7 +85,6 @@ |
| 85 | 85 | #main-wrapper { |
| 86 | 86 | display: flex; |
| 87 | 87 | flex-direction: column; |
| 88 | height: 100vh; | |
| 89 | 88 | } |
| 90 | 89 | |
| 91 | 90 | #contents-wrapper { |
| ... | ... | @@ -106,6 +105,11 @@ |
| 106 | 105 | #main-wrapper { |
| 107 | 106 | flex-direction: row; |
| 108 | 107 | } |
| 108 | #toc { | |
| 109 | height: 100vh; | |
| 110 | position: sticky; | |
| 111 | top: 0; | |
| 112 | } | |
| 109 | 113 | #contents-wrapper, #toc { |
| 110 | 114 | overflow: auto; |
| 111 | 115 | } |
lib/std/build.zig+9| ... | ... | @@ -1939,6 +1939,15 @@ pub const LibExeObjStep = struct { |
| 1939 | 1939 | out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable; |
| 1940 | 1940 | return; |
| 1941 | 1941 | }, |
| 1942 | ?[:0]const u8 => { | |
| 1943 | out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable; | |
| 1944 | if (value) |payload| { | |
| 1945 | out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable; | |
| 1946 | } else { | |
| 1947 | out.writeAll("null;\n") catch unreachable; | |
| 1948 | } | |
| 1949 | return; | |
| 1950 | }, | |
| 1942 | 1951 | ?[]const u8 => { |
| 1943 | 1952 | out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable; |
| 1944 | 1953 | if (value) |payload| { |
lib/std/c/netbsd.zig+5-4| ... | ... | @@ -18,14 +18,14 @@ pub extern "c" fn _lwp_self() lwpid_t; |
| 18 | 18 | |
| 19 | 19 | pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int; |
| 20 | 20 | pub extern "c" fn arc4random_buf(buf: [*]u8, len: usize) void; |
| 21 | pub extern "c" fn __fstat50(fd: fd_t, buf: *Stat) c_int; | |
| 22 | pub extern "c" fn __stat50(path: [*:0]const u8, buf: *Stat) c_int; | |
| 21 | pub extern "c" fn __fstat50(fd: fd_t, buf: *libc_stat) c_int; | |
| 22 | pub extern "c" fn __stat50(path: [*:0]const u8, buf: *libc_stat) c_int; | |
| 23 | 23 | pub extern "c" fn __clock_gettime50(clk_id: c_int, tp: *timespec) c_int; |
| 24 | 24 | pub extern "c" fn __clock_getres50(clk_id: c_int, tp: *timespec) c_int; |
| 25 | 25 | pub extern "c" fn __getdents30(fd: c_int, buf_ptr: [*]u8, nbytes: usize) c_int; |
| 26 | 26 | pub extern "c" fn __sigaltstack14(ss: ?*stack_t, old_ss: ?*stack_t) c_int; |
| 27 | 27 | pub extern "c" fn __nanosleep50(rqtp: *const timespec, rmtp: ?*timespec) c_int; |
| 28 | pub extern "c" fn __sigaction14(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; | |
| 28 | pub extern "c" fn __sigaction14(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int; | |
| 29 | 29 | pub extern "c" fn __sigprocmask14(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; |
| 30 | 30 | pub extern "c" fn __socket30(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int; |
| 31 | 31 | pub extern "c" fn __gettimeofday50(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; |
| ... | ... | @@ -34,7 +34,6 @@ pub extern "c" fn __getrusage50(who: c_int, usage: *rusage) c_int; |
| 34 | 34 | pub extern "c" fn __libc_thr_yield() c_int; |
| 35 | 35 | |
| 36 | 36 | pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int; |
| 37 | pub extern "c" fn malloc_usable_size(?*const c_void) usize; | |
| 38 | 37 | |
| 39 | 38 | pub const pthread_mutex_t = extern struct { |
| 40 | 39 | ptm_magic: u32 = 0x33330003, |
| ... | ... | @@ -93,3 +92,5 @@ pub const pthread_attr_t = extern struct { |
| 93 | 92 | pta_flags: i32, |
| 94 | 93 | pta_private: ?*c_void, |
| 95 | 94 | }; |
| 95 | ||
| 96 | pub const sem_t = ?*opaque {}; |
lib/std/debug.zig+1-1| ... | ... | @@ -462,7 +462,7 @@ pub const TTY = struct { |
| 462 | 462 | // TODO give this a payload of file handle |
| 463 | 463 | windows_api, |
| 464 | 464 | |
| 465 | fn setColor(conf: Config, out_stream: anytype, color: Color) void { | |
| 465 | pub fn setColor(conf: Config, out_stream: anytype, color: Color) void { | |
| 466 | 466 | nosuspend switch (conf) { |
| 467 | 467 | .no_color => return, |
| 468 | 468 | .escape_codes => switch (color) { |
lib/std/fs/file.zig+4-4| ... | ... | @@ -223,15 +223,15 @@ pub const File = struct { |
| 223 | 223 | return os.lseek_SET(self.handle, offset); |
| 224 | 224 | } |
| 225 | 225 | |
| 226 | pub const GetPosError = os.SeekError || os.FStatError; | |
| 226 | pub const GetSeekPosError = os.SeekError || os.FStatError; | |
| 227 | 227 | |
| 228 | 228 | /// TODO: integrate with async I/O |
| 229 | pub fn getPos(self: File) GetPosError!u64 { | |
| 229 | pub fn getPos(self: File) GetSeekPosError!u64 { | |
| 230 | 230 | return os.lseek_CUR_get(self.handle); |
| 231 | 231 | } |
| 232 | 232 | |
| 233 | 233 | /// TODO: integrate with async I/O |
| 234 | pub fn getEndPos(self: File) GetPosError!u64 { | |
| 234 | pub fn getEndPos(self: File) GetSeekPosError!u64 { | |
| 235 | 235 | if (builtin.os.tag == .windows) { |
| 236 | 236 | return windows.GetFileSizeEx(self.handle); |
| 237 | 237 | } |
| ... | ... | @@ -819,7 +819,7 @@ pub const File = struct { |
| 819 | 819 | pub const SeekableStream = io.SeekableStream( |
| 820 | 820 | File, |
| 821 | 821 | SeekError, |
| 822 | GetPosError, | |
| 822 | GetSeekPosError, | |
| 823 | 823 | seekTo, |
| 824 | 824 | seekBy, |
| 825 | 825 | getPos, |
lib/std/hash_map.zig-4| ... | ... | @@ -398,10 +398,6 @@ pub fn HashMapUnmanaged( |
| 398 | 398 | return size * 100 < max_load_percentage * cap; |
| 399 | 399 | } |
| 400 | 400 | |
| 401 | pub fn init(allocator: *Allocator) Self { | |
| 402 | return .{}; | |
| 403 | } | |
| 404 | ||
| 405 | 401 | pub fn deinit(self: *Self, allocator: *Allocator) void { |
| 406 | 402 | self.deallocate(allocator); |
| 407 | 403 | self.* = undefined; |
lib/std/io/stream_source.zig+1-2| ... | ... | @@ -5,7 +5,6 @@ |
| 5 | 5 | // and substantial portions of the software. |
| 6 | 6 | const std = @import("../std.zig"); |
| 7 | 7 | const io = std.io; |
| 8 | const testing = std.testing; | |
| 9 | 8 | |
| 10 | 9 | /// Provides `io.Reader`, `io.Writer`, and `io.SeekableStream` for in-memory buffers as |
| 11 | 10 | /// well as files. |
| ... | ... | @@ -19,7 +18,7 @@ pub const StreamSource = union(enum) { |
| 19 | 18 | pub const ReadError = std.fs.File.ReadError; |
| 20 | 19 | pub const WriteError = std.fs.File.WriteError; |
| 21 | 20 | pub const SeekError = std.fs.File.SeekError; |
| 22 | pub const GetSeekPosError = std.fs.File.GetPosError; | |
| 21 | pub const GetSeekPosError = std.fs.File.GetSeekPosError; | |
| 23 | 22 | |
| 24 | 23 | pub const Reader = io.Reader(*StreamSource, ReadError, read); |
| 25 | 24 | pub const Writer = io.Writer(*StreamSource, WriteError, write); |
lib/std/json.zig+2-2| ... | ... | @@ -1234,7 +1234,7 @@ test "json.validate" { |
| 1234 | 1234 | const Allocator = std.mem.Allocator; |
| 1235 | 1235 | const ArenaAllocator = std.heap.ArenaAllocator; |
| 1236 | 1236 | const ArrayList = std.ArrayList; |
| 1237 | const StringHashMap = std.StringHashMap; | |
| 1237 | const StringArrayHashMap = std.StringArrayHashMap; | |
| 1238 | 1238 | |
| 1239 | 1239 | pub const ValueTree = struct { |
| 1240 | 1240 | arena: ArenaAllocator, |
| ... | ... | @@ -1245,7 +1245,7 @@ pub const ValueTree = struct { |
| 1245 | 1245 | } |
| 1246 | 1246 | }; |
| 1247 | 1247 | |
| 1248 | pub const ObjectMap = StringHashMap(Value); | |
| 1248 | pub const ObjectMap = StringArrayHashMap(Value); | |
| 1249 | 1249 | pub const Array = ArrayList(Value); |
| 1250 | 1250 | |
| 1251 | 1251 | /// Represents a JSON value |
lib/std/json/test.zig+119-103| ... | ... | @@ -12,7 +12,7 @@ const std = @import("../std.zig"); |
| 12 | 12 | const json = std.json; |
| 13 | 13 | const testing = std.testing; |
| 14 | 14 | |
| 15 | fn testNonStreaming(comptime s: []const u8) !void { | |
| 15 | fn testNonStreaming(s: []const u8) !void { | |
| 16 | 16 | var p = json.Parser.init(testing.allocator, false); |
| 17 | 17 | defer p.deinit(); |
| 18 | 18 | |
| ... | ... | @@ -20,44 +20,60 @@ fn testNonStreaming(comptime s: []const u8) !void { |
| 20 | 20 | defer tree.deinit(); |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | fn ok(comptime s: []const u8) void { | |
| 23 | fn ok(s: []const u8) !void { | |
| 24 | 24 | testing.expect(json.validate(s)); |
| 25 | 25 | |
| 26 | testNonStreaming(s) catch testing.expect(false); | |
| 26 | try testNonStreaming(s); | |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | fn err(comptime s: []const u8) void { | |
| 29 | fn err(s: []const u8) void { | |
| 30 | 30 | testing.expect(!json.validate(s)); |
| 31 | 31 | |
| 32 | 32 | testNonStreaming(s) catch return; |
| 33 | 33 | testing.expect(false); |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | fn utf8Error(comptime s: []const u8) void { | |
| 36 | fn utf8Error(s: []const u8) void { | |
| 37 | 37 | testing.expect(!json.validate(s)); |
| 38 | 38 | |
| 39 | 39 | testing.expectError(error.InvalidUtf8Byte, testNonStreaming(s)); |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | fn any(comptime s: []const u8) void { | |
| 42 | fn any(s: []const u8) void { | |
| 43 | 43 | _ = json.validate(s); |
| 44 | 44 | |
| 45 | 45 | testNonStreaming(s) catch {}; |
| 46 | 46 | } |
| 47 | 47 | |
| 48 | fn anyStreamingErrNonStreaming(comptime s: []const u8) void { | |
| 48 | fn anyStreamingErrNonStreaming(s: []const u8) void { | |
| 49 | 49 | _ = json.validate(s); |
| 50 | 50 | |
| 51 | 51 | testNonStreaming(s) catch return; |
| 52 | 52 | testing.expect(false); |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | fn roundTrip(s: []const u8) !void { | |
| 56 | testing.expect(json.validate(s)); | |
| 57 | ||
| 58 | var p = json.Parser.init(testing.allocator, false); | |
| 59 | defer p.deinit(); | |
| 60 | ||
| 61 | var tree = try p.parse(s); | |
| 62 | defer tree.deinit(); | |
| 63 | ||
| 64 | var buf: [256]u8 = undefined; | |
| 65 | var fbs = std.io.fixedBufferStream(&buf); | |
| 66 | try tree.root.jsonStringify(.{}, fbs.writer()); | |
| 67 | ||
| 68 | testing.expectEqualStrings(s, fbs.getWritten()); | |
| 69 | } | |
| 70 | ||
| 55 | 71 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 56 | 72 | // |
| 57 | 73 | // Additional tests not part of test JSONTestSuite. |
| 58 | 74 | |
| 59 | 75 | test "y_trailing_comma_after_empty" { |
| 60 | ok( | |
| 76 | try roundTrip( | |
| 61 | 77 | \\{"1":[],"2":{},"3":"4"} |
| 62 | 78 | ); |
| 63 | 79 | } |
| ... | ... | @@ -65,252 +81,252 @@ test "y_trailing_comma_after_empty" { |
| 65 | 81 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 66 | 82 | |
| 67 | 83 | test "y_array_arraysWithSpaces" { |
| 68 | ok( | |
| 84 | try ok( | |
| 69 | 85 | \\[[] ] |
| 70 | 86 | ); |
| 71 | 87 | } |
| 72 | 88 | |
| 73 | 89 | test "y_array_empty" { |
| 74 | ok( | |
| 90 | try roundTrip( | |
| 75 | 91 | \\[] |
| 76 | 92 | ); |
| 77 | 93 | } |
| 78 | 94 | |
| 79 | 95 | test "y_array_empty-string" { |
| 80 | ok( | |
| 96 | try roundTrip( | |
| 81 | 97 | \\[""] |
| 82 | 98 | ); |
| 83 | 99 | } |
| 84 | 100 | |
| 85 | 101 | test "y_array_ending_with_newline" { |
| 86 | ok( | |
| 102 | try roundTrip( | |
| 87 | 103 | \\["a"] |
| 88 | 104 | ); |
| 89 | 105 | } |
| 90 | 106 | |
| 91 | 107 | test "y_array_false" { |
| 92 | ok( | |
| 108 | try roundTrip( | |
| 93 | 109 | \\[false] |
| 94 | 110 | ); |
| 95 | 111 | } |
| 96 | 112 | |
| 97 | 113 | test "y_array_heterogeneous" { |
| 98 | ok( | |
| 114 | try ok( | |
| 99 | 115 | \\[null, 1, "1", {}] |
| 100 | 116 | ); |
| 101 | 117 | } |
| 102 | 118 | |
| 103 | 119 | test "y_array_null" { |
| 104 | ok( | |
| 120 | try roundTrip( | |
| 105 | 121 | \\[null] |
| 106 | 122 | ); |
| 107 | 123 | } |
| 108 | 124 | |
| 109 | 125 | test "y_array_with_1_and_newline" { |
| 110 | ok( | |
| 126 | try ok( | |
| 111 | 127 | \\[1 |
| 112 | 128 | \\] |
| 113 | 129 | ); |
| 114 | 130 | } |
| 115 | 131 | |
| 116 | 132 | test "y_array_with_leading_space" { |
| 117 | ok( | |
| 133 | try ok( | |
| 118 | 134 | \\ [1] |
| 119 | 135 | ); |
| 120 | 136 | } |
| 121 | 137 | |
| 122 | 138 | test "y_array_with_several_null" { |
| 123 | ok( | |
| 139 | try roundTrip( | |
| 124 | 140 | \\[1,null,null,null,2] |
| 125 | 141 | ); |
| 126 | 142 | } |
| 127 | 143 | |
| 128 | 144 | test "y_array_with_trailing_space" { |
| 129 | ok("[2] "); | |
| 145 | try ok("[2] "); | |
| 130 | 146 | } |
| 131 | 147 | |
| 132 | 148 | test "y_number_0e+1" { |
| 133 | ok( | |
| 149 | try ok( | |
| 134 | 150 | \\[0e+1] |
| 135 | 151 | ); |
| 136 | 152 | } |
| 137 | 153 | |
| 138 | 154 | test "y_number_0e1" { |
| 139 | ok( | |
| 155 | try ok( | |
| 140 | 156 | \\[0e1] |
| 141 | 157 | ); |
| 142 | 158 | } |
| 143 | 159 | |
| 144 | 160 | test "y_number_after_space" { |
| 145 | ok( | |
| 161 | try ok( | |
| 146 | 162 | \\[ 4] |
| 147 | 163 | ); |
| 148 | 164 | } |
| 149 | 165 | |
| 150 | 166 | test "y_number_double_close_to_zero" { |
| 151 | ok( | |
| 167 | try ok( | |
| 152 | 168 | \\[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] |
| 153 | 169 | ); |
| 154 | 170 | } |
| 155 | 171 | |
| 156 | 172 | test "y_number_int_with_exp" { |
| 157 | ok( | |
| 173 | try ok( | |
| 158 | 174 | \\[20e1] |
| 159 | 175 | ); |
| 160 | 176 | } |
| 161 | 177 | |
| 162 | 178 | test "y_number" { |
| 163 | ok( | |
| 179 | try ok( | |
| 164 | 180 | \\[123e65] |
| 165 | 181 | ); |
| 166 | 182 | } |
| 167 | 183 | |
| 168 | 184 | test "y_number_minus_zero" { |
| 169 | ok( | |
| 185 | try ok( | |
| 170 | 186 | \\[-0] |
| 171 | 187 | ); |
| 172 | 188 | } |
| 173 | 189 | |
| 174 | 190 | test "y_number_negative_int" { |
| 175 | ok( | |
| 191 | try roundTrip( | |
| 176 | 192 | \\[-123] |
| 177 | 193 | ); |
| 178 | 194 | } |
| 179 | 195 | |
| 180 | 196 | test "y_number_negative_one" { |
| 181 | ok( | |
| 197 | try roundTrip( | |
| 182 | 198 | \\[-1] |
| 183 | 199 | ); |
| 184 | 200 | } |
| 185 | 201 | |
| 186 | 202 | test "y_number_negative_zero" { |
| 187 | ok( | |
| 203 | try ok( | |
| 188 | 204 | \\[-0] |
| 189 | 205 | ); |
| 190 | 206 | } |
| 191 | 207 | |
| 192 | 208 | test "y_number_real_capital_e" { |
| 193 | ok( | |
| 209 | try ok( | |
| 194 | 210 | \\[1E22] |
| 195 | 211 | ); |
| 196 | 212 | } |
| 197 | 213 | |
| 198 | 214 | test "y_number_real_capital_e_neg_exp" { |
| 199 | ok( | |
| 215 | try ok( | |
| 200 | 216 | \\[1E-2] |
| 201 | 217 | ); |
| 202 | 218 | } |
| 203 | 219 | |
| 204 | 220 | test "y_number_real_capital_e_pos_exp" { |
| 205 | ok( | |
| 221 | try ok( | |
| 206 | 222 | \\[1E+2] |
| 207 | 223 | ); |
| 208 | 224 | } |
| 209 | 225 | |
| 210 | 226 | test "y_number_real_exponent" { |
| 211 | ok( | |
| 227 | try ok( | |
| 212 | 228 | \\[123e45] |
| 213 | 229 | ); |
| 214 | 230 | } |
| 215 | 231 | |
| 216 | 232 | test "y_number_real_fraction_exponent" { |
| 217 | ok( | |
| 233 | try ok( | |
| 218 | 234 | \\[123.456e78] |
| 219 | 235 | ); |
| 220 | 236 | } |
| 221 | 237 | |
| 222 | 238 | test "y_number_real_neg_exp" { |
| 223 | ok( | |
| 239 | try ok( | |
| 224 | 240 | \\[1e-2] |
| 225 | 241 | ); |
| 226 | 242 | } |
| 227 | 243 | |
| 228 | 244 | test "y_number_real_pos_exponent" { |
| 229 | ok( | |
| 245 | try ok( | |
| 230 | 246 | \\[1e+2] |
| 231 | 247 | ); |
| 232 | 248 | } |
| 233 | 249 | |
| 234 | 250 | test "y_number_simple_int" { |
| 235 | ok( | |
| 251 | try roundTrip( | |
| 236 | 252 | \\[123] |
| 237 | 253 | ); |
| 238 | 254 | } |
| 239 | 255 | |
| 240 | 256 | test "y_number_simple_real" { |
| 241 | ok( | |
| 257 | try ok( | |
| 242 | 258 | \\[123.456789] |
| 243 | 259 | ); |
| 244 | 260 | } |
| 245 | 261 | |
| 246 | 262 | test "y_object_basic" { |
| 247 | ok( | |
| 263 | try roundTrip( | |
| 248 | 264 | \\{"asd":"sdf"} |
| 249 | 265 | ); |
| 250 | 266 | } |
| 251 | 267 | |
| 252 | 268 | test "y_object_duplicated_key_and_value" { |
| 253 | ok( | |
| 269 | try ok( | |
| 254 | 270 | \\{"a":"b","a":"b"} |
| 255 | 271 | ); |
| 256 | 272 | } |
| 257 | 273 | |
| 258 | 274 | test "y_object_duplicated_key" { |
| 259 | ok( | |
| 275 | try ok( | |
| 260 | 276 | \\{"a":"b","a":"c"} |
| 261 | 277 | ); |
| 262 | 278 | } |
| 263 | 279 | |
| 264 | 280 | test "y_object_empty" { |
| 265 | ok( | |
| 281 | try roundTrip( | |
| 266 | 282 | \\{} |
| 267 | 283 | ); |
| 268 | 284 | } |
| 269 | 285 | |
| 270 | 286 | test "y_object_empty_key" { |
| 271 | ok( | |
| 287 | try roundTrip( | |
| 272 | 288 | \\{"":0} |
| 273 | 289 | ); |
| 274 | 290 | } |
| 275 | 291 | |
| 276 | 292 | test "y_object_escaped_null_in_key" { |
| 277 | ok( | |
| 293 | try ok( | |
| 278 | 294 | \\{"foo\u0000bar": 42} |
| 279 | 295 | ); |
| 280 | 296 | } |
| 281 | 297 | |
| 282 | 298 | test "y_object_extreme_numbers" { |
| 283 | ok( | |
| 299 | try ok( | |
| 284 | 300 | \\{ "min": -1.0e+28, "max": 1.0e+28 } |
| 285 | 301 | ); |
| 286 | 302 | } |
| 287 | 303 | |
| 288 | 304 | test "y_object" { |
| 289 | ok( | |
| 305 | try ok( | |
| 290 | 306 | \\{"asd":"sdf", "dfg":"fgh"} |
| 291 | 307 | ); |
| 292 | 308 | } |
| 293 | 309 | |
| 294 | 310 | test "y_object_long_strings" { |
| 295 | ok( | |
| 311 | try ok( | |
| 296 | 312 | \\{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} |
| 297 | 313 | ); |
| 298 | 314 | } |
| 299 | 315 | |
| 300 | 316 | test "y_object_simple" { |
| 301 | ok( | |
| 317 | try roundTrip( | |
| 302 | 318 | \\{"a":[]} |
| 303 | 319 | ); |
| 304 | 320 | } |
| 305 | 321 | |
| 306 | 322 | test "y_object_string_unicode" { |
| 307 | ok( | |
| 323 | try ok( | |
| 308 | 324 | \\{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" } |
| 309 | 325 | ); |
| 310 | 326 | } |
| 311 | 327 | |
| 312 | 328 | test "y_object_with_newlines" { |
| 313 | ok( | |
| 329 | try ok( | |
| 314 | 330 | \\{ |
| 315 | 331 | \\"a": "b" |
| 316 | 332 | \\} |
| ... | ... | @@ -318,311 +334,311 @@ test "y_object_with_newlines" { |
| 318 | 334 | } |
| 319 | 335 | |
| 320 | 336 | test "y_string_1_2_3_bytes_UTF-8_sequences" { |
| 321 | ok( | |
| 337 | try ok( | |
| 322 | 338 | \\["\u0060\u012a\u12AB"] |
| 323 | 339 | ); |
| 324 | 340 | } |
| 325 | 341 | |
| 326 | 342 | test "y_string_accepted_surrogate_pair" { |
| 327 | ok( | |
| 343 | try ok( | |
| 328 | 344 | \\["\uD801\udc37"] |
| 329 | 345 | ); |
| 330 | 346 | } |
| 331 | 347 | |
| 332 | 348 | test "y_string_accepted_surrogate_pairs" { |
| 333 | ok( | |
| 349 | try ok( | |
| 334 | 350 | \\["\ud83d\ude39\ud83d\udc8d"] |
| 335 | 351 | ); |
| 336 | 352 | } |
| 337 | 353 | |
| 338 | 354 | test "y_string_allowed_escapes" { |
| 339 | ok( | |
| 355 | try ok( | |
| 340 | 356 | \\["\"\\\/\b\f\n\r\t"] |
| 341 | 357 | ); |
| 342 | 358 | } |
| 343 | 359 | |
| 344 | 360 | test "y_string_backslash_and_u_escaped_zero" { |
| 345 | ok( | |
| 361 | try ok( | |
| 346 | 362 | \\["\\u0000"] |
| 347 | 363 | ); |
| 348 | 364 | } |
| 349 | 365 | |
| 350 | 366 | test "y_string_backslash_doublequotes" { |
| 351 | ok( | |
| 367 | try roundTrip( | |
| 352 | 368 | \\["\""] |
| 353 | 369 | ); |
| 354 | 370 | } |
| 355 | 371 | |
| 356 | 372 | test "y_string_comments" { |
| 357 | ok( | |
| 373 | try ok( | |
| 358 | 374 | \\["a/*b*/c/*d//e"] |
| 359 | 375 | ); |
| 360 | 376 | } |
| 361 | 377 | |
| 362 | 378 | test "y_string_double_escape_a" { |
| 363 | ok( | |
| 379 | try ok( | |
| 364 | 380 | \\["\\a"] |
| 365 | 381 | ); |
| 366 | 382 | } |
| 367 | 383 | |
| 368 | 384 | test "y_string_double_escape_n" { |
| 369 | ok( | |
| 385 | try roundTrip( | |
| 370 | 386 | \\["\\n"] |
| 371 | 387 | ); |
| 372 | 388 | } |
| 373 | 389 | |
| 374 | 390 | test "y_string_escaped_control_character" { |
| 375 | ok( | |
| 391 | try ok( | |
| 376 | 392 | \\["\u0012"] |
| 377 | 393 | ); |
| 378 | 394 | } |
| 379 | 395 | |
| 380 | 396 | test "y_string_escaped_noncharacter" { |
| 381 | ok( | |
| 397 | try ok( | |
| 382 | 398 | \\["\uFFFF"] |
| 383 | 399 | ); |
| 384 | 400 | } |
| 385 | 401 | |
| 386 | 402 | test "y_string_in_array" { |
| 387 | ok( | |
| 403 | try ok( | |
| 388 | 404 | \\["asd"] |
| 389 | 405 | ); |
| 390 | 406 | } |
| 391 | 407 | |
| 392 | 408 | test "y_string_in_array_with_leading_space" { |
| 393 | ok( | |
| 409 | try ok( | |
| 394 | 410 | \\[ "asd"] |
| 395 | 411 | ); |
| 396 | 412 | } |
| 397 | 413 | |
| 398 | 414 | test "y_string_last_surrogates_1_and_2" { |
| 399 | ok( | |
| 415 | try ok( | |
| 400 | 416 | \\["\uDBFF\uDFFF"] |
| 401 | 417 | ); |
| 402 | 418 | } |
| 403 | 419 | |
| 404 | 420 | test "y_string_nbsp_uescaped" { |
| 405 | ok( | |
| 421 | try ok( | |
| 406 | 422 | \\["new\u00A0line"] |
| 407 | 423 | ); |
| 408 | 424 | } |
| 409 | 425 | |
| 410 | 426 | test "y_string_nonCharacterInUTF-8_U+10FFFF" { |
| 411 | ok( | |
| 427 | try ok( | |
| 412 | 428 | \\[""] |
| 413 | 429 | ); |
| 414 | 430 | } |
| 415 | 431 | |
| 416 | 432 | test "y_string_nonCharacterInUTF-8_U+FFFF" { |
| 417 | ok( | |
| 433 | try ok( | |
| 418 | 434 | \\[""] |
| 419 | 435 | ); |
| 420 | 436 | } |
| 421 | 437 | |
| 422 | 438 | test "y_string_null_escape" { |
| 423 | ok( | |
| 439 | try ok( | |
| 424 | 440 | \\["\u0000"] |
| 425 | 441 | ); |
| 426 | 442 | } |
| 427 | 443 | |
| 428 | 444 | test "y_string_one-byte-utf-8" { |
| 429 | ok( | |
| 445 | try ok( | |
| 430 | 446 | \\["\u002c"] |
| 431 | 447 | ); |
| 432 | 448 | } |
| 433 | 449 | |
| 434 | 450 | test "y_string_pi" { |
| 435 | ok( | |
| 451 | try ok( | |
| 436 | 452 | \\["π"] |
| 437 | 453 | ); |
| 438 | 454 | } |
| 439 | 455 | |
| 440 | 456 | test "y_string_reservedCharacterInUTF-8_U+1BFFF" { |
| 441 | ok( | |
| 457 | try ok( | |
| 442 | 458 | \\[""] |
| 443 | 459 | ); |
| 444 | 460 | } |
| 445 | 461 | |
| 446 | 462 | test "y_string_simple_ascii" { |
| 447 | ok( | |
| 463 | try ok( | |
| 448 | 464 | \\["asd "] |
| 449 | 465 | ); |
| 450 | 466 | } |
| 451 | 467 | |
| 452 | 468 | test "y_string_space" { |
| 453 | ok( | |
| 469 | try roundTrip( | |
| 454 | 470 | \\" " |
| 455 | 471 | ); |
| 456 | 472 | } |
| 457 | 473 | |
| 458 | 474 | test "y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF" { |
| 459 | ok( | |
| 475 | try ok( | |
| 460 | 476 | \\["\uD834\uDd1e"] |
| 461 | 477 | ); |
| 462 | 478 | } |
| 463 | 479 | |
| 464 | 480 | test "y_string_three-byte-utf-8" { |
| 465 | ok( | |
| 481 | try ok( | |
| 466 | 482 | \\["\u0821"] |
| 467 | 483 | ); |
| 468 | 484 | } |
| 469 | 485 | |
| 470 | 486 | test "y_string_two-byte-utf-8" { |
| 471 | ok( | |
| 487 | try ok( | |
| 472 | 488 | \\["\u0123"] |
| 473 | 489 | ); |
| 474 | 490 | } |
| 475 | 491 | |
| 476 | 492 | test "y_string_u+2028_line_sep" { |
| 477 | ok("[\"\xe2\x80\xa8\"]"); | |
| 493 | try ok("[\"\xe2\x80\xa8\"]"); | |
| 478 | 494 | } |
| 479 | 495 | |
| 480 | 496 | test "y_string_u+2029_par_sep" { |
| 481 | ok("[\"\xe2\x80\xa9\"]"); | |
| 497 | try ok("[\"\xe2\x80\xa9\"]"); | |
| 482 | 498 | } |
| 483 | 499 | |
| 484 | 500 | test "y_string_uescaped_newline" { |
| 485 | ok( | |
| 501 | try ok( | |
| 486 | 502 | \\["new\u000Aline"] |
| 487 | 503 | ); |
| 488 | 504 | } |
| 489 | 505 | |
| 490 | 506 | test "y_string_uEscape" { |
| 491 | ok( | |
| 507 | try ok( | |
| 492 | 508 | \\["\u0061\u30af\u30EA\u30b9"] |
| 493 | 509 | ); |
| 494 | 510 | } |
| 495 | 511 | |
| 496 | 512 | test "y_string_unescaped_char_delete" { |
| 497 | ok("[\"\x7f\"]"); | |
| 513 | try ok("[\"\x7f\"]"); | |
| 498 | 514 | } |
| 499 | 515 | |
| 500 | 516 | test "y_string_unicode_2" { |
| 501 | ok( | |
| 517 | try ok( | |
| 502 | 518 | \\["⍂㈴⍂"] |
| 503 | 519 | ); |
| 504 | 520 | } |
| 505 | 521 | |
| 506 | 522 | test "y_string_unicodeEscapedBackslash" { |
| 507 | ok( | |
| 523 | try ok( | |
| 508 | 524 | \\["\u005C"] |
| 509 | 525 | ); |
| 510 | 526 | } |
| 511 | 527 | |
| 512 | 528 | test "y_string_unicode_escaped_double_quote" { |
| 513 | ok( | |
| 529 | try ok( | |
| 514 | 530 | \\["\u0022"] |
| 515 | 531 | ); |
| 516 | 532 | } |
| 517 | 533 | |
| 518 | 534 | test "y_string_unicode" { |
| 519 | ok( | |
| 535 | try ok( | |
| 520 | 536 | \\["\uA66D"] |
| 521 | 537 | ); |
| 522 | 538 | } |
| 523 | 539 | |
| 524 | 540 | test "y_string_unicode_U+10FFFE_nonchar" { |
| 525 | ok( | |
| 541 | try ok( | |
| 526 | 542 | \\["\uDBFF\uDFFE"] |
| 527 | 543 | ); |
| 528 | 544 | } |
| 529 | 545 | |
| 530 | 546 | test "y_string_unicode_U+1FFFE_nonchar" { |
| 531 | ok( | |
| 547 | try ok( | |
| 532 | 548 | \\["\uD83F\uDFFE"] |
| 533 | 549 | ); |
| 534 | 550 | } |
| 535 | 551 | |
| 536 | 552 | test "y_string_unicode_U+200B_ZERO_WIDTH_SPACE" { |
| 537 | ok( | |
| 553 | try ok( | |
| 538 | 554 | \\["\u200B"] |
| 539 | 555 | ); |
| 540 | 556 | } |
| 541 | 557 | |
| 542 | 558 | test "y_string_unicode_U+2064_invisible_plus" { |
| 543 | ok( | |
| 559 | try ok( | |
| 544 | 560 | \\["\u2064"] |
| 545 | 561 | ); |
| 546 | 562 | } |
| 547 | 563 | |
| 548 | 564 | test "y_string_unicode_U+FDD0_nonchar" { |
| 549 | ok( | |
| 565 | try ok( | |
| 550 | 566 | \\["\uFDD0"] |
| 551 | 567 | ); |
| 552 | 568 | } |
| 553 | 569 | |
| 554 | 570 | test "y_string_unicode_U+FFFE_nonchar" { |
| 555 | ok( | |
| 571 | try ok( | |
| 556 | 572 | \\["\uFFFE"] |
| 557 | 573 | ); |
| 558 | 574 | } |
| 559 | 575 | |
| 560 | 576 | test "y_string_utf8" { |
| 561 | ok( | |
| 577 | try ok( | |
| 562 | 578 | \\["€𝄞"] |
| 563 | 579 | ); |
| 564 | 580 | } |
| 565 | 581 | |
| 566 | 582 | test "y_string_with_del_character" { |
| 567 | ok("[\"a\x7fa\"]"); | |
| 583 | try ok("[\"a\x7fa\"]"); | |
| 568 | 584 | } |
| 569 | 585 | |
| 570 | 586 | test "y_structure_lonely_false" { |
| 571 | ok( | |
| 587 | try roundTrip( | |
| 572 | 588 | \\false |
| 573 | 589 | ); |
| 574 | 590 | } |
| 575 | 591 | |
| 576 | 592 | test "y_structure_lonely_int" { |
| 577 | ok( | |
| 593 | try roundTrip( | |
| 578 | 594 | \\42 |
| 579 | 595 | ); |
| 580 | 596 | } |
| 581 | 597 | |
| 582 | 598 | test "y_structure_lonely_negative_real" { |
| 583 | ok( | |
| 599 | try ok( | |
| 584 | 600 | \\-0.1 |
| 585 | 601 | ); |
| 586 | 602 | } |
| 587 | 603 | |
| 588 | 604 | test "y_structure_lonely_null" { |
| 589 | ok( | |
| 605 | try roundTrip( | |
| 590 | 606 | \\null |
| 591 | 607 | ); |
| 592 | 608 | } |
| 593 | 609 | |
| 594 | 610 | test "y_structure_lonely_string" { |
| 595 | ok( | |
| 611 | try roundTrip( | |
| 596 | 612 | \\"asd" |
| 597 | 613 | ); |
| 598 | 614 | } |
| 599 | 615 | |
| 600 | 616 | test "y_structure_lonely_true" { |
| 601 | ok( | |
| 617 | try roundTrip( | |
| 602 | 618 | \\true |
| 603 | 619 | ); |
| 604 | 620 | } |
| 605 | 621 | |
| 606 | 622 | test "y_structure_string_empty" { |
| 607 | ok( | |
| 623 | try roundTrip( | |
| 608 | 624 | \\"" |
| 609 | 625 | ); |
| 610 | 626 | } |
| 611 | 627 | |
| 612 | 628 | test "y_structure_trailing_newline" { |
| 613 | ok( | |
| 629 | try roundTrip( | |
| 614 | 630 | \\["a"] |
| 615 | 631 | ); |
| 616 | 632 | } |
| 617 | 633 | |
| 618 | 634 | test "y_structure_true_in_array" { |
| 619 | ok( | |
| 635 | try roundTrip( | |
| 620 | 636 | \\[true] |
| 621 | 637 | ); |
| 622 | 638 | } |
| 623 | 639 | |
| 624 | 640 | test "y_structure_whitespace_array" { |
| 625 | ok(" [] "); | |
| 641 | try ok(" [] "); | |
| 626 | 642 | } |
| 627 | 643 | |
| 628 | 644 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
lib/std/math/ln.zig+3-2| ... | ... | @@ -36,8 +36,9 @@ pub fn ln(x: anytype) @TypeOf(x) { |
| 36 | 36 | .ComptimeInt => { |
| 37 | 37 | return @as(comptime_int, math.floor(ln_64(@as(f64, x)))); |
| 38 | 38 | }, |
| 39 | .Int => { | |
| 40 | return @as(T, math.floor(ln_64(@as(f64, x)))); | |
| 39 | .Int => |IntType| switch (IntType.signedness) { | |
| 40 | .signed => return @compileError("ln not implemented for signed integers"), | |
| 41 | .unsigned => return @as(T, math.floor(ln_64(@as(f64, x)))), | |
| 41 | 42 | }, |
| 42 | 43 | else => @compileError("ln not implemented for " ++ @typeName(T)), |
| 43 | 44 | } |
lib/std/math/log.zig+6-4| ... | ... | @@ -31,9 +31,11 @@ pub fn log(comptime T: type, base: T, x: T) T { |
| 31 | 31 | .ComptimeInt => { |
| 32 | 32 | return @as(comptime_int, math.floor(math.ln(@as(f64, x)) / math.ln(float_base))); |
| 33 | 33 | }, |
| 34 | .Int => { | |
| 35 | // TODO implement integer log without using float math | |
| 36 | return @floatToInt(T, math.floor(math.ln(@intToFloat(f64, x)) / math.ln(float_base))); | |
| 34 | ||
| 35 | // TODO implement integer log without using float math | |
| 36 | .Int => |IntType| switch (IntType.signedness) { | |
| 37 | .signed => return @compileError("log not implemented for signed integers"), | |
| 38 | .unsigned => return @floatToInt(T, math.floor(math.ln(@intToFloat(f64, x)) / math.ln(float_base))), | |
| 37 | 39 | }, |
| 38 | 40 | |
| 39 | 41 | .Float => { |
| ... | ... | @@ -53,7 +55,7 @@ pub fn log(comptime T: type, base: T, x: T) T { |
| 53 | 55 | test "math.log integer" { |
| 54 | 56 | expect(log(u8, 2, 0x1) == 0); |
| 55 | 57 | expect(log(u8, 2, 0x2) == 1); |
| 56 | expect(log(i16, 2, 0x72) == 6); | |
| 58 | expect(log(u16, 2, 0x72) == 6); | |
| 57 | 59 | expect(log(u32, 2, 0xFFFFFF) == 23); |
| 58 | 60 | expect(log(u64, 2, 0x7FF0123456789ABC) == 62); |
| 59 | 61 | } |
lib/std/math/log10.zig+3-2| ... | ... | @@ -37,8 +37,9 @@ pub fn log10(x: anytype) @TypeOf(x) { |
| 37 | 37 | .ComptimeInt => { |
| 38 | 38 | return @as(comptime_int, math.floor(log10_64(@as(f64, x)))); |
| 39 | 39 | }, |
| 40 | .Int => { | |
| 41 | return @floatToInt(T, math.floor(log10_64(@intToFloat(f64, x)))); | |
| 40 | .Int => |IntType| switch (IntType.signedness) { | |
| 41 | .signed => return @compileError("log10 not implemented for signed integers"), | |
| 42 | .unsigned => return @floatToInt(T, math.floor(log10_64(@intToFloat(f64, x)))), | |
| 42 | 43 | }, |
| 43 | 44 | else => @compileError("log10 not implemented for " ++ @typeName(T)), |
| 44 | 45 | } |
lib/std/math/log2.zig+3-2| ... | ... | @@ -43,8 +43,9 @@ pub fn log2(x: anytype) @TypeOf(x) { |
| 43 | 43 | }) : (result += 1) {} |
| 44 | 44 | return result; |
| 45 | 45 | }, |
| 46 | .Int => { | |
| 47 | return math.log2_int(T, x); | |
| 46 | .Int => |IntType| switch (IntType.signedness) { | |
| 47 | .signed => return @compileError("log2 not implemented for signed integers"), | |
| 48 | .unsigned => return math.log2_int(T, x), | |
| 48 | 49 | }, |
| 49 | 50 | else => @compileError("log2 not implemented for " ++ @typeName(T)), |
| 50 | 51 | } |
lib/std/math/sqrt.zig+4-1| ... | ... | @@ -31,7 +31,10 @@ pub fn sqrt(x: anytype) Sqrt(@TypeOf(x)) { |
| 31 | 31 | } |
| 32 | 32 | return @as(T, sqrt_int(u128, x)); |
| 33 | 33 | }, |
| 34 | .Int => return sqrt_int(T, x), | |
| 34 | .Int => |IntType| switch (IntType.signedness) { | |
| 35 | .signed => return @compileError("sqrt not implemented for signed integers"), | |
| 36 | .unsigned => return sqrt_int(T, x), | |
| 37 | }, | |
| 35 | 38 | else => @compileError("sqrt not implemented for " ++ @typeName(T)), |
| 36 | 39 | } |
| 37 | 40 | } |
lib/std/meta.zig+38-1| ... | ... | @@ -970,12 +970,15 @@ fn castPtr(comptime DestType: type, target: anytype) DestType { |
| 970 | 970 | |
| 971 | 971 | if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile) |
| 972 | 972 | return @intToPtr(DestType, @ptrToInt(target)) |
| 973 | else if (@typeInfo(dest.child) == .Opaque) | |
| 974 | // dest.alignment would error out | |
| 975 | return @ptrCast(DestType, target) | |
| 973 | 976 | else |
| 974 | 977 | return @ptrCast(DestType, @alignCast(dest.alignment, target)); |
| 975 | 978 | } |
| 976 | 979 | |
| 977 | 980 | fn ptrInfo(comptime PtrType: type) TypeInfo.Pointer { |
| 978 | return switch(@typeInfo(PtrType)){ | |
| 981 | return switch (@typeInfo(PtrType)) { | |
| 979 | 982 | .Optional => |opt_info| @typeInfo(opt_info.child).Pointer, |
| 980 | 983 | .Pointer => |ptr_info| ptr_info, |
| 981 | 984 | else => unreachable, |
| ... | ... | @@ -1010,6 +1013,8 @@ test "std.meta.cast" { |
| 1010 | 1013 | |
| 1011 | 1014 | testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*const u8, 2))); |
| 1012 | 1015 | testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2))); |
| 1016 | ||
| 1017 | testing.expectEqual(@intToPtr(?*c_void, 2), cast(?*c_void, @intToPtr(*u8, 2))); | |
| 1013 | 1018 | } |
| 1014 | 1019 | |
| 1015 | 1020 | /// Given a value returns its size as C's sizeof operator would. |
| ... | ... | @@ -1297,3 +1302,35 @@ pub fn globalOption(comptime name: []const u8, comptime T: type) ?T { |
| 1297 | 1302 | return null; |
| 1298 | 1303 | return @as(T, @field(root, name)); |
| 1299 | 1304 | } |
| 1305 | ||
| 1306 | /// This function is for translate-c and is not intended for general use. | |
| 1307 | /// Convert from clang __builtin_shufflevector index to Zig @shuffle index | |
| 1308 | /// clang requires __builtin_shufflevector index arguments to be integer constants. | |
| 1309 | /// negative values for `this_index` indicate "don't care" so we arbitrarily choose 0 | |
| 1310 | /// clang enforces that `this_index` is less than the total number of vector elements | |
| 1311 | /// See https://ziglang.org/documentation/master/#shuffle | |
| 1312 | /// See https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-shufflevector | |
| 1313 | pub fn shuffleVectorIndex(comptime this_index: c_int, comptime source_vector_len: usize) i32 { | |
| 1314 | if (this_index <= 0) return 0; | |
| 1315 | ||
| 1316 | const positive_index = @intCast(usize, this_index); | |
| 1317 | if (positive_index < source_vector_len) return @intCast(i32, this_index); | |
| 1318 | const b_index = positive_index - source_vector_len; | |
| 1319 | return ~@intCast(i32, b_index); | |
| 1320 | } | |
| 1321 | ||
| 1322 | test "shuffleVectorIndex" { | |
| 1323 | const vector_len: usize = 4; | |
| 1324 | ||
| 1325 | testing.expect(shuffleVectorIndex(-1, vector_len) == 0); | |
| 1326 | ||
| 1327 | testing.expect(shuffleVectorIndex(0, vector_len) == 0); | |
| 1328 | testing.expect(shuffleVectorIndex(1, vector_len) == 1); | |
| 1329 | testing.expect(shuffleVectorIndex(2, vector_len) == 2); | |
| 1330 | testing.expect(shuffleVectorIndex(3, vector_len) == 3); | |
| 1331 | ||
| 1332 | testing.expect(shuffleVectorIndex(4, vector_len) == -1); | |
| 1333 | testing.expect(shuffleVectorIndex(5, vector_len) == -2); | |
| 1334 | testing.expect(shuffleVectorIndex(6, vector_len) == -3); | |
| 1335 | testing.expect(shuffleVectorIndex(7, vector_len) == -4); | |
| 1336 | } |
lib/std/os.zig+5-2| ... | ... | @@ -3254,6 +3254,9 @@ pub const ConnectError = error{ |
| 3254 | 3254 | |
| 3255 | 3255 | /// Connection was reset by peer before connect could complete. |
| 3256 | 3256 | ConnectionResetByPeer, |
| 3257 | ||
| 3258 | /// Socket is non-blocking and already has a pending connection in progress. | |
| 3259 | ConnectionPending, | |
| 3257 | 3260 | } || UnexpectedError; |
| 3258 | 3261 | |
| 3259 | 3262 | /// Initiate a connection on a socket. |
| ... | ... | @@ -3294,7 +3297,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne |
| 3294 | 3297 | EADDRNOTAVAIL => return error.AddressNotAvailable, |
| 3295 | 3298 | EAFNOSUPPORT => return error.AddressFamilyNotSupported, |
| 3296 | 3299 | EAGAIN, EINPROGRESS => return error.WouldBlock, |
| 3297 | EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | |
| 3300 | EALREADY => return error.ConnectionPending, | |
| 3298 | 3301 | EBADF => unreachable, // sockfd is not a valid open file descriptor. |
| 3299 | 3302 | ECONNREFUSED => return error.ConnectionRefused, |
| 3300 | 3303 | ECONNRESET => return error.ConnectionResetByPeer, |
| ... | ... | @@ -3325,7 +3328,7 @@ pub fn getsockoptError(sockfd: fd_t) ConnectError!void { |
| 3325 | 3328 | EADDRNOTAVAIL => return error.AddressNotAvailable, |
| 3326 | 3329 | EAFNOSUPPORT => return error.AddressFamilyNotSupported, |
| 3327 | 3330 | EAGAIN => return error.SystemResources, |
| 3328 | EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | |
| 3331 | EALREADY => return error.ConnectionPending, | |
| 3329 | 3332 | EBADF => unreachable, // sockfd is not a valid open file descriptor. |
| 3330 | 3333 | ECONNREFUSED => return error.ConnectionRefused, |
| 3331 | 3334 | EFAULT => unreachable, // The socket structure address is outside the user's address space. |
lib/std/os/bits/linux.zig+87| ... | ... | @@ -21,6 +21,7 @@ pub usingnamespace switch (builtin.arch) { |
| 21 | 21 | .riscv64 => @import("linux/riscv64.zig"), |
| 22 | 22 | .sparcv9 => @import("linux/sparc64.zig"), |
| 23 | 23 | .mips, .mipsel => @import("linux/mips.zig"), |
| 24 | .powerpc => @import("linux/powerpc.zig"), | |
| 24 | 25 | .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"), |
| 25 | 26 | else => struct {}, |
| 26 | 27 | }; |
| ... | ... | @@ -586,6 +587,92 @@ pub const IP_DEFAULT_MULTICAST_TTL = 1; |
| 586 | 587 | pub const IP_DEFAULT_MULTICAST_LOOP = 1; |
| 587 | 588 | pub const IP_MAX_MEMBERSHIPS = 20; |
| 588 | 589 | |
| 590 | // IPv6 socket options | |
| 591 | ||
| 592 | pub const IPV6_ADDRFORM = 1; | |
| 593 | pub const IPV6_2292PKTINFO = 2; | |
| 594 | pub const IPV6_2292HOPOPTS = 3; | |
| 595 | pub const IPV6_2292DSTOPTS = 4; | |
| 596 | pub const IPV6_2292RTHDR = 5; | |
| 597 | pub const IPV6_2292PKTOPTIONS = 6; | |
| 598 | pub const IPV6_CHECKSUM = 7; | |
| 599 | pub const IPV6_2292HOPLIMIT = 8; | |
| 600 | pub const IPV6_NEXTHOP = 9; | |
| 601 | pub const IPV6_AUTHHDR = 10; | |
| 602 | pub const IPV6_FLOWINFO = 11; | |
| 603 | ||
| 604 | pub const IPV6_UNICAST_HOPS = 16; | |
| 605 | pub const IPV6_MULTICAST_IF = 17; | |
| 606 | pub const IPV6_MULTICAST_HOPS = 18; | |
| 607 | pub const IPV6_MULTICAST_LOOP = 19; | |
| 608 | pub const IPV6_ADD_MEMBERSHIP = 20; | |
| 609 | pub const IPV6_DROP_MEMBERSHIP = 21; | |
| 610 | pub const IPV6_ROUTER_ALERT = 22; | |
| 611 | pub const IPV6_MTU_DISCOVER = 23; | |
| 612 | pub const IPV6_MTU = 24; | |
| 613 | pub const IPV6_RECVERR = 25; | |
| 614 | pub const IPV6_V6ONLY = 26; | |
| 615 | pub const IPV6_JOIN_ANYCAST = 27; | |
| 616 | pub const IPV6_LEAVE_ANYCAST = 28; | |
| 617 | ||
| 618 | // IPV6_MTU_DISCOVER values | |
| 619 | pub const IPV6_PMTUDISC_DONT = 0; | |
| 620 | pub const IPV6_PMTUDISC_WANT = 1; | |
| 621 | pub const IPV6_PMTUDISC_DO = 2; | |
| 622 | pub const IPV6_PMTUDISC_PROBE = 3; | |
| 623 | pub const IPV6_PMTUDISC_INTERFACE = 4; | |
| 624 | pub const IPV6_PMTUDISC_OMIT = 5; | |
| 625 | ||
| 626 | // Flowlabel | |
| 627 | pub const IPV6_FLOWLABEL_MGR = 32; | |
| 628 | pub const IPV6_FLOWINFO_SEND = 33; | |
| 629 | pub const IPV6_IPSEC_POLICY = 34; | |
| 630 | pub const IPV6_XFRM_POLICY = 35; | |
| 631 | pub const IPV6_HDRINCL = 36; | |
| 632 | ||
| 633 | // Advanced API (RFC3542) (1) | |
| 634 | pub const IPV6_RECVPKTINFO = 49; | |
| 635 | pub const IPV6_PKTINFO = 50; | |
| 636 | pub const IPV6_RECVHOPLIMIT = 51; | |
| 637 | pub const IPV6_HOPLIMIT = 52; | |
| 638 | pub const IPV6_RECVHOPOPTS = 53; | |
| 639 | pub const IPV6_HOPOPTS = 54; | |
| 640 | pub const IPV6_RTHDRDSTOPTS = 55; | |
| 641 | pub const IPV6_RECVRTHDR = 56; | |
| 642 | pub const IPV6_RTHDR = 57; | |
| 643 | pub const IPV6_RECVDSTOPTS = 58; | |
| 644 | pub const IPV6_DSTOPTS = 59; | |
| 645 | pub const IPV6_RECVPATHMTU = 60; | |
| 646 | pub const IPV6_PATHMTU = 61; | |
| 647 | pub const IPV6_DONTFRAG = 62; | |
| 648 | ||
| 649 | // Advanced API (RFC3542) (2) | |
| 650 | pub const IPV6_RECVTCLASS = 66; | |
| 651 | pub const IPV6_TCLASS = 67; | |
| 652 | ||
| 653 | pub const IPV6_AUTOFLOWLABEL = 70; | |
| 654 | ||
| 655 | // RFC5014: Source address selection | |
| 656 | pub const IPV6_ADDR_PREFERENCES = 72; | |
| 657 | ||
| 658 | pub const IPV6_PREFER_SRC_TMP = 0x0001; | |
| 659 | pub const IPV6_PREFER_SRC_PUBLIC = 0x0002; | |
| 660 | pub const IPV6_PREFER_SRC_PUBTMP_DEFAULT = 0x0100; | |
| 661 | pub const IPV6_PREFER_SRC_COA = 0x0004; | |
| 662 | pub const IPV6_PREFER_SRC_HOME = 0x0400; | |
| 663 | pub const IPV6_PREFER_SRC_CGA = 0x0008; | |
| 664 | pub const IPV6_PREFER_SRC_NONCGA = 0x0800; | |
| 665 | ||
| 666 | // RFC5082: Generalized Ttl Security Mechanism | |
| 667 | pub const IPV6_MINHOPCOUNT = 73; | |
| 668 | ||
| 669 | pub const IPV6_ORIGDSTADDR = 74; | |
| 670 | pub const IPV6_RECVORIGDSTADDR = IPV6_ORIGDSTADDR; | |
| 671 | pub const IPV6_TRANSPARENT = 75; | |
| 672 | pub const IPV6_UNICAST_IF = 76; | |
| 673 | pub const IPV6_RECVFRAGSIZE = 77; | |
| 674 | pub const IPV6_FREEBIND = 78; | |
| 675 | ||
| 589 | 676 | pub const MSG_OOB = 0x0001; |
| 590 | 677 | pub const MSG_PEEK = 0x0002; |
| 591 | 678 | pub const MSG_DONTROUTE = 0x0004; |
lib/std/os/bits/linux/powerpc.zig created+635| ... | ... | @@ -0,0 +1,635 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2015-2021 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 6 | ||
| 7 | const std = @import("../../../std.zig"); | |
| 8 | const linux = std.os.linux; | |
| 9 | const socklen_t = linux.socklen_t; | |
| 10 | const iovec = linux.iovec; | |
| 11 | const iovec_const = linux.iovec_const; | |
| 12 | const uid_t = linux.uid_t; | |
| 13 | const gid_t = linux.gid_t; | |
| 14 | const pid_t = linux.pid_t; | |
| 15 | const stack_t = linux.stack_t; | |
| 16 | const sigset_t = linux.sigset_t; | |
| 17 | pub const SYS = extern enum(usize) { | |
| 18 | restart_syscall = 0, | |
| 19 | exit = 1, | |
| 20 | fork = 2, | |
| 21 | read = 3, | |
| 22 | write = 4, | |
| 23 | open = 5, | |
| 24 | close = 6, | |
| 25 | waitpid = 7, | |
| 26 | creat = 8, | |
| 27 | link = 9, | |
| 28 | unlink = 10, | |
| 29 | execve = 11, | |
| 30 | chdir = 12, | |
| 31 | time = 13, | |
| 32 | mknod = 14, | |
| 33 | chmod = 15, | |
| 34 | lchown = 16, | |
| 35 | @"break" = 17, | |
| 36 | oldstat = 18, | |
| 37 | lseek = 19, | |
| 38 | getpid = 20, | |
| 39 | mount = 21, | |
| 40 | umount = 22, | |
| 41 | setuid = 23, | |
| 42 | getuid = 24, | |
| 43 | stime = 25, | |
| 44 | ptrace = 26, | |
| 45 | alarm = 27, | |
| 46 | oldfstat = 28, | |
| 47 | pause = 29, | |
| 48 | utime = 30, | |
| 49 | stty = 31, | |
| 50 | gtty = 32, | |
| 51 | access = 33, | |
| 52 | nice = 34, | |
| 53 | ftime = 35, | |
| 54 | sync = 36, | |
| 55 | kill = 37, | |
| 56 | rename = 38, | |
| 57 | mkdir = 39, | |
| 58 | rmdir = 40, | |
| 59 | dup = 41, | |
| 60 | pipe = 42, | |
| 61 | times = 43, | |
| 62 | prof = 44, | |
| 63 | brk = 45, | |
| 64 | setgid = 46, | |
| 65 | getgid = 47, | |
| 66 | signal = 48, | |
| 67 | geteuid = 49, | |
| 68 | getegid = 50, | |
| 69 | acct = 51, | |
| 70 | umount2 = 52, | |
| 71 | lock = 53, | |
| 72 | ioctl = 54, | |
| 73 | fcntl = 55, | |
| 74 | mpx = 56, | |
| 75 | setpgid = 57, | |
| 76 | ulimit = 58, | |
| 77 | oldolduname = 59, | |
| 78 | umask = 60, | |
| 79 | chroot = 61, | |
| 80 | ustat = 62, | |
| 81 | dup2 = 63, | |
| 82 | getppid = 64, | |
| 83 | getpgrp = 65, | |
| 84 | setsid = 66, | |
| 85 | sigaction = 67, | |
| 86 | sgetmask = 68, | |
| 87 | ssetmask = 69, | |
| 88 | setreuid = 70, | |
| 89 | setregid = 71, | |
| 90 | sigsuspend = 72, | |
| 91 | sigpending = 73, | |
| 92 | sethostname = 74, | |
| 93 | setrlimit = 75, | |
| 94 | getrlimit = 76, | |
| 95 | getrusage = 77, | |
| 96 | gettimeofday = 78, | |
| 97 | settimeofday = 79, | |
| 98 | getgroups = 80, | |
| 99 | setgroups = 81, | |
| 100 | select = 82, | |
| 101 | symlink = 83, | |
| 102 | oldlstat = 84, | |
| 103 | readlink = 85, | |
| 104 | uselib = 86, | |
| 105 | swapon = 87, | |
| 106 | reboot = 88, | |
| 107 | readdir = 89, | |
| 108 | mmap = 90, | |
| 109 | munmap = 91, | |
| 110 | truncate = 92, | |
| 111 | ftruncate = 93, | |
| 112 | fchmod = 94, | |
| 113 | fchown = 95, | |
| 114 | getpriority = 96, | |
| 115 | setpriority = 97, | |
| 116 | profil = 98, | |
| 117 | statfs = 99, | |
| 118 | fstatfs = 100, | |
| 119 | ioperm = 101, | |
| 120 | socketcall = 102, | |
| 121 | syslog = 103, | |
| 122 | setitimer = 104, | |
| 123 | getitimer = 105, | |
| 124 | stat = 106, | |
| 125 | lstat = 107, | |
| 126 | fstat = 108, | |
| 127 | olduname = 109, | |
| 128 | iopl = 110, | |
| 129 | vhangup = 111, | |
| 130 | idle = 112, | |
| 131 | vm86 = 113, | |
| 132 | wait4 = 114, | |
| 133 | swapoff = 115, | |
| 134 | sysinfo = 116, | |
| 135 | ipc = 117, | |
| 136 | fsync = 118, | |
| 137 | sigreturn = 119, | |
| 138 | clone = 120, | |
| 139 | setdomainname = 121, | |
| 140 | uname = 122, | |
| 141 | modify_ldt = 123, | |
| 142 | adjtimex = 124, | |
| 143 | mprotect = 125, | |
| 144 | sigprocmask = 126, | |
| 145 | create_module = 127, | |
| 146 | init_module = 128, | |
| 147 | delete_module = 129, | |
| 148 | get_kernel_syms = 130, | |
| 149 | quotactl = 131, | |
| 150 | getpgid = 132, | |
| 151 | fchdir = 133, | |
| 152 | bdflush = 134, | |
| 153 | sysfs = 135, | |
| 154 | personality = 136, | |
| 155 | afs_syscall = 137, | |
| 156 | setfsuid = 138, | |
| 157 | setfsgid = 139, | |
| 158 | _llseek = 140, | |
| 159 | getdents = 141, | |
| 160 | _newselect = 142, | |
| 161 | flock = 143, | |
| 162 | msync = 144, | |
| 163 | readv = 145, | |
| 164 | writev = 146, | |
| 165 | getsid = 147, | |
| 166 | fdatasync = 148, | |
| 167 | _sysctl = 149, | |
| 168 | mlock = 150, | |
| 169 | munlock = 151, | |
| 170 | mlockall = 152, | |
| 171 | munlockall = 153, | |
| 172 | sched_setparam = 154, | |
| 173 | sched_getparam = 155, | |
| 174 | sched_setscheduler = 156, | |
| 175 | sched_getscheduler = 157, | |
| 176 | sched_yield = 158, | |
| 177 | sched_get_priority_max = 159, | |
| 178 | sched_get_priority_min = 160, | |
| 179 | sched_rr_get_interval = 161, | |
| 180 | nanosleep = 162, | |
| 181 | mremap = 163, | |
| 182 | setresuid = 164, | |
| 183 | getresuid = 165, | |
| 184 | query_module = 166, | |
| 185 | poll = 167, | |
| 186 | nfsservctl = 168, | |
| 187 | setresgid = 169, | |
| 188 | getresgid = 170, | |
| 189 | prctl = 171, | |
| 190 | rt_sigreturn = 172, | |
| 191 | rt_sigaction = 173, | |
| 192 | rt_sigprocmask = 174, | |
| 193 | rt_sigpending = 175, | |
| 194 | rt_sigtimedwait = 176, | |
| 195 | rt_sigqueueinfo = 177, | |
| 196 | rt_sigsuspend = 178, | |
| 197 | pread64 = 179, | |
| 198 | pwrite64 = 180, | |
| 199 | chown = 181, | |
| 200 | getcwd = 182, | |
| 201 | capget = 183, | |
| 202 | capset = 184, | |
| 203 | sigaltstack = 185, | |
| 204 | sendfile = 186, | |
| 205 | getpmsg = 187, | |
| 206 | putpmsg = 188, | |
| 207 | vfork = 189, | |
| 208 | ugetrlimit = 190, | |
| 209 | readahead = 191, | |
| 210 | mmap2 = 192, | |
| 211 | truncate64 = 193, | |
| 212 | ftruncate64 = 194, | |
| 213 | stat64 = 195, | |
| 214 | lstat64 = 196, | |
| 215 | fstat64 = 197, | |
| 216 | pciconfig_read = 198, | |
| 217 | pciconfig_write = 199, | |
| 218 | pciconfig_iobase = 200, | |
| 219 | multiplexer = 201, | |
| 220 | getdents64 = 202, | |
| 221 | pivot_root = 203, | |
| 222 | fcntl64 = 204, | |
| 223 | madvise = 205, | |
| 224 | mincore = 206, | |
| 225 | gettid = 207, | |
| 226 | tkill = 208, | |
| 227 | setxattr = 209, | |
| 228 | lsetxattr = 210, | |
| 229 | fsetxattr = 211, | |
| 230 | getxattr = 212, | |
| 231 | lgetxattr = 213, | |
| 232 | fgetxattr = 214, | |
| 233 | listxattr = 215, | |
| 234 | llistxattr = 216, | |
| 235 | flistxattr = 217, | |
| 236 | removexattr = 218, | |
| 237 | lremovexattr = 219, | |
| 238 | fremovexattr = 220, | |
| 239 | futex = 221, | |
| 240 | sched_setaffinity = 222, | |
| 241 | sched_getaffinity = 223, | |
| 242 | tuxcall = 225, | |
| 243 | sendfile64 = 226, | |
| 244 | io_setup = 227, | |
| 245 | io_destroy = 228, | |
| 246 | io_getevents = 229, | |
| 247 | io_submit = 230, | |
| 248 | io_cancel = 231, | |
| 249 | set_tid_address = 232, | |
| 250 | fadvise64 = 233, | |
| 251 | exit_group = 234, | |
| 252 | lookup_dcookie = 235, | |
| 253 | epoll_create = 236, | |
| 254 | epoll_ctl = 237, | |
| 255 | epoll_wait = 238, | |
| 256 | remap_file_pages = 239, | |
| 257 | timer_create = 240, | |
| 258 | timer_settime = 241, | |
| 259 | timer_gettime = 242, | |
| 260 | timer_getoverrun = 243, | |
| 261 | timer_delete = 244, | |
| 262 | clock_settime = 245, | |
| 263 | clock_gettime = 246, | |
| 264 | clock_getres = 247, | |
| 265 | clock_nanosleep = 248, | |
| 266 | swapcontext = 249, | |
| 267 | tgkill = 250, | |
| 268 | utimes = 251, | |
| 269 | statfs64 = 252, | |
| 270 | fstatfs64 = 253, | |
| 271 | fadvise64_64 = 254, | |
| 272 | rtas = 255, | |
| 273 | sys_debug_setcontext = 256, | |
| 274 | migrate_pages = 258, | |
| 275 | mbind = 259, | |
| 276 | get_mempolicy = 260, | |
| 277 | set_mempolicy = 261, | |
| 278 | mq_open = 262, | |
| 279 | mq_unlink = 263, | |
| 280 | mq_timedsend = 264, | |
| 281 | mq_timedreceive = 265, | |
| 282 | mq_notify = 266, | |
| 283 | mq_getsetattr = 267, | |
| 284 | kexec_load = 268, | |
| 285 | add_key = 269, | |
| 286 | request_key = 270, | |
| 287 | keyctl = 271, | |
| 288 | waitid = 272, | |
| 289 | ioprio_set = 273, | |
| 290 | ioprio_get = 274, | |
| 291 | inotify_init = 275, | |
| 292 | inotify_add_watch = 276, | |
| 293 | inotify_rm_watch = 277, | |
| 294 | spu_run = 278, | |
| 295 | spu_create = 279, | |
| 296 | pselect6 = 280, | |
| 297 | ppoll = 281, | |
| 298 | unshare = 282, | |
| 299 | splice = 283, | |
| 300 | tee = 284, | |
| 301 | vmsplice = 285, | |
| 302 | openat = 286, | |
| 303 | mkdirat = 287, | |
| 304 | mknodat = 288, | |
| 305 | fchownat = 289, | |
| 306 | futimesat = 290, | |
| 307 | fstatat64 = 291, | |
| 308 | unlinkat = 292, | |
| 309 | renameat = 293, | |
| 310 | linkat = 294, | |
| 311 | symlinkat = 295, | |
| 312 | readlinkat = 296, | |
| 313 | fchmodat = 297, | |
| 314 | faccessat = 298, | |
| 315 | get_robust_list = 299, | |
| 316 | set_robust_list = 300, | |
| 317 | move_pages = 301, | |
| 318 | getcpu = 302, | |
| 319 | epoll_pwait = 303, | |
| 320 | utimensat = 304, | |
| 321 | signalfd = 305, | |
| 322 | timerfd_create = 306, | |
| 323 | eventfd = 307, | |
| 324 | sync_file_range2 = 308, | |
| 325 | fallocate = 309, | |
| 326 | subpage_prot = 310, | |
| 327 | timerfd_settime = 311, | |
| 328 | timerfd_gettime = 312, | |
| 329 | signalfd4 = 313, | |
| 330 | eventfd2 = 314, | |
| 331 | epoll_create1 = 315, | |
| 332 | dup3 = 316, | |
| 333 | pipe2 = 317, | |
| 334 | inotify_init1 = 318, | |
| 335 | perf_event_open = 319, | |
| 336 | preadv = 320, | |
| 337 | pwritev = 321, | |
| 338 | rt_tgsigqueueinfo = 322, | |
| 339 | fanotify_init = 323, | |
| 340 | fanotify_mark = 324, | |
| 341 | prlimit64 = 325, | |
| 342 | socket = 326, | |
| 343 | bind = 327, | |
| 344 | connect = 328, | |
| 345 | listen = 329, | |
| 346 | accept = 330, | |
| 347 | getsockname = 331, | |
| 348 | getpeername = 332, | |
| 349 | socketpair = 333, | |
| 350 | send = 334, | |
| 351 | sendto = 335, | |
| 352 | recv = 336, | |
| 353 | recvfrom = 337, | |
| 354 | shutdown = 338, | |
| 355 | setsockopt = 339, | |
| 356 | getsockopt = 340, | |
| 357 | sendmsg = 341, | |
| 358 | recvmsg = 342, | |
| 359 | recvmmsg = 343, | |
| 360 | accept4 = 344, | |
| 361 | name_to_handle_at = 345, | |
| 362 | open_by_handle_at = 346, | |
| 363 | clock_adjtime = 347, | |
| 364 | syncfs = 348, | |
| 365 | sendmmsg = 349, | |
| 366 | setns = 350, | |
| 367 | process_vm_readv = 351, | |
| 368 | process_vm_writev = 352, | |
| 369 | finit_module = 353, | |
| 370 | kcmp = 354, | |
| 371 | sched_setattr = 355, | |
| 372 | sched_getattr = 356, | |
| 373 | renameat2 = 357, | |
| 374 | seccomp = 358, | |
| 375 | getrandom = 359, | |
| 376 | memfd_create = 360, | |
| 377 | bpf = 361, | |
| 378 | execveat = 362, | |
| 379 | switch_endian = 363, | |
| 380 | userfaultfd = 364, | |
| 381 | membarrier = 365, | |
| 382 | mlock2 = 378, | |
| 383 | copy_file_range = 379, | |
| 384 | preadv2 = 380, | |
| 385 | pwritev2 = 381, | |
| 386 | kexec_file_load = 382, | |
| 387 | statx = 383, | |
| 388 | pkey_alloc = 384, | |
| 389 | pkey_free = 385, | |
| 390 | pkey_mprotect = 386, | |
| 391 | rseq = 387, | |
| 392 | io_pgetevents = 388, | |
| 393 | semget = 393, | |
| 394 | semctl = 394, | |
| 395 | shmget = 395, | |
| 396 | shmctl = 396, | |
| 397 | shmat = 397, | |
| 398 | shmdt = 398, | |
| 399 | msgget = 399, | |
| 400 | msgsnd = 400, | |
| 401 | msgrcv = 401, | |
| 402 | msgctl = 402, | |
| 403 | clock_gettime64 = 403, | |
| 404 | clock_settime64 = 404, | |
| 405 | clock_adjtime64 = 405, | |
| 406 | clock_getres_time64 = 406, | |
| 407 | clock_nanosleep_time64 = 407, | |
| 408 | timer_gettime64 = 408, | |
| 409 | timer_settime64 = 409, | |
| 410 | timerfd_gettime64 = 410, | |
| 411 | timerfd_settime64 = 411, | |
| 412 | utimensat_time64 = 412, | |
| 413 | pselect6_time64 = 413, | |
| 414 | ppoll_time64 = 414, | |
| 415 | io_pgetevents_time64 = 416, | |
| 416 | recvmmsg_time64 = 417, | |
| 417 | mq_timedsend_time64 = 418, | |
| 418 | mq_timedreceive_time64 = 419, | |
| 419 | semtimedop_time64 = 420, | |
| 420 | rt_sigtimedwait_time64 = 421, | |
| 421 | futex_time64 = 422, | |
| 422 | sched_rr_get_interval_time64 = 423, | |
| 423 | pidfd_send_signal = 424, | |
| 424 | io_uring_setup = 425, | |
| 425 | io_uring_enter = 426, | |
| 426 | io_uring_register = 427, | |
| 427 | open_tree = 428, | |
| 428 | move_mount = 429, | |
| 429 | fsopen = 430, | |
| 430 | fsconfig = 431, | |
| 431 | fsmount = 432, | |
| 432 | fspick = 433, | |
| 433 | pidfd_open = 434, | |
| 434 | clone3 = 435, | |
| 435 | close_range = 436, | |
| 436 | openat2 = 437, | |
| 437 | pidfd_getfd = 438, | |
| 438 | faccessat2 = 439, | |
| 439 | process_madvise = 440, | |
| 440 | }; | |
| 441 | ||
| 442 | pub const O_CREAT = 0o100; | |
| 443 | pub const O_EXCL = 0o200; | |
| 444 | pub const O_NOCTTY = 0o400; | |
| 445 | pub const O_TRUNC = 0o1000; | |
| 446 | pub const O_APPEND = 0o2000; | |
| 447 | pub const O_NONBLOCK = 0o4000; | |
| 448 | pub const O_DSYNC = 0o10000; | |
| 449 | pub const O_SYNC = 0o4010000; | |
| 450 | pub const O_RSYNC = 0o4010000; | |
| 451 | pub const O_DIRECTORY = 0o40000; | |
| 452 | pub const O_NOFOLLOW = 0o100000; | |
| 453 | pub const O_CLOEXEC = 0o2000000; | |
| 454 | ||
| 455 | pub const O_ASYNC = 0o20000; | |
| 456 | pub const O_DIRECT = 0o400000; | |
| 457 | pub const O_LARGEFILE = 0o200000; | |
| 458 | pub const O_NOATIME = 0o1000000; | |
| 459 | pub const O_PATH = 0o10000000; | |
| 460 | pub const O_TMPFILE = 0o20040000; | |
| 461 | pub const O_NDELAY = O_NONBLOCK; | |
| 462 | ||
| 463 | pub const F_DUPFD = 0; | |
| 464 | pub const F_GETFD = 1; | |
| 465 | pub const F_SETFD = 2; | |
| 466 | pub const F_GETFL = 3; | |
| 467 | pub const F_SETFL = 4; | |
| 468 | ||
| 469 | pub const F_SETOWN = 8; | |
| 470 | pub const F_GETOWN = 9; | |
| 471 | pub const F_SETSIG = 10; | |
| 472 | pub const F_GETSIG = 11; | |
| 473 | ||
| 474 | pub const F_GETLK = 12; | |
| 475 | pub const F_SETLK = 13; | |
| 476 | pub const F_SETLKW = 14; | |
| 477 | ||
| 478 | pub const F_SETOWN_EX = 15; | |
| 479 | pub const F_GETOWN_EX = 16; | |
| 480 | ||
| 481 | pub const F_GETOWNER_UIDS = 17; | |
| 482 | ||
| 483 | pub const F_RDLCK = 0; | |
| 484 | pub const F_WRLCK = 1; | |
| 485 | pub const F_UNLCK = 2; | |
| 486 | ||
| 487 | pub const LOCK_SH = 1; | |
| 488 | pub const LOCK_EX = 2; | |
| 489 | pub const LOCK_UN = 8; | |
| 490 | pub const LOCK_NB = 4; | |
| 491 | ||
| 492 | /// stack-like segment | |
| 493 | pub const MAP_GROWSDOWN = 0x0100; | |
| 494 | ||
| 495 | /// ETXTBSY | |
| 496 | pub const MAP_DENYWRITE = 0x0800; | |
| 497 | ||
| 498 | /// mark it as an executable | |
| 499 | pub const MAP_EXECUTABLE = 0x1000; | |
| 500 | ||
| 501 | /// pages are locked | |
| 502 | pub const MAP_LOCKED = 0x0080; | |
| 503 | ||
| 504 | /// don't check for reservations | |
| 505 | pub const MAP_NORESERVE = 0x0040; | |
| 506 | ||
| 507 | pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; | |
| 508 | pub const VDSO_CGT_VER = "LINUX_2.6.15"; | |
| 509 | ||
| 510 | pub const Flock = extern struct { | |
| 511 | l_type: i16, | |
| 512 | l_whence: i16, | |
| 513 | l_start: off_t, | |
| 514 | l_len: off_t, | |
| 515 | l_pid: pid_t, | |
| 516 | }; | |
| 517 | ||
| 518 | pub const msghdr = extern struct { | |
| 519 | msg_name: ?*sockaddr, | |
| 520 | msg_namelen: socklen_t, | |
| 521 | msg_iov: [*]iovec, | |
| 522 | msg_iovlen: usize, | |
| 523 | msg_control: ?*c_void, | |
| 524 | msg_controllen: socklen_t, | |
| 525 | msg_flags: i32, | |
| 526 | }; | |
| 527 | ||
| 528 | pub const msghdr_const = extern struct { | |
| 529 | msg_name: ?*const sockaddr, | |
| 530 | msg_namelen: socklen_t, | |
| 531 | msg_iov: [*]iovec_const, | |
| 532 | msg_iovlen: usize, | |
| 533 | msg_control: ?*c_void, | |
| 534 | msg_controllen: socklen_t, | |
| 535 | msg_flags: i32, | |
| 536 | }; | |
| 537 | ||
| 538 | pub const blksize_t = i32; | |
| 539 | pub const nlink_t = u32; | |
| 540 | pub const time_t = isize; | |
| 541 | pub const mode_t = u32; | |
| 542 | pub const off_t = i64; | |
| 543 | pub const ino_t = u64; | |
| 544 | pub const dev_t = u64; | |
| 545 | pub const blkcnt_t = i64; | |
| 546 | ||
| 547 | // The `stat` definition used by the Linux kernel. | |
| 548 | pub const kernel_stat = extern struct { | |
| 549 | dev: dev_t, | |
| 550 | ino: ino_t, | |
| 551 | mode: mode_t, | |
| 552 | nlink: nlink_t, | |
| 553 | uid: uid_t, | |
| 554 | gid: gid_t, | |
| 555 | rdev: dev_t, | |
| 556 | __rdev_padding: i16, | |
| 557 | size: off_t, | |
| 558 | blksize: blksize_t, | |
| 559 | blocks: blkcnt_t, | |
| 560 | __atim32: timespec32, | |
| 561 | __mtim32: timespec32, | |
| 562 | __ctim32: timespec32, | |
| 563 | __unused: [2]u32, | |
| 564 | atim: timespec, | |
| 565 | mtim: timespec, | |
| 566 | ctim: timespec, | |
| 567 | ||
| 568 | const timespec32 = extern struct { | |
| 569 | tv_sec: i32, | |
| 570 | tv_nsec: i32, | |
| 571 | }; | |
| 572 | ||
| 573 | pub fn atime(self: @This()) timespec { | |
| 574 | return self.atim; | |
| 575 | } | |
| 576 | ||
| 577 | pub fn mtime(self: @This()) timespec { | |
| 578 | return self.mtim; | |
| 579 | } | |
| 580 | ||
| 581 | pub fn ctime(self: @This()) timespec { | |
| 582 | return self.ctim; | |
| 583 | } | |
| 584 | }; | |
| 585 | ||
| 586 | // The `stat64` definition used by the libc. | |
| 587 | pub const libc_stat = kernel_stat; | |
| 588 | ||
| 589 | pub const timespec = extern struct { | |
| 590 | tv_sec: time_t, | |
| 591 | tv_nsec: isize, | |
| 592 | }; | |
| 593 | ||
| 594 | pub const timeval = extern struct { | |
| 595 | tv_sec: time_t, | |
| 596 | tv_usec: isize, | |
| 597 | }; | |
| 598 | ||
| 599 | pub const timezone = extern struct { | |
| 600 | tz_minuteswest: i32, | |
| 601 | tz_dsttime: i32, | |
| 602 | }; | |
| 603 | ||
| 604 | pub const greg_t = u32; | |
| 605 | pub const gregset_t = [48]greg_t; | |
| 606 | pub const fpregset_t = [33]f64; | |
| 607 | ||
| 608 | pub const vrregset = extern struct { | |
| 609 | vrregs: [32][4]u32, | |
| 610 | vrsave: u32, | |
| 611 | _pad: [2]u32, | |
| 612 | vscr: u32, | |
| 613 | }; | |
| 614 | pub const vrregset_t = vrregset; | |
| 615 | ||
| 616 | pub const mcontext_t = extern struct { | |
| 617 | gp_regs: gregset_t, | |
| 618 | fp_regs: fpregset_t, | |
| 619 | v_regs: vrregset_t align(16), | |
| 620 | }; | |
| 621 | ||
| 622 | pub const ucontext_t = extern struct { | |
| 623 | flags: u32, | |
| 624 | link: *ucontext_t, | |
| 625 | stack: stack_t, | |
| 626 | pad: [7]i32, | |
| 627 | regs: *mcontext_t, | |
| 628 | sigmask: sigset_t, | |
| 629 | pad2: [3]i32, | |
| 630 | mcontext: mcontext_t, | |
| 631 | }; | |
| 632 | ||
| 633 | pub const Elf_Symndx = u32; | |
| 634 | ||
| 635 | pub const MMAP2_UNIT = 4096; |
lib/std/os/bits/netbsd.zig+22-4| ... | ... | @@ -813,10 +813,6 @@ pub const sigset_t = extern struct { |
| 813 | 813 | __bits: [_SIG_WORDS]u32, |
| 814 | 814 | }; |
| 815 | 815 | |
| 816 | pub const SIG_ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize)); | |
| 817 | pub const SIG_DFL = @intToPtr(?Sigaction.sigaction_fn, 0); | |
| 818 | pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1); | |
| 819 | ||
| 820 | 816 | pub const empty_sigset = sigset_t{ .__bits = [_]u32{0} ** _SIG_WORDS }; |
| 821 | 817 | |
| 822 | 818 | // XXX x86_64 specific |
| ... | ... | @@ -1219,3 +1215,25 @@ pub const rlimit = extern struct { |
| 1219 | 1215 | pub const SHUT_RD = 0; |
| 1220 | 1216 | pub const SHUT_WR = 1; |
| 1221 | 1217 | pub const SHUT_RDWR = 2; |
| 1218 | ||
| 1219 | pub const nfds_t = u32; | |
| 1220 | ||
| 1221 | pub const pollfd = extern struct { | |
| 1222 | fd: fd_t, | |
| 1223 | events: i16, | |
| 1224 | revents: i16, | |
| 1225 | }; | |
| 1226 | ||
| 1227 | /// Testable events (may be specified in events field). | |
| 1228 | pub const POLLIN = 0x0001; | |
| 1229 | pub const POLLPRI = 0x0002; | |
| 1230 | pub const POLLOUT = 0x0004; | |
| 1231 | pub const POLLRDNORM = 0x0040; | |
| 1232 | pub const POLLWRNORM = POLLOUT; | |
| 1233 | pub const POLLRDBAND = 0x0080; | |
| 1234 | pub const POLLWRBAND = 0x0100; | |
| 1235 | ||
| 1236 | /// Non-testable events (may not be specified in events field). | |
| 1237 | pub const POLLERR = 0x0008; | |
| 1238 | pub const POLLHUP = 0x0010; | |
| 1239 | pub const POLLNVAL = 0x0020; |
lib/std/os/linux.zig+1| ... | ... | @@ -26,6 +26,7 @@ pub usingnamespace switch (builtin.arch) { |
| 26 | 26 | .riscv64 => @import("linux/riscv64.zig"), |
| 27 | 27 | .sparcv9 => @import("linux/sparc64.zig"), |
| 28 | 28 | .mips, .mipsel => @import("linux/mips.zig"), |
| 29 | .powerpc => @import("linux/powerpc.zig"), | |
| 29 | 30 | .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"), |
| 30 | 31 | else => struct {}, |
| 31 | 32 | }; |
lib/std/os/linux/io_uring.zig+1-1| ... | ... | @@ -1354,7 +1354,7 @@ test "timeout (after a relative time)" { |
| 1354 | 1354 | .flags = 0, |
| 1355 | 1355 | }, cqe); |
| 1356 | 1356 | |
| 1357 | // Tests should not depend on timings: skip test (result) if outside margin. | |
| 1357 | // Tests should not depend on timings: skip test if outside margin. | |
| 1358 | 1358 | if (!std.math.approxEqAbs(f64, ms, @intToFloat(f64, stopped - started), margin)) return error.SkipZigTest; |
| 1359 | 1359 | } |
| 1360 | 1360 |
lib/std/os/linux/powerpc.zig created+133| ... | ... | @@ -0,0 +1,133 @@ |
| 1 | // SPDX-License-Identifier: MIT | |
| 2 | // Copyright (c) 2015-2021 Zig Contributors | |
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | |
| 4 | // The MIT license requires this copyright notice to be included in all copies | |
| 5 | // and substantial portions of the software. | |
| 6 | ||
| 7 | usingnamespace @import("../bits.zig"); | |
| 8 | ||
| 9 | pub fn syscall0(number: SYS) usize { | |
| 10 | return asm volatile ( | |
| 11 | \\ sc | |
| 12 | \\ bns+ 1f | |
| 13 | \\ neg 3, 3 | |
| 14 | \\ 1: | |
| 15 | : [ret] "={r3}" (-> usize) | |
| 16 | : [number] "{r0}" (@enumToInt(number)) | |
| 17 | : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" | |
| 18 | ); | |
| 19 | } | |
| 20 | ||
| 21 | pub fn syscall1(number: SYS, arg1: usize) usize { | |
| 22 | return asm volatile ( | |
| 23 | \\ sc | |
| 24 | \\ bns+ 1f | |
| 25 | \\ neg 3, 3 | |
| 26 | \\ 1: | |
| 27 | : [ret] "={r3}" (-> usize) | |
| 28 | : [number] "{r0}" (@enumToInt(number)), | |
| 29 | [arg1] "{r3}" (arg1) | |
| 30 | : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" | |
| 31 | ); | |
| 32 | } | |
| 33 | ||
| 34 | pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize { | |
| 35 | return asm volatile ( | |
| 36 | \\ sc | |
| 37 | \\ bns+ 1f | |
| 38 | \\ neg 3, 3 | |
| 39 | \\ 1: | |
| 40 | : [ret] "={r3}" (-> usize) | |
| 41 | : [number] "{r0}" (@enumToInt(number)), | |
| 42 | [arg1] "{r3}" (arg1), | |
| 43 | [arg2] "{r4}" (arg2) | |
| 44 | : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" | |
| 45 | ); | |
| 46 | } | |
| 47 | ||
| 48 | pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize { | |
| 49 | return asm volatile ( | |
| 50 | \\ sc | |
| 51 | \\ bns+ 1f | |
| 52 | \\ neg 3, 3 | |
| 53 | \\ 1: | |
| 54 | : [ret] "={r3}" (-> usize) | |
| 55 | : [number] "{r0}" (@enumToInt(number)), | |
| 56 | [arg1] "{r3}" (arg1), | |
| 57 | [arg2] "{r4}" (arg2), | |
| 58 | [arg3] "{r5}" (arg3) | |
| 59 | : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" | |
| 60 | ); | |
| 61 | } | |
| 62 | ||
| 63 | pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { | |
| 64 | return asm volatile ( | |
| 65 | \\ sc | |
| 66 | \\ bns+ 1f | |
| 67 | \\ neg 3, 3 | |
| 68 | \\ 1: | |
| 69 | : [ret] "={r3}" (-> usize) | |
| 70 | : [number] "{r0}" (@enumToInt(number)), | |
| 71 | [arg1] "{r3}" (arg1), | |
| 72 | [arg2] "{r4}" (arg2), | |
| 73 | [arg3] "{r5}" (arg3), | |
| 74 | [arg4] "{r6}" (arg4) | |
| 75 | : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" | |
| 76 | ); | |
| 77 | } | |
| 78 | ||
| 79 | pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { | |
| 80 | return asm volatile ( | |
| 81 | \\ sc | |
| 82 | \\ bns+ 1f | |
| 83 | \\ neg 3, 3 | |
| 84 | \\ 1: | |
| 85 | : [ret] "={r3}" (-> usize) | |
| 86 | : [number] "{r0}" (@enumToInt(number)), | |
| 87 | [arg1] "{r3}" (arg1), | |
| 88 | [arg2] "{r4}" (arg2), | |
| 89 | [arg3] "{r5}" (arg3), | |
| 90 | [arg4] "{r6}" (arg4), | |
| 91 | [arg5] "{r7}" (arg5) | |
| 92 | : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" | |
| 93 | ); | |
| 94 | } | |
| 95 | ||
| 96 | pub fn syscall6( | |
| 97 | number: SYS, | |
| 98 | arg1: usize, | |
| 99 | arg2: usize, | |
| 100 | arg3: usize, | |
| 101 | arg4: usize, | |
| 102 | arg5: usize, | |
| 103 | arg6: usize, | |
| 104 | ) usize { | |
| 105 | return asm volatile ( | |
| 106 | \\ sc | |
| 107 | \\ bns+ 1f | |
| 108 | \\ neg 3, 3 | |
| 109 | \\ 1: | |
| 110 | : [ret] "={r3}" (-> usize) | |
| 111 | : [number] "{r0}" (@enumToInt(number)), | |
| 112 | [arg1] "{r3}" (arg1), | |
| 113 | [arg2] "{r4}" (arg2), | |
| 114 | [arg3] "{r5}" (arg3), | |
| 115 | [arg4] "{r6}" (arg4), | |
| 116 | [arg5] "{r7}" (arg5), | |
| 117 | [arg6] "{r8}" (arg6) | |
| 118 | : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" | |
| 119 | ); | |
| 120 | } | |
| 121 | ||
| 122 | /// This matches the libc clone function. | |
| 123 | pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: usize, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; | |
| 124 | ||
| 125 | pub const restore = restore_rt; | |
| 126 | ||
| 127 | pub fn restore_rt() callconv(.Naked) void { | |
| 128 | return asm volatile ("sc" | |
| 129 | : | |
| 130 | : [number] "{r0}" (@enumToInt(SYS.rt_sigreturn)) | |
| 131 | : "memory", "cr0", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12" | |
| 132 | ); | |
| 133 | } |
lib/std/special/c.zig+65| ... | ... | @@ -491,6 +491,71 @@ fn clone() callconv(.Naked) void { |
| 491 | 491 | \\ syscall |
| 492 | 492 | ); |
| 493 | 493 | }, |
| 494 | .powerpc => { | |
| 495 | // __clone(func, stack, flags, arg, ptid, tls, ctid) | |
| 496 | // 3, 4, 5, 6, 7, 8, 9 | |
| 497 | ||
| 498 | // syscall(SYS_clone, flags, stack, ptid, tls, ctid) | |
| 499 | // 0 3, 4, 5, 6, 7 | |
| 500 | asm volatile ( | |
| 501 | \\# store non-volatile regs r30, r31 on stack in order to put our | |
| 502 | \\# start func and its arg there | |
| 503 | \\stwu 30, -16(1) | |
| 504 | \\stw 31, 4(1) | |
| 505 | \\ | |
| 506 | \\# save r3 (func) into r30, and r6(arg) into r31 | |
| 507 | \\mr 30, 3 | |
| 508 | \\mr 31, 6 | |
| 509 | \\ | |
| 510 | \\# create initial stack frame for new thread | |
| 511 | \\clrrwi 4, 4, 4 | |
| 512 | \\li 0, 0 | |
| 513 | \\stwu 0, -16(4) | |
| 514 | \\ | |
| 515 | \\#move c into first arg | |
| 516 | \\mr 3, 5 | |
| 517 | \\#mr 4, 4 | |
| 518 | \\mr 5, 7 | |
| 519 | \\mr 6, 8 | |
| 520 | \\mr 7, 9 | |
| 521 | \\ | |
| 522 | \\# move syscall number into r0 | |
| 523 | \\li 0, 120 | |
| 524 | \\ | |
| 525 | \\sc | |
| 526 | \\ | |
| 527 | \\# check for syscall error | |
| 528 | \\bns+ 1f # jump to label 1 if no summary overflow. | |
| 529 | \\#else | |
| 530 | \\neg 3, 3 #negate the result (errno) | |
| 531 | \\1: | |
| 532 | \\# compare sc result with 0 | |
| 533 | \\cmpwi cr7, 3, 0 | |
| 534 | \\ | |
| 535 | \\# if not 0, jump to end | |
| 536 | \\bne cr7, 2f | |
| 537 | \\ | |
| 538 | \\#else: we're the child | |
| 539 | \\#call funcptr: move arg (d) into r3 | |
| 540 | \\mr 3, 31 | |
| 541 | \\#move r30 (funcptr) into CTR reg | |
| 542 | \\mtctr 30 | |
| 543 | \\# call CTR reg | |
| 544 | \\bctrl | |
| 545 | \\# mov SYS_exit into r0 (the exit param is already in r3) | |
| 546 | \\li 0, 1 | |
| 547 | \\sc | |
| 548 | \\ | |
| 549 | \\2: | |
| 550 | \\ | |
| 551 | \\# restore stack | |
| 552 | \\lwz 30, 0(1) | |
| 553 | \\lwz 31, 4(1) | |
| 554 | \\addi 1, 1, 16 | |
| 555 | \\ | |
| 556 | \\blr | |
| 557 | ); | |
| 558 | }, | |
| 494 | 559 | .powerpc64, .powerpc64le => { |
| 495 | 560 | // __clone(func, stack, flags, arg, ptid, tls, ctid) |
| 496 | 561 | // 3, 4, 5, 6, 7, 8, 9 |
lib/std/special/docs/index.html+16-5| ... | ... | @@ -43,6 +43,8 @@ |
| 43 | 43 | |
| 44 | 44 | /* layout */ |
| 45 | 45 | .canvas { |
| 46 | display: flex; | |
| 47 | flex-direction: column; | |
| 46 | 48 | width: 100vw; |
| 47 | 49 | height: 100vh; |
| 48 | 50 | overflow: hidden; |
| ... | ... | @@ -53,12 +55,21 @@ |
| 53 | 55 | background-color: var(--bg-color); |
| 54 | 56 | } |
| 55 | 57 | |
| 58 | .banner { | |
| 59 | background-color: darkred; | |
| 60 | text-align: center; | |
| 61 | color: white; | |
| 62 | padding: 15px 5px; | |
| 63 | } | |
| 64 | ||
| 65 | .banner a { | |
| 66 | color: bisque; | |
| 67 | text-decoration: underline; | |
| 68 | } | |
| 69 | ||
| 56 | 70 | .flex-main { |
| 57 | 71 | display: flex; |
| 58 | width: 100%; | |
| 59 | height: 100%; | |
| 60 | justify-content: center; | |
| 61 | ||
| 72 | overflow-y: hidden; | |
| 62 | 73 | z-index: 100; |
| 63 | 74 | } |
| 64 | 75 | |
| ... | ... | @@ -515,7 +526,7 @@ |
| 515 | 526 | </style> |
| 516 | 527 | </head> |
| 517 | 528 | <body class="canvas"> |
| 518 | <div style="background-color: darkred; width: 100vw; text-align: center; color: white; padding: 15px 5px;">These docs are experimental. <a style="color: bisque;text-decoration: underline;" href="https://kristoff.it/blog/zig-new-relationship-llvm/">Progress depends on the self-hosted compiler</a>, <a style="color: bisque;text-decoration: underline;" href="https://github.com/ziglang/zig/wiki/How-to-read-the-standard-library-source-code">consider reading the stlib source in the meantime</a>.</div> | |
| 529 | <div class="banner">These docs are experimental. <a href="https://kristoff.it/blog/zig-new-relationship-llvm/">Progress depends on the self-hosted compiler</a>, <a href="https://github.com/ziglang/zig/wiki/How-to-read-the-standard-library-source-code">consider reading the stdlib source in the meantime</a>.</div> | |
| 519 | 530 | <div class="flex-main"> |
| 520 | 531 | <div class="flex-filler"></div> |
| 521 | 532 | <div class="flex-left sidebar"> |
lib/std/special/docs/main.js+7-1| ... | ... | @@ -1844,7 +1844,13 @@ |
| 1844 | 1844 | var oldHash = location.hash; |
| 1845 | 1845 | var parts = oldHash.split("?"); |
| 1846 | 1846 | var newPart2 = (domSearch.value === "") ? "" : ("?" + domSearch.value); |
| 1847 | location.hash = (parts.length === 1) ? (oldHash + newPart2) : (parts[0] + newPart2); | |
| 1847 | var newHash = (oldHash === "" ? "#" : parts[0]) + newPart2; | |
| 1848 | // create a history entry only once per search | |
| 1849 | if (parts.length === 1) { | |
| 1850 | location.assign(newHash); | |
| 1851 | } else { | |
| 1852 | location.replace(newHash); | |
| 1853 | } | |
| 1848 | 1854 | } |
| 1849 | 1855 | function getSearchTerms() { |
| 1850 | 1856 | var list = curNavSearch.trim().split(/[ \r\n\t]+/); |
lib/std/start.zig+102-27| ... | ... | @@ -7,7 +7,7 @@ |
| 7 | 7 | |
| 8 | 8 | const root = @import("root"); |
| 9 | 9 | const std = @import("std.zig"); |
| 10 | const builtin = std.builtin; | |
| 10 | const builtin = @import("builtin"); | |
| 11 | 11 | const assert = std.debug.assert; |
| 12 | 12 | const uefi = std.os.uefi; |
| 13 | 13 | const tlcsprng = @import("crypto/tlcsprng.zig"); |
| ... | ... | @@ -17,39 +17,101 @@ var argc_argv_ptr: [*]usize = undefined; |
| 17 | 17 | const start_sym_name = if (builtin.arch.isMIPS()) "__start" else "_start"; |
| 18 | 18 | |
| 19 | 19 | comptime { |
| 20 | if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) { | |
| 21 | if (builtin.os.tag == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { | |
| 22 | @export(_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" }); | |
| 20 | // The self-hosted compiler is not fully capable of handling all of this start.zig file. | |
| 21 | // Until then, we have simplified logic here for self-hosted. TODO remove this once | |
| 22 | // self-hosted is capable enough to handle all of the real start.zig logic. | |
| 23 | if (builtin.zig_is_stage2) { | |
| 24 | if (builtin.output_mode == .Exe) { | |
| 25 | if (builtin.link_libc or builtin.object_format == .c) { | |
| 26 | if (!@hasDecl(root, "main")) { | |
| 27 | @export(main2, "main"); | |
| 28 | } | |
| 29 | } else { | |
| 30 | if (!@hasDecl(root, "_start")) { | |
| 31 | @export(_start2, "_start"); | |
| 32 | } | |
| 33 | } | |
| 23 | 34 | } |
| 24 | } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { | |
| 25 | if (builtin.link_libc and @hasDecl(root, "main")) { | |
| 26 | if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { | |
| 27 | @export(main, .{ .name = "main", .linkage = .Weak }); | |
| 35 | } else { | |
| 36 | if (builtin.output_mode == .Lib and builtin.link_mode == .Dynamic) { | |
| 37 | if (builtin.os.tag == .windows and !@hasDecl(root, "_DllMainCRTStartup")) { | |
| 38 | @export(_DllMainCRTStartup, .{ .name = "_DllMainCRTStartup" }); | |
| 28 | 39 | } |
| 29 | } else if (builtin.os.tag == .windows) { | |
| 30 | if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and | |
| 31 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) | |
| 32 | { | |
| 33 | @export(WinStartup, .{ .name = "wWinMainCRTStartup" }); | |
| 34 | } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and | |
| 35 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) | |
| 36 | { | |
| 37 | @compileError("WinMain not supported; declare wWinMain or main instead"); | |
| 38 | } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and | |
| 39 | !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) | |
| 40 | { | |
| 41 | @export(wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" }); | |
| 40 | } else if (builtin.output_mode == .Exe or @hasDecl(root, "main")) { | |
| 41 | if (builtin.link_libc and @hasDecl(root, "main")) { | |
| 42 | if (@typeInfo(@TypeOf(root.main)).Fn.calling_convention != .C) { | |
| 43 | @export(main, .{ .name = "main", .linkage = .Weak }); | |
| 44 | } | |
| 45 | } else if (builtin.os.tag == .windows) { | |
| 46 | if (!@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and | |
| 47 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) | |
| 48 | { | |
| 49 | @export(WinStartup, .{ .name = "wWinMainCRTStartup" }); | |
| 50 | } else if (@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup") and | |
| 51 | !@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup")) | |
| 52 | { | |
| 53 | @compileError("WinMain not supported; declare wWinMain or main instead"); | |
| 54 | } else if (@hasDecl(root, "wWinMain") and !@hasDecl(root, "wWinMainCRTStartup") and | |
| 55 | !@hasDecl(root, "WinMain") and !@hasDecl(root, "WinMainCRTStartup")) | |
| 56 | { | |
| 57 | @export(wWinMainCRTStartup, .{ .name = "wWinMainCRTStartup" }); | |
| 58 | } | |
| 59 | } else if (builtin.os.tag == .uefi) { | |
| 60 | if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" }); | |
| 61 | } else if (builtin.arch.isWasm() and builtin.os.tag == .freestanding) { | |
| 62 | if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name }); | |
| 63 | } else if (builtin.os.tag != .other and builtin.os.tag != .freestanding) { | |
| 64 | if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name }); | |
| 42 | 65 | } |
| 43 | } else if (builtin.os.tag == .uefi) { | |
| 44 | if (!@hasDecl(root, "EfiMain")) @export(EfiMain, .{ .name = "EfiMain" }); | |
| 45 | } else if (builtin.arch.isWasm() and builtin.os.tag == .freestanding) { | |
| 46 | if (!@hasDecl(root, start_sym_name)) @export(wasm_freestanding_start, .{ .name = start_sym_name }); | |
| 47 | } else if (builtin.os.tag != .other and builtin.os.tag != .freestanding) { | |
| 48 | if (!@hasDecl(root, start_sym_name)) @export(_start, .{ .name = start_sym_name }); | |
| 49 | 66 | } |
| 50 | 67 | } |
| 51 | 68 | } |
| 52 | 69 | |
| 70 | // Simplified start code for stage2 until it supports more language features /// | |
| 71 | ||
| 72 | fn main2() callconv(.C) c_int { | |
| 73 | root.main(); | |
| 74 | return 0; | |
| 75 | } | |
| 76 | ||
| 77 | fn _start2() callconv(.Naked) noreturn { | |
| 78 | root.main(); | |
| 79 | exit2(0); | |
| 80 | } | |
| 81 | ||
| 82 | fn exit2(code: u8) noreturn { | |
| 83 | switch (builtin.arch) { | |
| 84 | .x86_64 => { | |
| 85 | asm volatile ("syscall" | |
| 86 | : | |
| 87 | : [number] "{rax}" (231), | |
| 88 | [arg1] "{rdi}" (code) | |
| 89 | : "rcx", "r11", "memory" | |
| 90 | ); | |
| 91 | }, | |
| 92 | .arm => { | |
| 93 | asm volatile ("svc #0" | |
| 94 | : | |
| 95 | : [number] "{r7}" (1), | |
| 96 | [arg1] "{r0}" (code) | |
| 97 | : "memory" | |
| 98 | ); | |
| 99 | }, | |
| 100 | .aarch64 => { | |
| 101 | asm volatile ("svc #0" | |
| 102 | : | |
| 103 | : [number] "{x8}" (93), | |
| 104 | [arg1] "{x0}" (code) | |
| 105 | : "memory", "cc" | |
| 106 | ); | |
| 107 | }, | |
| 108 | else => @compileError("TODO"), | |
| 109 | } | |
| 110 | unreachable; | |
| 111 | } | |
| 112 | ||
| 113 | //////////////////////////////////////////////////////////////////////////////// | |
| 114 | ||
| 53 | 115 | fn _DllMainCRTStartup( |
| 54 | 116 | hinstDLL: std.os.windows.HINSTANCE, |
| 55 | 117 | fdwReason: std.os.windows.DWORD, |
| ... | ... | @@ -135,6 +197,19 @@ fn _start() callconv(.Naked) noreturn { |
| 135 | 197 | : [argc] "={sp}" (-> [*]usize) |
| 136 | 198 | ); |
| 137 | 199 | }, |
| 200 | .powerpc => { | |
| 201 | // Setup the initial stack frame and clear the back chain pointer. | |
| 202 | argc_argv_ptr = asm volatile ( | |
| 203 | \\ mr 4, 1 | |
| 204 | \\ li 0, 0 | |
| 205 | \\ stwu 1,-16(1) | |
| 206 | \\ stw 0, 0(1) | |
| 207 | \\ mtlr 0 | |
| 208 | : [argc] "={r4}" (-> [*]usize) | |
| 209 | : | |
| 210 | : "r0" | |
| 211 | ); | |
| 212 | }, | |
| 138 | 213 | .powerpc64le => { |
| 139 | 214 | // Setup the initial stack frame and clear the back chain pointer. |
| 140 | 215 | // TODO: Support powerpc64 (big endian) on ELFv2. |
lib/std/std.zig+1-1| ... | ... | @@ -92,7 +92,7 @@ pub const zig = @import("zig.zig"); |
| 92 | 92 | pub const start = @import("start.zig"); |
| 93 | 93 | |
| 94 | 94 | // This forces the start.zig file to be imported, and the comptime logic inside that |
| 95 | // file decides whether to export any appropriate start symbols. | |
| 95 | // file decides whether to export any appropriate start symbols, and call main. | |
| 96 | 96 | comptime { |
| 97 | 97 | _ = start; |
| 98 | 98 | } |
lib/std/target.zig+1-1| ... | ... | @@ -1178,7 +1178,7 @@ pub const Target = struct { |
| 1178 | 1178 | return switch (arch) { |
| 1179 | 1179 | .arm, .armeb, .thumb, .thumbeb => &arm.cpu.generic, |
| 1180 | 1180 | .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic, |
| 1181 | .avr => &avr.cpu.avr1, | |
| 1181 | .avr => &avr.cpu.avr2, | |
| 1182 | 1182 | .bpfel, .bpfeb => &bpf.cpu.generic, |
| 1183 | 1183 | .hexagon => &hexagon.cpu.generic, |
| 1184 | 1184 | .mips, .mipsel => &mips.cpu.mips32, |
lib/std/testing.zig+3-3| ... | ... | @@ -431,7 +431,7 @@ fn printIndicatorLine(source: []const u8, indicator_index: usize) void { |
| 431 | 431 | |
| 432 | 432 | fn printWithVisibleNewlines(source: []const u8) void { |
| 433 | 433 | var i: usize = 0; |
| 434 | while (std.mem.indexOf(u8, source[i..], "\n")) |nl| : (i += nl + 1) { | |
| 434 | while (std.mem.indexOfScalar(u8, source[i..], '\n')) |nl| : (i += nl + 1) { | |
| 435 | 435 | printLine(source[i .. i + nl]); |
| 436 | 436 | } |
| 437 | 437 | print("{s}␃\n", .{source[i..]}); // End of Text symbol (ETX) |
| ... | ... | @@ -439,7 +439,7 @@ fn printWithVisibleNewlines(source: []const u8) void { |
| 439 | 439 | |
| 440 | 440 | fn printLine(line: []const u8) void { |
| 441 | 441 | if (line.len != 0) switch (line[line.len - 1]) { |
| 442 | ' ', '\t' => print("{s}⏎\n", .{line}), // Carriage return symbol, | |
| 442 | ' ', '\t' => return print("{s}⏎\n", .{line}), // Carriage return symbol, | |
| 443 | 443 | else => {}, |
| 444 | 444 | }; |
| 445 | 445 | print("{s}\n", .{line}); |
| ... | ... | @@ -451,7 +451,7 @@ test { |
| 451 | 451 | |
| 452 | 452 | /// Given a type, reference all the declarations inside, so that the semantic analyzer sees them. |
| 453 | 453 | pub fn refAllDecls(comptime T: type) void { |
| 454 | if (!@import("builtin").is_test) return; | |
| 454 | if (!std.builtin.is_test) return; | |
| 455 | 455 | inline for (std.meta.declarations(T)) |decl| { |
| 456 | 456 | _ = decl; |
| 457 | 457 | } |
lib/std/time.zig+6-2| ... | ... | @@ -271,7 +271,9 @@ test "timestamp" { |
| 271 | 271 | sleep(ns_per_ms); |
| 272 | 272 | const time_1 = milliTimestamp(); |
| 273 | 273 | const interval = time_1 - time_0; |
| 274 | testing.expect(interval > 0 and interval < margin); | |
| 274 | testing.expect(interval > 0); | |
| 275 | // Tests should not depend on timings: skip test if outside margin. | |
| 276 | if (!(interval < margin)) return error.SkipZigTest; | |
| 275 | 277 | } |
| 276 | 278 | |
| 277 | 279 | test "Timer" { |
| ... | ... | @@ -280,7 +282,9 @@ test "Timer" { |
| 280 | 282 | var timer = try Timer.start(); |
| 281 | 283 | sleep(10 * ns_per_ms); |
| 282 | 284 | const time_0 = timer.read(); |
| 283 | testing.expect(time_0 > 0 and time_0 < margin); | |
| 285 | testing.expect(time_0 > 0); | |
| 286 | // Tests should not depend on timings: skip test if outside margin. | |
| 287 | if (!(time_0 < margin)) return error.SkipZigTest; | |
| 284 | 288 | |
| 285 | 289 | const time_1 = timer.lap(); |
| 286 | 290 | testing.expect(time_1 >= time_0); |
lib/std/wasm.zig+6-1| ... | ... | @@ -5,6 +5,8 @@ |
| 5 | 5 | // and substantial portions of the software. |
| 6 | 6 | const testing = @import("std.zig").testing; |
| 7 | 7 | |
| 8 | // TODO: Add support for multi-byte ops (e.g. table operations) | |
| 9 | ||
| 8 | 10 | /// Wasm instruction opcodes |
| 9 | 11 | /// |
| 10 | 12 | /// All instructions are defined as per spec: |
| ... | ... | @@ -175,7 +177,7 @@ pub const Opcode = enum(u8) { |
| 175 | 177 | i32_reinterpret_f32 = 0xBC, |
| 176 | 178 | i64_reinterpret_f64 = 0xBD, |
| 177 | 179 | f32_reinterpret_i32 = 0xBE, |
| 178 | i64_reinterpret_i64 = 0xBF, | |
| 180 | f64_reinterpret_i64 = 0xBF, | |
| 179 | 181 | i32_extend8_s = 0xC0, |
| 180 | 182 | i32_extend16_s = 0xC1, |
| 181 | 183 | i64_extend8_s = 0xC2, |
| ... | ... | @@ -278,3 +280,6 @@ pub const block_empty: u8 = 0x40; |
| 278 | 280 | // binary constants |
| 279 | 281 | pub const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm |
| 280 | 282 | pub const version = [_]u8{ 0x01, 0x00, 0x00, 0x00 }; // version 1 |
| 283 | ||
| 284 | // Each wasm page size is 64kB | |
| 285 | pub const page_size = 64 * 1024; |
lib/std/zig.zig+32-12| ... | ... | @@ -18,34 +18,54 @@ pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; |
| 18 | 18 | |
| 19 | 19 | pub const SrcHash = [16]u8; |
| 20 | 20 | |
| 21 | /// If the source is small enough, it is used directly as the hash. | |
| 22 | /// If it is long, blake3 hash is computed. | |
| 23 | 21 | pub fn hashSrc(src: []const u8) SrcHash { |
| 24 | 22 | var out: SrcHash = undefined; |
| 25 | if (src.len <= @typeInfo(SrcHash).Array.len) { | |
| 26 | std.mem.copy(u8, &out, src); | |
| 27 | std.mem.set(u8, out[src.len..], 0); | |
| 28 | } else { | |
| 29 | std.crypto.hash.Blake3.hash(src, &out, .{}); | |
| 30 | } | |
| 23 | std.crypto.hash.Blake3.hash(src, &out, .{}); | |
| 31 | 24 | return out; |
| 32 | 25 | } |
| 33 | 26 | |
| 34 | pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { | |
| 27 | pub fn hashName(parent_hash: SrcHash, sep: []const u8, name: []const u8) SrcHash { | |
| 28 | var out: SrcHash = undefined; | |
| 29 | var hasher = std.crypto.hash.Blake3.init(.{}); | |
| 30 | hasher.update(&parent_hash); | |
| 31 | hasher.update(sep); | |
| 32 | hasher.update(name); | |
| 33 | hasher.final(&out); | |
| 34 | return out; | |
| 35 | } | |
| 36 | ||
| 37 | pub const Loc = struct { | |
| 38 | line: usize, | |
| 39 | column: usize, | |
| 40 | /// Does not include the trailing newline. | |
| 41 | source_line: []const u8, | |
| 42 | }; | |
| 43 | ||
| 44 | pub fn findLineColumn(source: []const u8, byte_offset: usize) Loc { | |
| 35 | 45 | var line: usize = 0; |
| 36 | 46 | var column: usize = 0; |
| 37 | for (source[0..byte_offset]) |byte| { | |
| 38 | switch (byte) { | |
| 47 | var line_start: usize = 0; | |
| 48 | var i: usize = 0; | |
| 49 | while (i < byte_offset) : (i += 1) { | |
| 50 | switch (source[i]) { | |
| 39 | 51 | '\n' => { |
| 40 | 52 | line += 1; |
| 41 | 53 | column = 0; |
| 54 | line_start = i + 1; | |
| 42 | 55 | }, |
| 43 | 56 | else => { |
| 44 | 57 | column += 1; |
| 45 | 58 | }, |
| 46 | 59 | } |
| 47 | 60 | } |
| 48 | return .{ .line = line, .column = column }; | |
| 61 | while (i < source.len and source[i] != '\n') { | |
| 62 | i += 1; | |
| 63 | } | |
| 64 | return .{ | |
| 65 | .line = line, | |
| 66 | .column = column, | |
| 67 | .source_line = source[line_start..i], | |
| 68 | }; | |
| 49 | 69 | } |
| 50 | 70 | |
| 51 | 71 | pub fn lineDelta(source: []const u8, start: usize, end: usize) isize { |
lib/std/zig/parser_test.zig+107-7| ... | ... | @@ -1801,17 +1801,21 @@ test "zig fmt: array literal with hint" { |
| 1801 | 1801 | ); |
| 1802 | 1802 | } |
| 1803 | 1803 | |
| 1804 | test "zig fmt: array literal veritical column alignment" { | |
| 1804 | test "zig fmt: array literal vertical column alignment" { | |
| 1805 | 1805 | try testTransform( |
| 1806 | 1806 | \\const a = []u8{ |
| 1807 | 1807 | \\ 1000, 200, |
| 1808 | 1808 | \\ 30, 4, |
| 1809 | \\ 50000, 60 | |
| 1809 | \\ 50000, 60, | |
| 1810 | 1810 | \\}; |
| 1811 | 1811 | \\const a = []u8{0, 1, 2, 3, 40, |
| 1812 | 1812 | \\ 4,5,600,7, |
| 1813 | 1813 | \\ 80, |
| 1814 | \\ 9, 10, 11, 0, 13, 14, 15}; | |
| 1814 | \\ 9, 10, 11, 0, 13, 14, 15,}; | |
| 1815 | \\const a = [12]u8{ | |
| 1816 | \\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | |
| 1817 | \\const a = [12]u8{ | |
| 1818 | \\ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, }; | |
| 1815 | 1819 | \\ |
| 1816 | 1820 | , |
| 1817 | 1821 | \\const a = []u8{ |
| ... | ... | @@ -1825,6 +1829,21 @@ test "zig fmt: array literal veritical column alignment" { |
| 1825 | 1829 | \\ 9, 10, 11, 0, 13, |
| 1826 | 1830 | \\ 14, 15, |
| 1827 | 1831 | \\}; |
| 1832 | \\const a = [12]u8{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; | |
| 1833 | \\const a = [12]u8{ | |
| 1834 | \\ 31, | |
| 1835 | \\ 28, | |
| 1836 | \\ 31, | |
| 1837 | \\ 30, | |
| 1838 | \\ 31, | |
| 1839 | \\ 30, | |
| 1840 | \\ 31, | |
| 1841 | \\ 31, | |
| 1842 | \\ 30, | |
| 1843 | \\ 31, | |
| 1844 | \\ 30, | |
| 1845 | \\ 31, | |
| 1846 | \\}; | |
| 1828 | 1847 | \\ |
| 1829 | 1848 | ); |
| 1830 | 1849 | } |
| ... | ... | @@ -2026,10 +2045,7 @@ test "zig fmt: add trailing comma to array literal" { |
| 2026 | 2045 | \\ return []u16{ |
| 2027 | 2046 | \\ 'm', 's', 'y', 's', '-', // hi |
| 2028 | 2047 | \\ }; |
| 2029 | \\ return []u16{ | |
| 2030 | \\ 'm', 's', 'y', 's', | |
| 2031 | \\ '-', | |
| 2032 | \\ }; | |
| 2048 | \\ return []u16{ 'm', 's', 'y', 's', '-' }; | |
| 2033 | 2049 | \\ return []u16{ 'm', 's', 'y', 's', '-' }; |
| 2034 | 2050 | \\} |
| 2035 | 2051 | \\ |
| ... | ... | @@ -4661,6 +4677,90 @@ test "zig fmt: insert trailing comma if there are comments between switch values |
| 4661 | 4677 | ); |
| 4662 | 4678 | } |
| 4663 | 4679 | |
| 4680 | test "zig fmt: insert trailing comma if comments in array init" { | |
| 4681 | try testTransform( | |
| 4682 | \\var a = .{ | |
| 4683 | \\ "foo", // | |
| 4684 | \\ "bar" | |
| 4685 | \\}; | |
| 4686 | \\var a = .{ | |
| 4687 | \\ "foo", | |
| 4688 | \\ "bar" // | |
| 4689 | \\}; | |
| 4690 | \\var a = .{ | |
| 4691 | \\ "foo", | |
| 4692 | \\ "//" | |
| 4693 | \\}; | |
| 4694 | \\var a = .{ | |
| 4695 | \\ "foo", | |
| 4696 | \\ "//" // | |
| 4697 | \\}; | |
| 4698 | \\ | |
| 4699 | , | |
| 4700 | \\var a = .{ | |
| 4701 | \\ "foo", // | |
| 4702 | \\ "bar", | |
| 4703 | \\}; | |
| 4704 | \\var a = .{ | |
| 4705 | \\ "foo", | |
| 4706 | \\ "bar", // | |
| 4707 | \\}; | |
| 4708 | \\var a = .{ "foo", "//" }; | |
| 4709 | \\var a = .{ | |
| 4710 | \\ "foo", | |
| 4711 | \\ "//", // | |
| 4712 | \\}; | |
| 4713 | \\ | |
| 4714 | ); | |
| 4715 | } | |
| 4716 | ||
| 4717 | test "zig fmt: make single-line if no trailing comma" { | |
| 4718 | try testTransform( | |
| 4719 | \\test "function call no trailing comma" { | |
| 4720 | \\ foo( | |
| 4721 | \\ 1, | |
| 4722 | \\ 2 | |
| 4723 | \\ ); | |
| 4724 | \\} | |
| 4725 | \\ | |
| 4726 | , | |
| 4727 | \\test "function call no trailing comma" { | |
| 4728 | \\ foo(1, 2); | |
| 4729 | \\} | |
| 4730 | \\ | |
| 4731 | ); | |
| 4732 | ||
| 4733 | try testTransform( | |
| 4734 | \\test "struct no trailing comma" { | |
| 4735 | \\ const a = .{ | |
| 4736 | \\ .foo = 1, | |
| 4737 | \\ .bar = 2 | |
| 4738 | \\ }; | |
| 4739 | \\} | |
| 4740 | \\ | |
| 4741 | , | |
| 4742 | \\test "struct no trailing comma" { | |
| 4743 | \\ const a = .{ .foo = 1, .bar = 2 }; | |
| 4744 | \\} | |
| 4745 | \\ | |
| 4746 | ); | |
| 4747 | ||
| 4748 | try testTransform( | |
| 4749 | \\test "array no trailing comma" { | |
| 4750 | \\ var stream = multiOutStream(.{ | |
| 4751 | \\ fbs1.outStream(), | |
| 4752 | \\ fbs2.outStream() | |
| 4753 | \\ }); | |
| 4754 | \\} | |
| 4755 | \\ | |
| 4756 | , | |
| 4757 | \\test "array no trailing comma" { | |
| 4758 | \\ var stream = multiOutStream(.{ fbs1.outStream(), fbs2.outStream() }); | |
| 4759 | \\} | |
| 4760 | \\ | |
| 4761 | ); | |
| 4762 | } | |
| 4763 | ||
| 4664 | 4764 | test "zig fmt: error for invalid bit range" { |
| 4665 | 4765 | try testError( |
| 4666 | 4766 | \\var x: []align(0:0:0)u8 = bar; |
lib/std/zig/perf_test.zig+7-3| ... | ... | @@ -9,6 +9,7 @@ const warn = std.debug.warn; |
| 9 | 9 | const Tokenizer = std.zig.Tokenizer; |
| 10 | 10 | const Parser = std.zig.Parser; |
| 11 | 11 | const io = std.io; |
| 12 | const fmtIntSizeBin = std.fmt.fmtIntSizeBin; | |
| 12 | 13 | |
| 13 | 14 | const source = @embedFile("../os.zig"); |
| 14 | 15 | var fixed_buffer_mem: [10 * 1024 * 1024]u8 = undefined; |
| ... | ... | @@ -25,12 +26,15 @@ pub fn main() !void { |
| 25 | 26 | const end = timer.read(); |
| 26 | 27 | memory_used /= iterations; |
| 27 | 28 | const elapsed_s = @intToFloat(f64, end - start) / std.time.ns_per_s; |
| 28 | const bytes_per_sec = @intToFloat(f64, source.len * iterations) / elapsed_s; | |
| 29 | const mb_per_sec = bytes_per_sec / (1024 * 1024); | |
| 29 | const bytes_per_sec_float = @intToFloat(f64, source.len * iterations) / elapsed_s; | |
| 30 | const bytes_per_sec = @floatToInt(u64, @floor(bytes_per_sec_float)); | |
| 30 | 31 | |
| 31 | 32 | var stdout_file = std.io.getStdOut(); |
| 32 | 33 | const stdout = stdout_file.writer(); |
| 33 | try stdout.print("{:.3} MiB/s, {} KiB used \n", .{ mb_per_sec, memory_used / 1024 }); | |
| 34 | try stdout.print("parsing speed: {:.2}/s, {:.2} used \n", .{ | |
| 35 | fmtIntSizeBin(bytes_per_sec), | |
| 36 | fmtIntSizeBin(memory_used), | |
| 37 | }); | |
| 34 | 38 | } |
| 35 | 39 | |
| 36 | 40 | fn testOnce() usize { |
lib/std/zig/render.zig+35-15| ... | ... | @@ -1632,9 +1632,10 @@ fn renderArrayInit( |
| 1632 | 1632 | } |
| 1633 | 1633 | } |
| 1634 | 1634 | |
| 1635 | const contains_newlines = !tree.tokensOnSameLine(array_init.ast.lbrace, rbrace); | |
| 1635 | const contains_comment = hasComment(tree, array_init.ast.lbrace, rbrace); | |
| 1636 | const contains_multiline_string = hasMultilineString(tree, array_init.ast.lbrace, rbrace); | |
| 1636 | 1637 | |
| 1637 | if (!trailing_comma and !contains_newlines) { | |
| 1638 | if (!trailing_comma and !contains_comment and !contains_multiline_string) { | |
| 1638 | 1639 | // Render all on one line, no trailing comma. |
| 1639 | 1640 | if (array_init.ast.elements.len == 1) { |
| 1640 | 1641 | // If there is only one element, we don't use spaces |
| ... | ... | @@ -1653,7 +1654,8 @@ fn renderArrayInit( |
| 1653 | 1654 | try renderToken(ais, tree, array_init.ast.lbrace, .newline); |
| 1654 | 1655 | |
| 1655 | 1656 | var expr_index: usize = 0; |
| 1656 | while (rowSize(tree, array_init.ast.elements[expr_index..], rbrace)) |row_size| { | |
| 1657 | while (true) { | |
| 1658 | const row_size = rowSize(tree, array_init.ast.elements[expr_index..], rbrace); | |
| 1657 | 1659 | const row_exprs = array_init.ast.elements[expr_index..]; |
| 1658 | 1660 | // A place to store the width of each expression and its column's maximum |
| 1659 | 1661 | const widths = try gpa.alloc(usize, row_exprs.len + row_size); |
| ... | ... | @@ -1686,7 +1688,7 @@ fn renderArrayInit( |
| 1686 | 1688 | const maybe_comma = expr_last_token + 1; |
| 1687 | 1689 | if (token_tags[maybe_comma] == .comma) { |
| 1688 | 1690 | if (hasSameLineComment(tree, maybe_comma)) |
| 1689 | break :sec_end i - this_line_size.? + 1; | |
| 1691 | break :sec_end i - this_line_size + 1; | |
| 1690 | 1692 | } |
| 1691 | 1693 | } |
| 1692 | 1694 | break :sec_end row_exprs.len; |
| ... | ... | @@ -2238,17 +2240,36 @@ fn renderToken(ais: *Ais, tree: ast.Tree, token_index: ast.TokenIndex, space: Sp |
| 2238 | 2240 | } |
| 2239 | 2241 | } |
| 2240 | 2242 | |
| 2241 | /// Returns true if there exists a comment between the start of token | |
| 2242 | /// `start_token` and the start of token `end_token`. This is used to determine | |
| 2243 | /// if e.g. a fn_proto should be wrapped and have a trailing comma inserted | |
| 2244 | /// even if there is none in the source. | |
| 2243 | /// Returns true if there exists a comment between any of the tokens from | |
| 2244 | /// `start_token` to `end_token`. This is used to determine if e.g. a | |
| 2245 | /// fn_proto should be wrapped and have a trailing comma inserted even if | |
| 2246 | /// there is none in the source. | |
| 2245 | 2247 | fn hasComment(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenIndex) bool { |
| 2246 | 2248 | const token_starts = tree.tokens.items(.start); |
| 2247 | 2249 | |
| 2248 | const start = token_starts[start_token]; | |
| 2249 | const end = token_starts[end_token]; | |
| 2250 | var i = start_token; | |
| 2251 | while (i < end_token) : (i += 1) { | |
| 2252 | const start = token_starts[i] + tree.tokenSlice(i).len; | |
| 2253 | const end = token_starts[i + 1]; | |
| 2254 | if (mem.indexOf(u8, tree.source[start..end], "//") != null) return true; | |
| 2255 | } | |
| 2256 | ||
| 2257 | return false; | |
| 2258 | } | |
| 2250 | 2259 | |
| 2251 | return mem.indexOf(u8, tree.source[start..end], "//") != null; | |
| 2260 | /// Returns true if there exists a multiline string literal between the start | |
| 2261 | /// of token `start_token` and the start of token `end_token`. | |
| 2262 | fn hasMultilineString(tree: ast.Tree, start_token: ast.TokenIndex, end_token: ast.TokenIndex) bool { | |
| 2263 | const token_tags = tree.tokens.items(.tag); | |
| 2264 | ||
| 2265 | for (token_tags[start_token..end_token]) |tag| { | |
| 2266 | switch (tag) { | |
| 2267 | .multiline_string_literal_line => return true, | |
| 2268 | else => continue, | |
| 2269 | } | |
| 2270 | } | |
| 2271 | ||
| 2272 | return false; | |
| 2252 | 2273 | } |
| 2253 | 2274 | |
| 2254 | 2275 | /// Assumes that start is the first byte past the previous token and |
| ... | ... | @@ -2500,9 +2521,8 @@ fn nodeCausesSliceOpSpace(tag: ast.Node.Tag) bool { |
| 2500 | 2521 | }; |
| 2501 | 2522 | } |
| 2502 | 2523 | |
| 2503 | // Returns the number of nodes in `expr` that are on the same line as `rtoken`, | |
| 2504 | // or null if they all are on the same line. | |
| 2505 | fn rowSize(tree: ast.Tree, exprs: []const ast.Node.Index, rtoken: ast.TokenIndex) ?usize { | |
| 2524 | // Returns the number of nodes in `expr` that are on the same line as `rtoken`. | |
| 2525 | fn rowSize(tree: ast.Tree, exprs: []const ast.Node.Index, rtoken: ast.TokenIndex) usize { | |
| 2506 | 2526 | const token_tags = tree.tokens.items(.tag); |
| 2507 | 2527 | |
| 2508 | 2528 | const first_token = tree.firstToken(exprs[0]); |
| ... | ... | @@ -2510,7 +2530,7 @@ fn rowSize(tree: ast.Tree, exprs: []const ast.Node.Index, rtoken: ast.TokenIndex |
| 2510 | 2530 | const maybe_comma = rtoken - 1; |
| 2511 | 2531 | if (token_tags[maybe_comma] == .comma) |
| 2512 | 2532 | return 1; |
| 2513 | return null; // no newlines | |
| 2533 | return exprs.len; // no newlines | |
| 2514 | 2534 | } |
| 2515 | 2535 | |
| 2516 | 2536 | var count: usize = 1; |
src/AstGen.zig+334-77| ... | ... | @@ -28,8 +28,6 @@ const BuiltinFn = @import("BuiltinFn.zig"); |
| 28 | 28 | instructions: std.MultiArrayList(zir.Inst) = .{}, |
| 29 | 29 | string_bytes: ArrayListUnmanaged(u8) = .{}, |
| 30 | 30 | extra: ArrayListUnmanaged(u32) = .{}, |
| 31 | decl_map: std.StringArrayHashMapUnmanaged(void) = .{}, | |
| 32 | decls: ArrayListUnmanaged(*Decl) = .{}, | |
| 33 | 31 | /// The end of special indexes. `zir.Inst.Ref` subtracts against this number to convert |
| 34 | 32 | /// to `zir.Inst.Index`. The default here is correct if there are 0 parameters. |
| 35 | 33 | ref_start_index: u32 = zir.Inst.Ref.typed_value_map.len, |
| ... | ... | @@ -110,8 +108,6 @@ pub fn deinit(astgen: *AstGen) void { |
| 110 | 108 | astgen.instructions.deinit(gpa); |
| 111 | 109 | astgen.extra.deinit(gpa); |
| 112 | 110 | astgen.string_bytes.deinit(gpa); |
| 113 | astgen.decl_map.deinit(gpa); | |
| 114 | astgen.decls.deinit(gpa); | |
| 115 | 111 | } |
| 116 | 112 | |
| 117 | 113 | pub const ResultLoc = union(enum) { |
| ... | ... | @@ -124,6 +120,9 @@ pub const ResultLoc = union(enum) { |
| 124 | 120 | /// The expression must generate a pointer rather than a value. For example, the left hand side |
| 125 | 121 | /// of an assignment uses this kind of result location. |
| 126 | 122 | ref, |
| 123 | /// The callee will accept a ref, but it is not necessary, and the `ResultLoc` | |
| 124 | /// may be treated as `none` instead. | |
| 125 | none_or_ref, | |
| 127 | 126 | /// The expression will be coerced into this type, but it will be evaluated as an rvalue. |
| 128 | 127 | ty: zir.Inst.Ref, |
| 129 | 128 | /// The expression must store its result into this typed pointer. The result instruction |
| ... | ... | @@ -157,7 +156,7 @@ pub const ResultLoc = union(enum) { |
| 157 | 156 | var elide_store_to_block_ptr_instructions = false; |
| 158 | 157 | switch (rl) { |
| 159 | 158 | // In this branch there will not be any store_to_block_ptr instructions. |
| 160 | .discard, .none, .ty, .ref => return .{ | |
| 159 | .discard, .none, .none_or_ref, .ty, .ref => return .{ | |
| 161 | 160 | .tag = .break_operand, |
| 162 | 161 | .elide_store_to_block_ptr_instructions = false, |
| 163 | 162 | }, |
| ... | ... | @@ -606,8 +605,13 @@ pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inn |
| 606 | 605 | |
| 607 | 606 | .deref => { |
| 608 | 607 | const lhs = try expr(gz, scope, .none, node_datas[node].lhs); |
| 609 | const result = try gz.addUnNode(.load, lhs, node); | |
| 610 | return rvalue(gz, scope, rl, result, node); | |
| 608 | switch (rl) { | |
| 609 | .ref, .none_or_ref => return lhs, | |
| 610 | else => { | |
| 611 | const result = try gz.addUnNode(.load, lhs, node); | |
| 612 | return rvalue(gz, scope, rl, result, node); | |
| 613 | }, | |
| 614 | } | |
| 611 | 615 | }, |
| 612 | 616 | .address_of => { |
| 613 | 617 | const result = try expr(gz, scope, .ref, node_datas[node].lhs); |
| ... | ... | @@ -816,10 +820,34 @@ pub fn structInitExpr( |
| 816 | 820 | } |
| 817 | 821 | switch (rl) { |
| 818 | 822 | .discard => return mod.failNode(scope, node, "TODO implement structInitExpr discard", .{}), |
| 819 | .none => return mod.failNode(scope, node, "TODO implement structInitExpr none", .{}), | |
| 823 | .none, .none_or_ref => return mod.failNode(scope, node, "TODO implement structInitExpr none", .{}), | |
| 820 | 824 | .ref => unreachable, // struct literal not valid as l-value |
| 821 | 825 | .ty => |ty_inst| { |
| 822 | return mod.failNode(scope, node, "TODO implement structInitExpr ty", .{}); | |
| 826 | const fields_list = try gpa.alloc(zir.Inst.StructInit.Item, struct_init.ast.fields.len); | |
| 827 | defer gpa.free(fields_list); | |
| 828 | ||
| 829 | for (struct_init.ast.fields) |field_init, i| { | |
| 830 | const name_token = tree.firstToken(field_init) - 2; | |
| 831 | const str_index = try gz.identAsString(name_token); | |
| 832 | ||
| 833 | const field_ty_inst = try gz.addPlNode(.field_type, field_init, zir.Inst.FieldType{ | |
| 834 | .container_type = ty_inst, | |
| 835 | .name_start = str_index, | |
| 836 | }); | |
| 837 | fields_list[i] = .{ | |
| 838 | .field_type = astgen.refToIndex(field_ty_inst).?, | |
| 839 | .init = try expr(gz, scope, .{ .ty = field_ty_inst }, field_init), | |
| 840 | }; | |
| 841 | } | |
| 842 | const init_inst = try gz.addPlNode(.struct_init, node, zir.Inst.StructInit{ | |
| 843 | .fields_len = @intCast(u32, fields_list.len), | |
| 844 | }); | |
| 845 | try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len + | |
| 846 | fields_list.len * @typeInfo(zir.Inst.StructInit.Item).Struct.fields.len); | |
| 847 | for (fields_list) |field| { | |
| 848 | _ = gz.astgen.addExtraAssumeCapacity(field); | |
| 849 | } | |
| 850 | return rvalue(gz, scope, rl, init_inst, node); | |
| 823 | 851 | }, |
| 824 | 852 | .ptr => |ptr_inst| { |
| 825 | 853 | const field_ptr_list = try gpa.alloc(zir.Inst.Index, struct_init.ast.fields.len); |
| ... | ... | @@ -1175,13 +1203,6 @@ fn blockExprStmts( |
| 1175 | 1203 | // in the above while loop. |
| 1176 | 1204 | const zir_tags = gz.astgen.instructions.items(.tag); |
| 1177 | 1205 | switch (zir_tags[inst]) { |
| 1178 | .@"const" => { | |
| 1179 | const tv = gz.astgen.instructions.items(.data)[inst].@"const"; | |
| 1180 | break :b switch (tv.ty.zigTypeTag()) { | |
| 1181 | .NoReturn, .Void => true, | |
| 1182 | else => false, | |
| 1183 | }; | |
| 1184 | }, | |
| 1185 | 1206 | // For some instructions, swap in a slightly different ZIR tag |
| 1186 | 1207 | // so we can avoid a separate ensure_result_used instruction. |
| 1187 | 1208 | .call_none_chkused => unreachable, |
| ... | ... | @@ -1248,7 +1269,10 @@ fn blockExprStmts( |
| 1248 | 1269 | .fn_type_var_args, |
| 1249 | 1270 | .fn_type_cc, |
| 1250 | 1271 | .fn_type_cc_var_args, |
| 1272 | .has_decl, | |
| 1251 | 1273 | .int, |
| 1274 | .float, | |
| 1275 | .float128, | |
| 1252 | 1276 | .intcast, |
| 1253 | 1277 | .int_type, |
| 1254 | 1278 | .is_non_null, |
| ... | ... | @@ -1321,12 +1345,18 @@ fn blockExprStmts( |
| 1321 | 1345 | .switch_capture_else, |
| 1322 | 1346 | .switch_capture_else_ref, |
| 1323 | 1347 | .struct_init_empty, |
| 1348 | .struct_init, | |
| 1349 | .field_type, | |
| 1324 | 1350 | .struct_decl, |
| 1325 | 1351 | .struct_decl_packed, |
| 1326 | 1352 | .struct_decl_extern, |
| 1327 | 1353 | .union_decl, |
| 1328 | 1354 | .enum_decl, |
| 1355 | .enum_decl_nonexhaustive, | |
| 1329 | 1356 | .opaque_decl, |
| 1357 | .int_to_enum, | |
| 1358 | .enum_to_int, | |
| 1359 | .type_info, | |
| 1330 | 1360 | => break :b false, |
| 1331 | 1361 | |
| 1332 | 1362 | // ZIR instructions that are always either `noreturn` or `void`. |
| ... | ... | @@ -1334,6 +1364,7 @@ fn blockExprStmts( |
| 1334 | 1364 | .dbg_stmt_node, |
| 1335 | 1365 | .ensure_result_used, |
| 1336 | 1366 | .ensure_result_non_error, |
| 1367 | .@"export", | |
| 1337 | 1368 | .set_eval_branch_quota, |
| 1338 | 1369 | .compile_log, |
| 1339 | 1370 | .ensure_err_payload_void, |
| ... | ... | @@ -1482,7 +1513,7 @@ fn varDecl( |
| 1482 | 1513 | init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node); |
| 1483 | 1514 | init_scope.rl_ty_inst = type_inst; |
| 1484 | 1515 | } else { |
| 1485 | const alloc = try init_scope.addUnNode(.alloc_inferred, undefined, node); | |
| 1516 | const alloc = try init_scope.addNode(.alloc_inferred, node); | |
| 1486 | 1517 | resolve_inferred_alloc = alloc; |
| 1487 | 1518 | init_scope.rl_ptr = alloc; |
| 1488 | 1519 | } |
| ... | ... | @@ -1557,7 +1588,7 @@ fn varDecl( |
| 1557 | 1588 | const alloc = try gz.addUnNode(.alloc_mut, type_inst, node); |
| 1558 | 1589 | break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } }; |
| 1559 | 1590 | } else a: { |
| 1560 | const alloc = try gz.addUnNode(.alloc_inferred_mut, undefined, node); | |
| 1591 | const alloc = try gz.addNode(.alloc_inferred_mut, node); | |
| 1561 | 1592 | resolve_inferred_alloc = alloc; |
| 1562 | 1593 | break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } }; |
| 1563 | 1594 | }; |
| ... | ... | @@ -1815,15 +1846,18 @@ fn containerDecl( |
| 1815 | 1846 | defer bit_bag.deinit(gpa); |
| 1816 | 1847 | |
| 1817 | 1848 | var cur_bit_bag: u32 = 0; |
| 1818 | var member_index: usize = 0; | |
| 1819 | while (true) { | |
| 1820 | const member_node = container_decl.ast.members[member_index]; | |
| 1849 | var field_index: usize = 0; | |
| 1850 | for (container_decl.ast.members) |member_node| { | |
| 1821 | 1851 | const member = switch (node_tags[member_node]) { |
| 1822 | 1852 | .container_field_init => tree.containerFieldInit(member_node), |
| 1823 | 1853 | .container_field_align => tree.containerFieldAlign(member_node), |
| 1824 | 1854 | .container_field => tree.containerField(member_node), |
| 1825 | else => unreachable, | |
| 1855 | else => continue, | |
| 1826 | 1856 | }; |
| 1857 | if (field_index % 16 == 0 and field_index != 0) { | |
| 1858 | try bit_bag.append(gpa, cur_bit_bag); | |
| 1859 | cur_bit_bag = 0; | |
| 1860 | } | |
| 1827 | 1861 | if (member.comptime_token) |comptime_token| { |
| 1828 | 1862 | return mod.failTok(scope, comptime_token, "TODO implement comptime struct fields", .{}); |
| 1829 | 1863 | } |
| ... | ... | @@ -1850,17 +1884,9 @@ fn containerDecl( |
| 1850 | 1884 | fields_data.appendAssumeCapacity(@enumToInt(default_inst)); |
| 1851 | 1885 | } |
| 1852 | 1886 | |
| 1853 | member_index += 1; | |
| 1854 | if (member_index < container_decl.ast.members.len) { | |
| 1855 | if (member_index % 16 == 0) { | |
| 1856 | try bit_bag.append(gpa, cur_bit_bag); | |
| 1857 | cur_bit_bag = 0; | |
| 1858 | } | |
| 1859 | } else { | |
| 1860 | break; | |
| 1861 | } | |
| 1887 | field_index += 1; | |
| 1862 | 1888 | } |
| 1863 | const empty_slot_count = 16 - ((member_index - 1) % 16); | |
| 1889 | const empty_slot_count = 16 - (field_index % 16); | |
| 1864 | 1890 | cur_bit_bag >>= @intCast(u5, empty_slot_count * 2); |
| 1865 | 1891 | |
| 1866 | 1892 | const result = try gz.addPlNode(tag, node, zir.Inst.StructDecl{ |
| ... | ... | @@ -1877,7 +1903,172 @@ fn containerDecl( |
| 1877 | 1903 | return mod.failTok(scope, container_decl.ast.main_token, "TODO AstGen for union decl", .{}); |
| 1878 | 1904 | }, |
| 1879 | 1905 | .keyword_enum => { |
| 1880 | return mod.failTok(scope, container_decl.ast.main_token, "TODO AstGen for enum decl", .{}); | |
| 1906 | if (container_decl.layout_token) |t| { | |
| 1907 | return mod.failTok(scope, t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{}); | |
| 1908 | } | |
| 1909 | // Count total fields as well as how many have explicitly provided tag values. | |
| 1910 | const counts = blk: { | |
| 1911 | var values: usize = 0; | |
| 1912 | var total_fields: usize = 0; | |
| 1913 | var decls: usize = 0; | |
| 1914 | var nonexhaustive_node: ast.Node.Index = 0; | |
| 1915 | for (container_decl.ast.members) |member_node| { | |
| 1916 | const member = switch (node_tags[member_node]) { | |
| 1917 | .container_field_init => tree.containerFieldInit(member_node), | |
| 1918 | .container_field_align => tree.containerFieldAlign(member_node), | |
| 1919 | .container_field => tree.containerField(member_node), | |
| 1920 | else => { | |
| 1921 | decls += 1; | |
| 1922 | continue; | |
| 1923 | }, | |
| 1924 | }; | |
| 1925 | if (member.comptime_token) |comptime_token| { | |
| 1926 | return mod.failTok(scope, comptime_token, "enum fields cannot be marked comptime", .{}); | |
| 1927 | } | |
| 1928 | if (member.ast.type_expr != 0) { | |
| 1929 | return mod.failNode(scope, member.ast.type_expr, "enum fields do not have types", .{}); | |
| 1930 | } | |
| 1931 | // Alignment expressions in enums are caught by the parser. | |
| 1932 | assert(member.ast.align_expr == 0); | |
| 1933 | ||
| 1934 | const name_token = member.ast.name_token; | |
| 1935 | if (mem.eql(u8, tree.tokenSlice(name_token), "_")) { | |
| 1936 | if (nonexhaustive_node != 0) { | |
| 1937 | const msg = msg: { | |
| 1938 | const msg = try mod.errMsg( | |
| 1939 | scope, | |
| 1940 | gz.nodeSrcLoc(member_node), | |
| 1941 | "redundant non-exhaustive enum mark", | |
| 1942 | .{}, | |
| 1943 | ); | |
| 1944 | errdefer msg.destroy(gpa); | |
| 1945 | const other_src = gz.nodeSrcLoc(nonexhaustive_node); | |
| 1946 | try mod.errNote(scope, other_src, msg, "other mark here", .{}); | |
| 1947 | break :msg msg; | |
| 1948 | }; | |
| 1949 | return mod.failWithOwnedErrorMsg(scope, msg); | |
| 1950 | } | |
| 1951 | nonexhaustive_node = member_node; | |
| 1952 | if (member.ast.value_expr != 0) { | |
| 1953 | return mod.failNode(scope, member.ast.value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{}); | |
| 1954 | } | |
| 1955 | continue; | |
| 1956 | } | |
| 1957 | total_fields += 1; | |
| 1958 | if (member.ast.value_expr != 0) { | |
| 1959 | values += 1; | |
| 1960 | } | |
| 1961 | } | |
| 1962 | break :blk .{ | |
| 1963 | .total_fields = total_fields, | |
| 1964 | .values = values, | |
| 1965 | .decls = decls, | |
| 1966 | .nonexhaustive_node = nonexhaustive_node, | |
| 1967 | }; | |
| 1968 | }; | |
| 1969 | if (counts.total_fields == 0) { | |
| 1970 | // One can construct an enum with no tags, and it functions the same as `noreturn`. But | |
| 1971 | // this is only useful for generic code; when explicitly using `enum {}` syntax, there | |
| 1972 | // must be at least one tag. | |
| 1973 | return mod.failNode(scope, node, "enum declarations must have at least one tag", .{}); | |
| 1974 | } | |
| 1975 | if (counts.nonexhaustive_node != 0 and arg_inst == .none) { | |
| 1976 | const msg = msg: { | |
| 1977 | const msg = try mod.errMsg( | |
| 1978 | scope, | |
| 1979 | gz.nodeSrcLoc(node), | |
| 1980 | "non-exhaustive enum missing integer tag type", | |
| 1981 | .{}, | |
| 1982 | ); | |
| 1983 | errdefer msg.destroy(gpa); | |
| 1984 | const other_src = gz.nodeSrcLoc(counts.nonexhaustive_node); | |
| 1985 | try mod.errNote(scope, other_src, msg, "marked non-exhaustive here", .{}); | |
| 1986 | break :msg msg; | |
| 1987 | }; | |
| 1988 | return mod.failWithOwnedErrorMsg(scope, msg); | |
| 1989 | } | |
| 1990 | if (counts.values == 0 and counts.decls == 0 and arg_inst == .none) { | |
| 1991 | // No explicitly provided tag values and no top level declarations! In this case, | |
| 1992 | // we can construct the enum type in AstGen and it will be correctly shared by all | |
| 1993 | // generic function instantiations and comptime function calls. | |
| 1994 | var new_decl_arena = std.heap.ArenaAllocator.init(gpa); | |
| 1995 | errdefer new_decl_arena.deinit(); | |
| 1996 | const arena = &new_decl_arena.allocator; | |
| 1997 | ||
| 1998 | var fields_map: std.StringArrayHashMapUnmanaged(void) = .{}; | |
| 1999 | try fields_map.ensureCapacity(arena, counts.total_fields); | |
| 2000 | for (container_decl.ast.members) |member_node| { | |
| 2001 | if (member_node == counts.nonexhaustive_node) | |
| 2002 | continue; | |
| 2003 | const member = switch (node_tags[member_node]) { | |
| 2004 | .container_field_init => tree.containerFieldInit(member_node), | |
| 2005 | .container_field_align => tree.containerFieldAlign(member_node), | |
| 2006 | .container_field => tree.containerField(member_node), | |
| 2007 | else => unreachable, // We checked earlier. | |
| 2008 | }; | |
| 2009 | const name_token = member.ast.name_token; | |
| 2010 | const tag_name = try mod.identifierTokenStringTreeArena( | |
| 2011 | scope, | |
| 2012 | name_token, | |
| 2013 | tree, | |
| 2014 | arena, | |
| 2015 | ); | |
| 2016 | const gop = fields_map.getOrPutAssumeCapacity(tag_name); | |
| 2017 | if (gop.found_existing) { | |
| 2018 | const msg = msg: { | |
| 2019 | const msg = try mod.errMsg( | |
| 2020 | scope, | |
| 2021 | gz.tokSrcLoc(name_token), | |
| 2022 | "duplicate enum tag", | |
| 2023 | .{}, | |
| 2024 | ); | |
| 2025 | errdefer msg.destroy(gpa); | |
| 2026 | // Iterate to find the other tag. We don't eagerly store it in a hash | |
| 2027 | // map because in the hot path there will be no compile error and we | |
| 2028 | // don't need to waste time with a hash map. | |
| 2029 | const bad_node = for (container_decl.ast.members) |other_member_node| { | |
| 2030 | const other_member = switch (node_tags[other_member_node]) { | |
| 2031 | .container_field_init => tree.containerFieldInit(other_member_node), | |
| 2032 | .container_field_align => tree.containerFieldAlign(other_member_node), | |
| 2033 | .container_field => tree.containerField(other_member_node), | |
| 2034 | else => unreachable, // We checked earlier. | |
| 2035 | }; | |
| 2036 | const other_tag_name = try mod.identifierTokenStringTreeArena( | |
| 2037 | scope, | |
| 2038 | other_member.ast.name_token, | |
| 2039 | tree, | |
| 2040 | arena, | |
| 2041 | ); | |
| 2042 | if (mem.eql(u8, tag_name, other_tag_name)) | |
| 2043 | break other_member_node; | |
| 2044 | } else unreachable; | |
| 2045 | const other_src = gz.nodeSrcLoc(bad_node); | |
| 2046 | try mod.errNote(scope, other_src, msg, "other tag here", .{}); | |
| 2047 | break :msg msg; | |
| 2048 | }; | |
| 2049 | return mod.failWithOwnedErrorMsg(scope, msg); | |
| 2050 | } | |
| 2051 | } | |
| 2052 | const enum_simple = try arena.create(Module.EnumSimple); | |
| 2053 | enum_simple.* = .{ | |
| 2054 | .owner_decl = astgen.decl, | |
| 2055 | .node_offset = astgen.decl.nodeIndexToRelative(node), | |
| 2056 | .fields = fields_map, | |
| 2057 | }; | |
| 2058 | const enum_ty = try Type.Tag.enum_simple.create(arena, enum_simple); | |
| 2059 | const enum_val = try Value.Tag.ty.create(arena, enum_ty); | |
| 2060 | const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{ | |
| 2061 | .ty = Type.initTag(.type), | |
| 2062 | .val = enum_val, | |
| 2063 | }); | |
| 2064 | const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl); | |
| 2065 | const result = try gz.addDecl(.decl_val, decl_index, node); | |
| 2066 | return rvalue(gz, scope, rl, result, node); | |
| 2067 | } | |
| 2068 | // In this case we must generate ZIR code for the tag values, similar to | |
| 2069 | // how structs are handled above. The new anonymous Decl will be created in | |
| 2070 | // Sema, not AstGen. | |
| 2071 | return mod.failNode(scope, node, "TODO AstGen for enum decl with decls or explicitly provided field values", .{}); | |
| 1881 | 2072 | }, |
| 1882 | 2073 | .keyword_opaque => { |
| 1883 | 2074 | const result = try gz.addNode(.opaque_decl, node); |
| ... | ... | @@ -1893,11 +2084,11 @@ fn errorSetDecl( |
| 1893 | 2084 | rl: ResultLoc, |
| 1894 | 2085 | node: ast.Node.Index, |
| 1895 | 2086 | ) InnerError!zir.Inst.Ref { |
| 1896 | const mod = gz.astgen.mod; | |
| 2087 | const astgen = gz.astgen; | |
| 2088 | const mod = astgen.mod; | |
| 1897 | 2089 | const tree = gz.tree(); |
| 1898 | 2090 | const main_tokens = tree.nodes.items(.main_token); |
| 1899 | 2091 | const token_tags = tree.tokens.items(.tag); |
| 1900 | const arena = gz.astgen.arena; | |
| 1901 | 2092 | |
| 1902 | 2093 | // Count how many fields there are. |
| 1903 | 2094 | const error_token = main_tokens[node]; |
| ... | ... | @@ -1914,6 +2105,11 @@ fn errorSetDecl( |
| 1914 | 2105 | } else unreachable; // TODO should not need else unreachable here |
| 1915 | 2106 | }; |
| 1916 | 2107 | |
| 2108 | const gpa = mod.gpa; | |
| 2109 | var new_decl_arena = std.heap.ArenaAllocator.init(gpa); | |
| 2110 | errdefer new_decl_arena.deinit(); | |
| 2111 | const arena = &new_decl_arena.allocator; | |
| 2112 | ||
| 1917 | 2113 | const fields = try arena.alloc([]const u8, count); |
| 1918 | 2114 | { |
| 1919 | 2115 | var tok_i = error_token + 2; |
| ... | ... | @@ -1922,7 +2118,7 @@ fn errorSetDecl( |
| 1922 | 2118 | switch (token_tags[tok_i]) { |
| 1923 | 2119 | .doc_comment, .comma => {}, |
| 1924 | 2120 | .identifier => { |
| 1925 | fields[field_i] = try mod.identifierTokenString(scope, tok_i); | |
| 2121 | fields[field_i] = try mod.identifierTokenStringTreeArena(scope, tok_i, tree, arena); | |
| 1926 | 2122 | field_i += 1; |
| 1927 | 2123 | }, |
| 1928 | 2124 | .r_brace => break, |
| ... | ... | @@ -1932,18 +2128,19 @@ fn errorSetDecl( |
| 1932 | 2128 | } |
| 1933 | 2129 | const error_set = try arena.create(Module.ErrorSet); |
| 1934 | 2130 | error_set.* = .{ |
| 1935 | .owner_decl = gz.astgen.decl, | |
| 1936 | .node_offset = gz.astgen.decl.nodeIndexToRelative(node), | |
| 2131 | .owner_decl = astgen.decl, | |
| 2132 | .node_offset = astgen.decl.nodeIndexToRelative(node), | |
| 1937 | 2133 | .names_ptr = fields.ptr, |
| 1938 | 2134 | .names_len = @intCast(u32, fields.len), |
| 1939 | 2135 | }; |
| 1940 | 2136 | const error_set_ty = try Type.Tag.error_set.create(arena, error_set); |
| 1941 | const typed_value = try arena.create(TypedValue); | |
| 1942 | typed_value.* = .{ | |
| 2137 | const error_set_val = try Value.Tag.ty.create(arena, error_set_ty); | |
| 2138 | const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{ | |
| 1943 | 2139 | .ty = Type.initTag(.type), |
| 1944 | .val = try Value.Tag.ty.create(arena, error_set_ty), | |
| 1945 | }; | |
| 1946 | const result = try gz.addConst(typed_value); | |
| 2140 | .val = error_set_val, | |
| 2141 | }); | |
| 2142 | const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl); | |
| 2143 | const result = try gz.addDecl(.decl_val, decl_index, node); | |
| 1947 | 2144 | return rvalue(gz, scope, rl, result, node); |
| 1948 | 2145 | } |
| 1949 | 2146 | |
| ... | ... | @@ -1980,7 +2177,7 @@ fn orelseCatchExpr( |
| 1980 | 2177 | // TODO handle catch |
| 1981 | 2178 | const operand_rl: ResultLoc = switch (block_scope.break_result_loc) { |
| 1982 | 2179 | .ref => .ref, |
| 1983 | .discard, .none, .block_ptr, .inferred_ptr => .none, | |
| 2180 | .discard, .none, .none_or_ref, .block_ptr, .inferred_ptr => .none, | |
| 1984 | 2181 | .ty => |elem_ty| blk: { |
| 1985 | 2182 | const wrapped_ty = try block_scope.addUnNode(.optional_type, elem_ty, node); |
| 1986 | 2183 | break :blk .{ .ty = wrapped_ty }; |
| ... | ... | @@ -2156,7 +2353,7 @@ pub fn fieldAccess( |
| 2156 | 2353 | .field_name_start = str_index, |
| 2157 | 2354 | }), |
| 2158 | 2355 | else => return rvalue(gz, scope, rl, try gz.addPlNode(.field_val, node, zir.Inst.Field{ |
| 2159 | .lhs = try expr(gz, scope, .none, object_node), | |
| 2356 | .lhs = try expr(gz, scope, .none_or_ref, object_node), | |
| 2160 | 2357 | .field_name_start = str_index, |
| 2161 | 2358 | }), node), |
| 2162 | 2359 | } |
| ... | ... | @@ -2179,7 +2376,7 @@ fn arrayAccess( |
| 2179 | 2376 | ), |
| 2180 | 2377 | else => return rvalue(gz, scope, rl, try gz.addBin( |
| 2181 | 2378 | .elem_val, |
| 2182 | try expr(gz, scope, .none, node_datas[node].lhs), | |
| 2379 | try expr(gz, scope, .none_or_ref, node_datas[node].lhs), | |
| 2183 | 2380 | try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs), |
| 2184 | 2381 | ), node), |
| 2185 | 2382 | } |
| ... | ... | @@ -3188,8 +3385,13 @@ fn switchExpr( |
| 3188 | 3385 | switch (strat.tag) { |
| 3189 | 3386 | .break_operand => { |
| 3190 | 3387 | // Switch expressions return `true` for `nodeMayNeedMemoryLocation` thus |
| 3191 | // this is always true. | |
| 3192 | assert(strat.elide_store_to_block_ptr_instructions); | |
| 3388 | // `elide_store_to_block_ptr_instructions` will either be true, | |
| 3389 | // or all prongs are noreturn. | |
| 3390 | if (!strat.elide_store_to_block_ptr_instructions) { | |
| 3391 | astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items); | |
| 3392 | astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items); | |
| 3393 | return astgen.indexToRef(switch_block); | |
| 3394 | } | |
| 3193 | 3395 | |
| 3194 | 3396 | // There will necessarily be a store_to_block_ptr for |
| 3195 | 3397 | // all prongs, except for prongs that ended with a noreturn instruction. |
| ... | ... | @@ -3418,7 +3620,8 @@ fn identifier( |
| 3418 | 3620 | const tracy = trace(@src()); |
| 3419 | 3621 | defer tracy.end(); |
| 3420 | 3622 | |
| 3421 | const mod = gz.astgen.mod; | |
| 3623 | const astgen = gz.astgen; | |
| 3624 | const mod = astgen.mod; | |
| 3422 | 3625 | const tree = gz.tree(); |
| 3423 | 3626 | const main_tokens = tree.nodes.items(.main_token); |
| 3424 | 3627 | |
| ... | ... | @@ -3451,7 +3654,7 @@ fn identifier( |
| 3451 | 3654 | const result = try gz.add(.{ |
| 3452 | 3655 | .tag = .int_type, |
| 3453 | 3656 | .data = .{ .int_type = .{ |
| 3454 | .src_node = gz.astgen.decl.nodeIndexToRelative(ident), | |
| 3657 | .src_node = astgen.decl.nodeIndexToRelative(ident), | |
| 3455 | 3658 | .signedness = signedness, |
| 3456 | 3659 | .bit_count = bit_count, |
| 3457 | 3660 | } }, |
| ... | ... | @@ -3474,9 +3677,13 @@ fn identifier( |
| 3474 | 3677 | .local_ptr => { |
| 3475 | 3678 | const local_ptr = s.cast(Scope.LocalPtr).?; |
| 3476 | 3679 | if (mem.eql(u8, local_ptr.name, ident_name)) { |
| 3477 | if (rl == .ref) return local_ptr.ptr; | |
| 3478 | const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident); | |
| 3479 | return rvalue(gz, scope, rl, loaded, ident); | |
| 3680 | switch (rl) { | |
| 3681 | .ref, .none_or_ref => return local_ptr.ptr, | |
| 3682 | else => { | |
| 3683 | const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident); | |
| 3684 | return rvalue(gz, scope, rl, loaded, ident); | |
| 3685 | }, | |
| 3686 | } | |
| 3480 | 3687 | } |
| 3481 | 3688 | s = local_ptr.parent; |
| 3482 | 3689 | }, |
| ... | ... | @@ -3485,15 +3692,15 @@ fn identifier( |
| 3485 | 3692 | }; |
| 3486 | 3693 | } |
| 3487 | 3694 | |
| 3488 | const gop = try gz.astgen.decl_map.getOrPut(mod.gpa, ident_name); | |
| 3489 | if (!gop.found_existing) { | |
| 3490 | const decl = mod.lookupDeclName(scope, ident_name) orelse | |
| 3491 | return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name}); | |
| 3492 | try gz.astgen.decls.append(mod.gpa, decl); | |
| 3493 | } | |
| 3494 | const decl_index = @intCast(u32, gop.index); | |
| 3695 | const decl = mod.lookupDeclName(scope, ident_name) orelse { | |
| 3696 | // TODO insert a "dependency on the non-existence of a decl" here to make this | |
| 3697 | // compile error go away when the decl is introduced. This data should be in a global | |
| 3698 | // sparse map since it is only relevant when a compile error occurs. | |
| 3699 | return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name}); | |
| 3700 | }; | |
| 3701 | const decl_index = try mod.declareDeclDependency(astgen.decl, decl); | |
| 3495 | 3702 | switch (rl) { |
| 3496 | .ref => return gz.addDecl(.decl_ref, decl_index, ident), | |
| 3703 | .ref, .none_or_ref => return gz.addDecl(.decl_ref, decl_index, ident), | |
| 3497 | 3704 | else => return rvalue(gz, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident), |
| 3498 | 3705 | } |
| 3499 | 3706 | } |
| ... | ... | @@ -3626,12 +3833,23 @@ fn floatLiteral( |
| 3626 | 3833 | const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) { |
| 3627 | 3834 | error.InvalidCharacter => unreachable, // validated by tokenizer |
| 3628 | 3835 | }; |
| 3629 | const typed_value = try arena.create(TypedValue); | |
| 3630 | typed_value.* = .{ | |
| 3631 | .ty = Type.initTag(.comptime_float), | |
| 3632 | .val = try Value.Tag.float_128.create(arena, float_number), | |
| 3633 | }; | |
| 3634 | const result = try gz.addConst(typed_value); | |
| 3836 | // If the value fits into a f32 without losing any precision, store it that way. | |
| 3837 | @setFloatMode(.Strict); | |
| 3838 | const smaller_float = @floatCast(f32, float_number); | |
| 3839 | const bigger_again: f128 = smaller_float; | |
| 3840 | if (bigger_again == float_number) { | |
| 3841 | const result = try gz.addFloat(smaller_float, node); | |
| 3842 | return rvalue(gz, scope, rl, result, node); | |
| 3843 | } | |
| 3844 | // We need to use 128 bits. Break the float into 4 u32 values so we can | |
| 3845 | // put it into the `extra` array. | |
| 3846 | const int_bits = @bitCast(u128, float_number); | |
| 3847 | const result = try gz.addPlNode(.float128, node, zir.Inst.Float128{ | |
| 3848 | .piece0 = @truncate(u32, int_bits), | |
| 3849 | .piece1 = @truncate(u32, int_bits >> 32), | |
| 3850 | .piece2 = @truncate(u32, int_bits >> 64), | |
| 3851 | .piece3 = @truncate(u32, int_bits >> 96), | |
| 3852 | }); | |
| 3635 | 3853 | return rvalue(gz, scope, rl, result, node); |
| 3636 | 3854 | } |
| 3637 | 3855 | |
| ... | ... | @@ -3697,7 +3915,7 @@ fn as( |
| 3697 | 3915 | ) InnerError!zir.Inst.Ref { |
| 3698 | 3916 | const dest_type = try typeExpr(gz, scope, lhs); |
| 3699 | 3917 | switch (rl) { |
| 3700 | .none, .discard, .ref, .ty => { | |
| 3918 | .none, .none_or_ref, .discard, .ref, .ty => { | |
| 3701 | 3919 | const result = try expr(gz, scope, .{ .ty = dest_type }, rhs); |
| 3702 | 3920 | return rvalue(gz, scope, rl, result, node); |
| 3703 | 3921 | }, |
| ... | ... | @@ -3781,7 +3999,7 @@ fn bitCast( |
| 3781 | 3999 | }); |
| 3782 | 4000 | return rvalue(gz, scope, rl, result, node); |
| 3783 | 4001 | }, |
| 3784 | .ref => unreachable, // `@bitCast` is not allowed as an r-value. | |
| 4002 | .ref, .none_or_ref => unreachable, // `@bitCast` is not allowed as an r-value. | |
| 3785 | 4003 | .ptr => |result_ptr| { |
| 3786 | 4004 | const casted_result_ptr = try gz.addUnNode(.bitcast_result_ptr, result_ptr, node); |
| 3787 | 4005 | return expr(gz, scope, .{ .ptr = casted_result_ptr }, rhs); |
| ... | ... | @@ -3882,11 +4100,11 @@ fn builtinCall( |
| 3882 | 4100 | return rvalue(gz, scope, rl, result, node); |
| 3883 | 4101 | }, |
| 3884 | 4102 | .breakpoint => { |
| 3885 | const result = try gz.add(.{ | |
| 4103 | _ = try gz.add(.{ | |
| 3886 | 4104 | .tag = .breakpoint, |
| 3887 | 4105 | .data = .{ .node = gz.astgen.decl.nodeIndexToRelative(node) }, |
| 3888 | 4106 | }); |
| 3889 | return rvalue(gz, scope, rl, result, node); | |
| 4107 | return rvalue(gz, scope, rl, .void_value, node); | |
| 3890 | 4108 | }, |
| 3891 | 4109 | .import => { |
| 3892 | 4110 | const target = try expr(gz, scope, .none, params[0]); |
| ... | ... | @@ -3943,6 +4161,50 @@ fn builtinCall( |
| 3943 | 4161 | .bit_cast => return bitCast(gz, scope, rl, node, params[0], params[1]), |
| 3944 | 4162 | .TypeOf => return typeOf(gz, scope, rl, node, params), |
| 3945 | 4163 | |
| 4164 | .int_to_enum => { | |
| 4165 | const result = try gz.addPlNode(.int_to_enum, node, zir.Inst.Bin{ | |
| 4166 | .lhs = try typeExpr(gz, scope, params[0]), | |
| 4167 | .rhs = try expr(gz, scope, .none, params[1]), | |
| 4168 | }); | |
| 4169 | return rvalue(gz, scope, rl, result, node); | |
| 4170 | }, | |
| 4171 | ||
| 4172 | .enum_to_int => { | |
| 4173 | const operand = try expr(gz, scope, .none, params[0]); | |
| 4174 | const result = try gz.addUnNode(.enum_to_int, operand, node); | |
| 4175 | return rvalue(gz, scope, rl, result, node); | |
| 4176 | }, | |
| 4177 | ||
| 4178 | .@"export" => { | |
| 4179 | // TODO: @export is supposed to be able to export things other than functions. | |
| 4180 | // Instead of `comptimeExpr` here we need `decl_ref`. | |
| 4181 | const fn_to_export = try comptimeExpr(gz, scope, .none, params[0]); | |
| 4182 | // TODO: the second parameter here is supposed to be | |
| 4183 | // `std.builtin.ExportOptions`, not a string. | |
| 4184 | const export_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); | |
| 4185 | _ = try gz.addPlNode(.@"export", node, zir.Inst.Bin{ | |
| 4186 | .lhs = fn_to_export, | |
| 4187 | .rhs = export_name, | |
| 4188 | }); | |
| 4189 | return rvalue(gz, scope, rl, .void_value, node); | |
| 4190 | }, | |
| 4191 | ||
| 4192 | .has_decl => { | |
| 4193 | const container_type = try typeExpr(gz, scope, params[0]); | |
| 4194 | const name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); | |
| 4195 | const result = try gz.addPlNode(.has_decl, node, zir.Inst.Bin{ | |
| 4196 | .lhs = container_type, | |
| 4197 | .rhs = name, | |
| 4198 | }); | |
| 4199 | return rvalue(gz, scope, rl, result, node); | |
| 4200 | }, | |
| 4201 | ||
| 4202 | .type_info => { | |
| 4203 | const operand = try typeExpr(gz, scope, params[0]); | |
| 4204 | const result = try gz.addUnNode(.type_info, operand, node); | |
| 4205 | return rvalue(gz, scope, rl, result, node); | |
| 4206 | }, | |
| 4207 | ||
| 3946 | 4208 | .add_with_overflow, |
| 3947 | 4209 | .align_cast, |
| 3948 | 4210 | .align_of, |
| ... | ... | @@ -3969,17 +4231,13 @@ fn builtinCall( |
| 3969 | 4231 | .div_floor, |
| 3970 | 4232 | .div_trunc, |
| 3971 | 4233 | .embed_file, |
| 3972 | .enum_to_int, | |
| 3973 | 4234 | .error_name, |
| 3974 | 4235 | .error_return_trace, |
| 3975 | 4236 | .err_set_cast, |
| 3976 | .@"export", | |
| 3977 | 4237 | .fence, |
| 3978 | 4238 | .field_parent_ptr, |
| 3979 | 4239 | .float_to_int, |
| 3980 | .has_decl, | |
| 3981 | 4240 | .has_field, |
| 3982 | .int_to_enum, | |
| 3983 | 4241 | .int_to_float, |
| 3984 | 4242 | .int_to_ptr, |
| 3985 | 4243 | .memcpy, |
| ... | ... | @@ -4023,7 +4281,6 @@ fn builtinCall( |
| 4023 | 4281 | .This, |
| 4024 | 4282 | .truncate, |
| 4025 | 4283 | .Type, |
| 4026 | .type_info, | |
| 4027 | 4284 | .type_name, |
| 4028 | 4285 | .union_init, |
| 4029 | 4286 | => return mod.failNode(scope, node, "TODO: implement builtin function {s}", .{ |
| ... | ... | @@ -4354,7 +4611,7 @@ fn rvalue( |
| 4354 | 4611 | src_node: ast.Node.Index, |
| 4355 | 4612 | ) InnerError!zir.Inst.Ref { |
| 4356 | 4613 | switch (rl) { |
| 4357 | .none => return result, | |
| 4614 | .none, .none_or_ref => return result, | |
| 4358 | 4615 | .discard => { |
| 4359 | 4616 | // Emit a compile error for discarding error values. |
| 4360 | 4617 | _ = try gz.addUnNode(.ensure_result_non_error, result, src_node); |
src/BuiltinFn.zig+1-1| ... | ... | @@ -484,7 +484,7 @@ pub const list = list: { |
| 484 | 484 | "@intToEnum", |
| 485 | 485 | .{ |
| 486 | 486 | .tag = .int_to_enum, |
| 487 | .param_count = 1, | |
| 487 | .param_count = 2, | |
| 488 | 488 | }, |
| 489 | 489 | }, |
| 490 | 490 | .{ |
src/Compilation.zig+132-58| ... | ... | @@ -272,32 +272,57 @@ pub const AllErrors = struct { |
| 272 | 272 | line: u32, |
| 273 | 273 | column: u32, |
| 274 | 274 | byte_offset: u32, |
| 275 | /// Does not include the trailing newline. | |
| 276 | source_line: ?[]const u8, | |
| 275 | 277 | notes: []Message = &.{}, |
| 276 | 278 | }, |
| 277 | 279 | plain: struct { |
| 278 | 280 | msg: []const u8, |
| 279 | 281 | }, |
| 280 | 282 | |
| 281 | pub fn renderToStdErr(msg: Message) void { | |
| 282 | return msg.renderToStdErrInner("error"); | |
| 283 | pub fn renderToStdErr(msg: Message, ttyconf: std.debug.TTY.Config) void { | |
| 284 | const stderr_mutex = std.debug.getStderrMutex(); | |
| 285 | const held = std.debug.getStderrMutex().acquire(); | |
| 286 | defer held.release(); | |
| 287 | const stderr = std.io.getStdErr(); | |
| 288 | return msg.renderToStdErrInner(ttyconf, stderr, "error:", .Red) catch return; | |
| 283 | 289 | } |
| 284 | 290 | |
| 285 | fn renderToStdErrInner(msg: Message, kind: []const u8) void { | |
| 291 | fn renderToStdErrInner( | |
| 292 | msg: Message, | |
| 293 | ttyconf: std.debug.TTY.Config, | |
| 294 | stderr_file: std.fs.File, | |
| 295 | kind: []const u8, | |
| 296 | color: std.debug.TTY.Color, | |
| 297 | ) anyerror!void { | |
| 298 | const stderr = stderr_file.writer(); | |
| 286 | 299 | switch (msg) { |
| 287 | 300 | .src => |src| { |
| 288 | std.debug.print("{s}:{d}:{d}: {s}: {s}\n", .{ | |
| 301 | ttyconf.setColor(stderr, .Bold); | |
| 302 | try stderr.print("{s}:{d}:{d}: ", .{ | |
| 289 | 303 | src.src_path, |
| 290 | 304 | src.line + 1, |
| 291 | 305 | src.column + 1, |
| 292 | kind, | |
| 293 | src.msg, | |
| 294 | 306 | }); |
| 307 | ttyconf.setColor(stderr, color); | |
| 308 | try stderr.writeAll(kind); | |
| 309 | ttyconf.setColor(stderr, .Bold); | |
| 310 | try stderr.print(" {s}\n", .{src.msg}); | |
| 311 | ttyconf.setColor(stderr, .Reset); | |
| 312 | if (src.source_line) |line| { | |
| 313 | try stderr.writeAll(line); | |
| 314 | try stderr.writeByte('\n'); | |
| 315 | try stderr.writeByteNTimes(' ', src.column); | |
| 316 | ttyconf.setColor(stderr, .Green); | |
| 317 | try stderr.writeAll("^\n"); | |
| 318 | ttyconf.setColor(stderr, .Reset); | |
| 319 | } | |
| 295 | 320 | for (src.notes) |note| { |
| 296 | note.renderToStdErrInner("note"); | |
| 321 | try note.renderToStdErrInner(ttyconf, stderr_file, "note:", .Cyan); | |
| 297 | 322 | } |
| 298 | 323 | }, |
| 299 | 324 | .plain => |plain| { |
| 300 | std.debug.print("{s}: {s}\n", .{ kind, plain.msg }); | |
| 325 | try stderr.print("{s}: {s}\n", .{ kind, plain.msg }); | |
| 301 | 326 | }, |
| 302 | 327 | } |
| 303 | 328 | } |
| ... | ... | @@ -327,6 +352,7 @@ pub const AllErrors = struct { |
| 327 | 352 | .byte_offset = byte_offset, |
| 328 | 353 | .line = @intCast(u32, loc.line), |
| 329 | 354 | .column = @intCast(u32, loc.column), |
| 355 | .source_line = try arena.allocator.dupe(u8, loc.source_line), | |
| 330 | 356 | }, |
| 331 | 357 | }; |
| 332 | 358 | } |
| ... | ... | @@ -342,6 +368,7 @@ pub const AllErrors = struct { |
| 342 | 368 | .line = @intCast(u32, loc.line), |
| 343 | 369 | .column = @intCast(u32, loc.column), |
| 344 | 370 | .notes = notes, |
| 371 | .source_line = try arena.allocator.dupe(u8, loc.source_line), | |
| 345 | 372 | }, |
| 346 | 373 | }); |
| 347 | 374 | } |
| ... | ... | @@ -906,38 +933,56 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 906 | 933 | artifact_sub_dir, |
| 907 | 934 | }; |
| 908 | 935 | |
| 909 | // TODO when we implement serialization and deserialization of incremental compilation metadata, | |
| 910 | // this is where we would load it. We have open a handle to the directory where | |
| 911 | // the output either already is, or will be. | |
| 936 | // If we rely on stage1, we must not redundantly add these packages. | |
| 937 | const use_stage1 = build_options.is_stage1 and use_llvm; | |
| 938 | if (!use_stage1) { | |
| 939 | const builtin_pkg = try Package.createWithDir( | |
| 940 | gpa, | |
| 941 | zig_cache_artifact_directory, | |
| 942 | null, | |
| 943 | "builtin.zig", | |
| 944 | ); | |
| 945 | errdefer builtin_pkg.destroy(gpa); | |
| 946 | ||
| 947 | const std_pkg = try Package.createWithDir( | |
| 948 | gpa, | |
| 949 | options.zig_lib_directory, | |
| 950 | "std", | |
| 951 | "std.zig", | |
| 952 | ); | |
| 953 | errdefer std_pkg.destroy(gpa); | |
| 954 | ||
| 955 | try root_pkg.addAndAdopt(gpa, "builtin", builtin_pkg); | |
| 956 | try root_pkg.add(gpa, "root", root_pkg); | |
| 957 | try root_pkg.addAndAdopt(gpa, "std", std_pkg); | |
| 958 | ||
| 959 | try std_pkg.add(gpa, "builtin", builtin_pkg); | |
| 960 | try std_pkg.add(gpa, "root", root_pkg); | |
| 961 | } | |
| 962 | ||
| 963 | // TODO when we implement serialization and deserialization of incremental | |
| 964 | // compilation metadata, this is where we would load it. We have open a handle | |
| 965 | // to the directory where the output either already is, or will be. | |
| 912 | 966 | // However we currently do not have serialization of such metadata, so for now |
| 913 | 967 | // we set up an empty Module that does the entire compilation fresh. |
| 914 | 968 | |
| 915 | const root_scope = rs: { | |
| 916 | if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) { | |
| 917 | const root_scope = try gpa.create(Module.Scope.File); | |
| 918 | const struct_ty = try Type.Tag.empty_struct.create( | |
| 919 | gpa, | |
| 920 | &root_scope.root_container, | |
| 921 | ); | |
| 922 | root_scope.* = .{ | |
| 923 | // TODO this is duped so it can be freed in Container.deinit | |
| 924 | .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path), | |
| 925 | .source = .{ .unloaded = {} }, | |
| 926 | .tree = undefined, | |
| 927 | .status = .never_loaded, | |
| 928 | .pkg = root_pkg, | |
| 929 | .root_container = .{ | |
| 930 | .file_scope = root_scope, | |
| 931 | .decls = .{}, | |
| 932 | .ty = struct_ty, | |
| 933 | }, | |
| 934 | }; | |
| 935 | break :rs root_scope; | |
| 936 | } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) { | |
| 937 | return error.ZirFilesUnsupported; | |
| 938 | } else { | |
| 939 | unreachable; | |
| 940 | } | |
| 969 | const root_scope = try gpa.create(Module.Scope.File); | |
| 970 | errdefer gpa.destroy(root_scope); | |
| 971 | ||
| 972 | const struct_ty = try Type.Tag.empty_struct.create(gpa, &root_scope.root_container); | |
| 973 | root_scope.* = .{ | |
| 974 | // TODO this is duped so it can be freed in Container.deinit | |
| 975 | .sub_file_path = try gpa.dupe(u8, root_pkg.root_src_path), | |
| 976 | .source = .{ .unloaded = {} }, | |
| 977 | .tree = undefined, | |
| 978 | .status = .never_loaded, | |
| 979 | .pkg = root_pkg, | |
| 980 | .root_container = .{ | |
| 981 | .file_scope = root_scope, | |
| 982 | .decls = .{}, | |
| 983 | .ty = struct_ty, | |
| 984 | .parent_name_hash = root_pkg.namespace_hash, | |
| 985 | }, | |
| 941 | 986 | }; |
| 942 | 987 | |
| 943 | 988 | const module = try arena.create(Module); |
| ... | ... | @@ -1339,16 +1384,17 @@ pub fn update(self: *Compilation) !void { |
| 1339 | 1384 | self.c_object_work_queue.writeItemAssumeCapacity(entry.key); |
| 1340 | 1385 | } |
| 1341 | 1386 | |
| 1342 | const use_stage1 = build_options.omit_stage2 or build_options.is_stage1 and self.bin_file.options.use_llvm; | |
| 1387 | const use_stage1 = build_options.omit_stage2 or | |
| 1388 | (build_options.is_stage1 and self.bin_file.options.use_llvm); | |
| 1343 | 1389 | if (!use_stage1) { |
| 1344 | 1390 | if (self.bin_file.options.module) |module| { |
| 1345 | 1391 | module.compile_log_text.shrinkAndFree(module.gpa, 0); |
| 1346 | 1392 | module.generation += 1; |
| 1347 | 1393 | |
| 1348 | 1394 | // TODO Detect which source files changed. |
| 1349 | // Until then we simulate a full cache miss. Source files could have been loaded for any reason; | |
| 1350 | // to force a refresh we unload now. | |
| 1351 | module.root_scope.unload(module.gpa); | |
| 1395 | // Until then we simulate a full cache miss. Source files could have been loaded | |
| 1396 | // for any reason; to force a refresh we unload now. | |
| 1397 | module.unloadFile(module.root_scope); | |
| 1352 | 1398 | module.failed_root_src_file = null; |
| 1353 | 1399 | module.analyzeContainer(&module.root_scope.root_container) catch |err| switch (err) { |
| 1354 | 1400 | error.AnalysisFail => { |
| ... | ... | @@ -1362,7 +1408,7 @@ pub fn update(self: *Compilation) !void { |
| 1362 | 1408 | |
| 1363 | 1409 | // TODO only analyze imports if they are still referenced |
| 1364 | 1410 | for (module.import_table.items()) |entry| { |
| 1365 | entry.value.unload(module.gpa); | |
| 1411 | module.unloadFile(entry.value); | |
| 1366 | 1412 | module.analyzeContainer(&entry.value.root_container) catch |err| switch (err) { |
| 1367 | 1413 | error.AnalysisFail => { |
| 1368 | 1414 | assert(self.totalErrorCount() != 0); |
| ... | ... | @@ -1377,14 +1423,17 @@ pub fn update(self: *Compilation) !void { |
| 1377 | 1423 | |
| 1378 | 1424 | if (!use_stage1) { |
| 1379 | 1425 | if (self.bin_file.options.module) |module| { |
| 1380 | // Process the deletion set. | |
| 1381 | while (module.deletion_set.popOrNull()) |decl| { | |
| 1382 | if (decl.dependants.items().len != 0) { | |
| 1383 | decl.deletion_flag = false; | |
| 1384 | continue; | |
| 1385 | } | |
| 1386 | try module.deleteDecl(decl); | |
| 1426 | // Process the deletion set. We use a while loop here because the | |
| 1427 | // deletion set may grow as we call `deleteDecl` within this loop, | |
| 1428 | // and more unreferenced Decls are revealed. | |
| 1429 | var entry_i: usize = 0; | |
| 1430 | while (entry_i < module.deletion_set.entries.items.len) : (entry_i += 1) { | |
| 1431 | const decl = module.deletion_set.entries.items[entry_i].key; | |
| 1432 | assert(decl.deletion_flag); | |
| 1433 | assert(decl.dependants.items().len == 0); | |
| 1434 | try module.deleteDecl(decl, null); | |
| 1387 | 1435 | } |
| 1436 | module.deletion_set.shrinkRetainingCapacity(0); | |
| 1388 | 1437 | } |
| 1389 | 1438 | } |
| 1390 | 1439 | |
| ... | ... | @@ -1429,11 +1478,25 @@ pub fn totalErrorCount(self: *Compilation) usize { |
| 1429 | 1478 | var total: usize = self.failed_c_objects.items().len; |
| 1430 | 1479 | |
| 1431 | 1480 | if (self.bin_file.options.module) |module| { |
| 1432 | total += module.failed_decls.count() + | |
| 1433 | module.emit_h_failed_decls.count() + | |
| 1434 | module.failed_exports.items().len + | |
| 1481 | total += module.failed_exports.items().len + | |
| 1435 | 1482 | module.failed_files.items().len + |
| 1436 | 1483 | @boolToInt(module.failed_root_src_file != null); |
| 1484 | // Skip errors for Decls within files that failed parsing. | |
| 1485 | // When a parse error is introduced, we keep all the semantic analysis for | |
| 1486 | // the previous parse success, including compile errors, but we cannot | |
| 1487 | // emit them until the file succeeds parsing. | |
| 1488 | for (module.failed_decls.items()) |entry| { | |
| 1489 | if (entry.key.container.file_scope.status == .unloaded_parse_failure) { | |
| 1490 | continue; | |
| 1491 | } | |
| 1492 | total += 1; | |
| 1493 | } | |
| 1494 | for (module.emit_h_failed_decls.items()) |entry| { | |
| 1495 | if (entry.key.container.file_scope.status == .unloaded_parse_failure) { | |
| 1496 | continue; | |
| 1497 | } | |
| 1498 | total += 1; | |
| 1499 | } | |
| 1437 | 1500 | } |
| 1438 | 1501 | |
| 1439 | 1502 | // The "no entry point found" error only counts if there are no other errors. |
| ... | ... | @@ -1472,6 +1535,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 1472 | 1535 | .byte_offset = 0, |
| 1473 | 1536 | .line = err_msg.line, |
| 1474 | 1537 | .column = err_msg.column, |
| 1538 | .source_line = null, // TODO | |
| 1475 | 1539 | }, |
| 1476 | 1540 | }); |
| 1477 | 1541 | } |
| ... | ... | @@ -1480,9 +1544,19 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 1480 | 1544 | try AllErrors.add(module, &arena, &errors, entry.value.*); |
| 1481 | 1545 | } |
| 1482 | 1546 | for (module.failed_decls.items()) |entry| { |
| 1547 | if (entry.key.container.file_scope.status == .unloaded_parse_failure) { | |
| 1548 | // Skip errors for Decls within files that had a parse failure. | |
| 1549 | // We'll try again once parsing succeeds. | |
| 1550 | continue; | |
| 1551 | } | |
| 1483 | 1552 | try AllErrors.add(module, &arena, &errors, entry.value.*); |
| 1484 | 1553 | } |
| 1485 | 1554 | for (module.emit_h_failed_decls.items()) |entry| { |
| 1555 | if (entry.key.container.file_scope.status == .unloaded_parse_failure) { | |
| 1556 | // Skip errors for Decls within files that had a parse failure. | |
| 1557 | // We'll try again once parsing succeeds. | |
| 1558 | continue; | |
| 1559 | } | |
| 1486 | 1560 | try AllErrors.add(module, &arena, &errors, entry.value.*); |
| 1487 | 1561 | } |
| 1488 | 1562 | for (module.failed_exports.items()) |entry| { |
| ... | ... | @@ -2437,7 +2511,7 @@ pub fn addCCArgs( |
| 2437 | 2511 | try argv.append("-fPIC"); |
| 2438 | 2512 | } |
| 2439 | 2513 | }, |
| 2440 | .shared_library, .assembly, .ll, .bc, .unknown, .static_library, .object, .zig, .zir => {}, | |
| 2514 | .shared_library, .assembly, .ll, .bc, .unknown, .static_library, .object, .zig => {}, | |
| 2441 | 2515 | } |
| 2442 | 2516 | if (out_dep_path) |p| { |
| 2443 | 2517 | try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p }); |
| ... | ... | @@ -2511,7 +2585,6 @@ pub const FileExt = enum { |
| 2511 | 2585 | object, |
| 2512 | 2586 | static_library, |
| 2513 | 2587 | zig, |
| 2514 | zir, | |
| 2515 | 2588 | unknown, |
| 2516 | 2589 | |
| 2517 | 2590 | pub fn clangSupportsDepFile(ext: FileExt) bool { |
| ... | ... | @@ -2525,7 +2598,6 @@ pub const FileExt = enum { |
| 2525 | 2598 | .object, |
| 2526 | 2599 | .static_library, |
| 2527 | 2600 | .zig, |
| 2528 | .zir, | |
| 2529 | 2601 | .unknown, |
| 2530 | 2602 | => false, |
| 2531 | 2603 | }; |
| ... | ... | @@ -2597,8 +2669,6 @@ pub fn classifyFileExt(filename: []const u8) FileExt { |
| 2597 | 2669 | return .h; |
| 2598 | 2670 | } else if (mem.endsWith(u8, filename, ".zig")) { |
| 2599 | 2671 | return .zig; |
| 2600 | } else if (mem.endsWith(u8, filename, ".zir")) { | |
| 2601 | return .zir; | |
| 2602 | 2672 | } else if (hasSharedLibraryExt(filename)) { |
| 2603 | 2673 | return .shared_library; |
| 2604 | 2674 | } else if (hasStaticLibraryExt(filename)) { |
| ... | ... | @@ -2619,7 +2689,6 @@ test "classifyFileExt" { |
| 2619 | 2689 | std.testing.expectEqual(FileExt.shared_library, classifyFileExt("foo.so.1.2.3")); |
| 2620 | 2690 | std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.so.1.2.3~")); |
| 2621 | 2691 | std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig")); |
| 2622 | std.testing.expectEqual(FileExt.zir, classifyFileExt("foo.zir")); | |
| 2623 | 2692 | } |
| 2624 | 2693 | |
| 2625 | 2694 | fn haveFramePointer(comp: *const Compilation) bool { |
| ... | ... | @@ -2814,6 +2883,8 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 |
| 2814 | 2883 | |
| 2815 | 2884 | const target = comp.getTarget(); |
| 2816 | 2885 | const generic_arch_name = target.cpu.arch.genericName(); |
| 2886 | const use_stage1 = build_options.omit_stage2 or | |
| 2887 | (build_options.is_stage1 and comp.bin_file.options.use_llvm); | |
| 2817 | 2888 | |
| 2818 | 2889 | @setEvalBranchQuota(4000); |
| 2819 | 2890 | try buffer.writer().print( |
| ... | ... | @@ -2826,6 +2897,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 |
| 2826 | 2897 | \\/// Zig version. When writing code that supports multiple versions of Zig, prefer |
| 2827 | 2898 | \\/// feature detection (i.e. with `@hasDecl` or `@hasField`) over version checks. |
| 2828 | 2899 | \\pub const zig_version = try @import("std").SemanticVersion.parse("{s}"); |
| 2900 | \\pub const zig_is_stage2 = {}; | |
| 2829 | 2901 | \\ |
| 2830 | 2902 | \\pub const output_mode = OutputMode.{}; |
| 2831 | 2903 | \\pub const link_mode = LinkMode.{}; |
| ... | ... | @@ -2839,6 +2911,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) ![]u8 |
| 2839 | 2911 | \\ |
| 2840 | 2912 | , .{ |
| 2841 | 2913 | build_options.version, |
| 2914 | !use_stage1, | |
| 2842 | 2915 | std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)), |
| 2843 | 2916 | std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)), |
| 2844 | 2917 | comp.bin_file.options.is_test, |
| ... | ... | @@ -3044,6 +3117,7 @@ fn buildOutputFromZig( |
| 3044 | 3117 | .handle = special_dir, |
| 3045 | 3118 | }, |
| 3046 | 3119 | .root_src_path = src_basename, |
| 3120 | .namespace_hash = Package.root_namespace_hash, | |
| 3047 | 3121 | }; |
| 3048 | 3122 | const root_name = src_basename[0 .. src_basename.len - std.fs.path.extension(src_basename).len]; |
| 3049 | 3123 | const target = comp.getTarget(); |
src/Module.zig+320-132| ... | ... | @@ -65,8 +65,8 @@ emit_h_failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{}, |
| 65 | 65 | /// Keep track of one `@compileLog` callsite per owner Decl. |
| 66 | 66 | compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, SrcLoc) = .{}, |
| 67 | 67 | /// Using a map here for consistency with the other fields here. |
| 68 | /// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator. | |
| 69 | failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *ErrorMsg) = .{}, | |
| 68 | /// The ErrorMsg memory is owned by the `Scope.File`, using Module's general purpose allocator. | |
| 69 | failed_files: std.AutoArrayHashMapUnmanaged(*Scope.File, *ErrorMsg) = .{}, | |
| 70 | 70 | /// Using a map here for consistency with the other fields here. |
| 71 | 71 | /// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator. |
| 72 | 72 | failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{}, |
| ... | ... | @@ -75,7 +75,7 @@ next_anon_name_index: usize = 0, |
| 75 | 75 | |
| 76 | 76 | /// Candidates for deletion. After a semantic analysis update completes, this list |
| 77 | 77 | /// contains Decls that need to be deleted if they end up having no references to them. |
| 78 | deletion_set: ArrayListUnmanaged(*Decl) = .{}, | |
| 78 | deletion_set: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, | |
| 79 | 79 | |
| 80 | 80 | /// Error tags and their values, tag names are duped with mod.gpa. |
| 81 | 81 | /// Corresponds with `error_name_list`. |
| ... | ... | @@ -150,9 +150,15 @@ pub const Decl = struct { |
| 150 | 150 | /// The direct parent container of the Decl. |
| 151 | 151 | /// Reference to externally owned memory. |
| 152 | 152 | container: *Scope.Container, |
| 153 | /// The AST Node decl index or ZIR Inst index that contains this declaration. | |
| 153 | ||
| 154 | /// An integer that can be checked against the corresponding incrementing | |
| 155 | /// generation field of Module. This is used to determine whether `complete` status | |
| 156 | /// represents pre- or post- re-analysis. | |
| 157 | generation: u32, | |
| 158 | /// The AST Node index or ZIR Inst index that contains this declaration. | |
| 154 | 159 | /// Must be recomputed when the corresponding source file is modified. |
| 155 | src_index: usize, | |
| 160 | src_node: ast.Node.Index, | |
| 161 | ||
| 156 | 162 | /// The most recent value of the Decl after a successful semantic analysis. |
| 157 | 163 | typed_value: union(enum) { |
| 158 | 164 | never_succeeded: void, |
| ... | ... | @@ -192,17 +198,12 @@ pub const Decl = struct { |
| 192 | 198 | /// to require re-analysis. |
| 193 | 199 | outdated, |
| 194 | 200 | }, |
| 195 | /// This flag is set when this Decl is added to a check_for_deletion set, and cleared | |
| 201 | /// This flag is set when this Decl is added to `Module.deletion_set`, and cleared | |
| 196 | 202 | /// when removed. |
| 197 | 203 | deletion_flag: bool, |
| 198 | 204 | /// Whether the corresponding AST decl has a `pub` keyword. |
| 199 | 205 | is_pub: bool, |
| 200 | 206 | |
| 201 | /// An integer that can be checked against the corresponding incrementing | |
| 202 | /// generation field of Module. This is used to determine whether `complete` status | |
| 203 | /// represents pre- or post- re-analysis. | |
| 204 | generation: u32, | |
| 205 | ||
| 206 | 207 | /// Represents the position of the code in the output file. |
| 207 | 208 | /// This is populated regardless of semantic analysis and code generation. |
| 208 | 209 | link: link.File.LinkBlock, |
| ... | ... | @@ -249,11 +250,11 @@ pub const Decl = struct { |
| 249 | 250 | } |
| 250 | 251 | |
| 251 | 252 | pub fn relativeToNodeIndex(decl: Decl, offset: i32) ast.Node.Index { |
| 252 | return @bitCast(ast.Node.Index, offset + @bitCast(i32, decl.srcNode())); | |
| 253 | return @bitCast(ast.Node.Index, offset + @bitCast(i32, decl.src_node)); | |
| 253 | 254 | } |
| 254 | 255 | |
| 255 | 256 | pub fn nodeIndexToRelative(decl: Decl, node_index: ast.Node.Index) i32 { |
| 256 | return @bitCast(i32, node_index) - @bitCast(i32, decl.srcNode()); | |
| 257 | return @bitCast(i32, node_index) - @bitCast(i32, decl.src_node); | |
| 257 | 258 | } |
| 258 | 259 | |
| 259 | 260 | pub fn tokSrcLoc(decl: Decl, token_index: ast.TokenIndex) LazySrcLoc { |
| ... | ... | @@ -271,14 +272,9 @@ pub const Decl = struct { |
| 271 | 272 | }; |
| 272 | 273 | } |
| 273 | 274 | |
| 274 | pub fn srcNode(decl: Decl) u32 { | |
| 275 | const tree = &decl.container.file_scope.tree; | |
| 276 | return tree.rootDecls()[decl.src_index]; | |
| 277 | } | |
| 278 | ||
| 279 | 275 | pub fn srcToken(decl: Decl) u32 { |
| 280 | 276 | const tree = &decl.container.file_scope.tree; |
| 281 | return tree.firstToken(decl.srcNode()); | |
| 277 | return tree.firstToken(decl.src_node); | |
| 282 | 278 | } |
| 283 | 279 | |
| 284 | 280 | pub fn srcByteOffset(decl: Decl) u32 { |
| ... | ... | @@ -290,6 +286,18 @@ pub const Decl = struct { |
| 290 | 286 | return decl.container.fullyQualifiedNameHash(mem.spanZ(decl.name)); |
| 291 | 287 | } |
| 292 | 288 | |
| 289 | pub fn renderFullyQualifiedName(decl: Decl, writer: anytype) !void { | |
| 290 | const unqualified_name = mem.spanZ(decl.name); | |
| 291 | return decl.container.renderFullyQualifiedName(unqualified_name, writer); | |
| 292 | } | |
| 293 | ||
| 294 | pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![]u8 { | |
| 295 | var buffer = std.ArrayList(u8).init(gpa); | |
| 296 | defer buffer.deinit(); | |
| 297 | try decl.renderFullyQualifiedName(buffer.writer()); | |
| 298 | return buffer.toOwnedSlice(); | |
| 299 | } | |
| 300 | ||
| 293 | 301 | pub fn typedValue(decl: *Decl) error{AnalysisFail}!TypedValue { |
| 294 | 302 | const tvm = decl.typedValueManaged() orelse return error.AnalysisFail; |
| 295 | 303 | return tvm.typed_value; |
| ... | ... | @@ -354,6 +362,13 @@ pub const ErrorSet = struct { |
| 354 | 362 | /// The string bytes are stored in the owner Decl arena. |
| 355 | 363 | /// They are in the same order they appear in the AST. |
| 356 | 364 | names_ptr: [*]const []const u8, |
| 365 | ||
| 366 | pub fn srcLoc(self: ErrorSet) SrcLoc { | |
| 367 | return .{ | |
| 368 | .container = .{ .decl = self.owner_decl }, | |
| 369 | .lazy = .{ .node_offset = self.node_offset }, | |
| 370 | }; | |
| 371 | } | |
| 357 | 372 | }; |
| 358 | 373 | |
| 359 | 374 | /// Represents the data that a struct declaration provides. |
| ... | ... | @@ -364,7 +379,7 @@ pub const Struct = struct { |
| 364 | 379 | /// Represents the declarations inside this struct. |
| 365 | 380 | container: Scope.Container, |
| 366 | 381 | |
| 367 | /// Offset from Decl node index, points to the struct AST node. | |
| 382 | /// Offset from `owner_decl`, points to the struct AST node. | |
| 368 | 383 | node_offset: i32, |
| 369 | 384 | |
| 370 | 385 | pub const Field = struct { |
| ... | ... | @@ -373,6 +388,64 @@ pub const Struct = struct { |
| 373 | 388 | /// Uses `unreachable_value` to indicate no default. |
| 374 | 389 | default_val: Value, |
| 375 | 390 | }; |
| 391 | ||
| 392 | pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![]u8 { | |
| 393 | return s.owner_decl.getFullyQualifiedName(gpa); | |
| 394 | } | |
| 395 | ||
| 396 | pub fn srcLoc(s: Struct) SrcLoc { | |
| 397 | return .{ | |
| 398 | .container = .{ .decl = s.owner_decl }, | |
| 399 | .lazy = .{ .node_offset = s.node_offset }, | |
| 400 | }; | |
| 401 | } | |
| 402 | }; | |
| 403 | ||
| 404 | /// Represents the data that an enum declaration provides, when the fields | |
| 405 | /// are auto-numbered, and there are no declarations. The integer tag type | |
| 406 | /// is inferred to be the smallest power of two unsigned int that fits | |
| 407 | /// the number of fields. | |
| 408 | pub const EnumSimple = struct { | |
| 409 | owner_decl: *Decl, | |
| 410 | /// Set of field names in declaration order. | |
| 411 | fields: std.StringArrayHashMapUnmanaged(void), | |
| 412 | /// Offset from `owner_decl`, points to the enum decl AST node. | |
| 413 | node_offset: i32, | |
| 414 | ||
| 415 | pub fn srcLoc(self: EnumSimple) SrcLoc { | |
| 416 | return .{ | |
| 417 | .container = .{ .decl = self.owner_decl }, | |
| 418 | .lazy = .{ .node_offset = self.node_offset }, | |
| 419 | }; | |
| 420 | } | |
| 421 | }; | |
| 422 | ||
| 423 | /// Represents the data that an enum declaration provides, when there is | |
| 424 | /// at least one tag value explicitly specified, or at least one declaration. | |
| 425 | pub const EnumFull = struct { | |
| 426 | owner_decl: *Decl, | |
| 427 | /// An integer type which is used for the numerical value of the enum. | |
| 428 | /// Whether zig chooses this type or the user specifies it, it is stored here. | |
| 429 | tag_ty: Type, | |
| 430 | /// Set of field names in declaration order. | |
| 431 | fields: std.StringArrayHashMapUnmanaged(void), | |
| 432 | /// Maps integer tag value to field index. | |
| 433 | /// Entries are in declaration order, same as `fields`. | |
| 434 | /// If this hash map is empty, it means the enum tags are auto-numbered. | |
| 435 | values: ValueMap, | |
| 436 | /// Represents the declarations inside this struct. | |
| 437 | container: Scope.Container, | |
| 438 | /// Offset from `owner_decl`, points to the enum decl AST node. | |
| 439 | node_offset: i32, | |
| 440 | ||
| 441 | pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.hash_u32, Value.eql, false); | |
| 442 | ||
| 443 | pub fn srcLoc(self: EnumFull) SrcLoc { | |
| 444 | return .{ | |
| 445 | .container = .{ .decl = self.owner_decl }, | |
| 446 | .lazy = .{ .node_offset = self.node_offset }, | |
| 447 | }; | |
| 448 | } | |
| 376 | 449 | }; |
| 377 | 450 | |
| 378 | 451 | /// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator. |
| ... | ... | @@ -601,6 +674,7 @@ pub const Scope = struct { |
| 601 | 674 | base: Scope = Scope{ .tag = base_tag }, |
| 602 | 675 | |
| 603 | 676 | file_scope: *Scope.File, |
| 677 | parent_name_hash: NameHash, | |
| 604 | 678 | |
| 605 | 679 | /// Direct children of the file. |
| 606 | 680 | decls: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{}, |
| ... | ... | @@ -619,8 +693,12 @@ pub const Scope = struct { |
| 619 | 693 | } |
| 620 | 694 | |
| 621 | 695 | pub fn fullyQualifiedNameHash(cont: *Container, name: []const u8) NameHash { |
| 622 | // TODO container scope qualified names. | |
| 623 | return std.zig.hashSrc(name); | |
| 696 | return std.zig.hashName(cont.parent_name_hash, ".", name); | |
| 697 | } | |
| 698 | ||
| 699 | pub fn renderFullyQualifiedName(cont: Container, name: []const u8, writer: anytype) !void { | |
| 700 | // TODO this should render e.g. "std.fs.Dir.OpenOptions" | |
| 701 | return writer.writeAll(name); | |
| 624 | 702 | } |
| 625 | 703 | }; |
| 626 | 704 | |
| ... | ... | @@ -650,10 +728,12 @@ pub const Scope = struct { |
| 650 | 728 | |
| 651 | 729 | pub fn unload(file: *File, gpa: *Allocator) void { |
| 652 | 730 | switch (file.status) { |
| 653 | .never_loaded, | |
| 654 | 731 | .unloaded_parse_failure, |
| 732 | .never_loaded, | |
| 655 | 733 | .unloaded_success, |
| 656 | => {}, | |
| 734 | => { | |
| 735 | file.status = .unloaded_success; | |
| 736 | }, | |
| 657 | 737 | |
| 658 | 738 | .loaded_success => { |
| 659 | 739 | file.tree.deinit(gpa); |
| ... | ... | @@ -1018,7 +1098,6 @@ pub const Scope = struct { |
| 1018 | 1098 | .instructions = gz.astgen.instructions.toOwnedSlice(), |
| 1019 | 1099 | .string_bytes = gz.astgen.string_bytes.toOwnedSlice(gpa), |
| 1020 | 1100 | .extra = gz.astgen.extra.toOwnedSlice(gpa), |
| 1021 | .decls = gz.astgen.decls.toOwnedSlice(gpa), | |
| 1022 | 1101 | }; |
| 1023 | 1102 | } |
| 1024 | 1103 | |
| ... | ... | @@ -1048,6 +1127,9 @@ pub const Scope = struct { |
| 1048 | 1127 | gz.rl_ty_inst = ty_inst; |
| 1049 | 1128 | gz.break_result_loc = parent_rl; |
| 1050 | 1129 | }, |
| 1130 | .none_or_ref => { | |
| 1131 | gz.break_result_loc = .ref; | |
| 1132 | }, | |
| 1051 | 1133 | .discard, .none, .ptr, .ref => { |
| 1052 | 1134 | gz.break_result_loc = parent_rl; |
| 1053 | 1135 | }, |
| ... | ... | @@ -1227,6 +1309,16 @@ pub const Scope = struct { |
| 1227 | 1309 | }); |
| 1228 | 1310 | } |
| 1229 | 1311 | |
| 1312 | pub fn addFloat(gz: *GenZir, number: f32, src_node: ast.Node.Index) !zir.Inst.Ref { | |
| 1313 | return gz.add(.{ | |
| 1314 | .tag = .float, | |
| 1315 | .data = .{ .float = .{ | |
| 1316 | .src_node = gz.astgen.decl.nodeIndexToRelative(src_node), | |
| 1317 | .number = number, | |
| 1318 | } }, | |
| 1319 | }); | |
| 1320 | } | |
| 1321 | ||
| 1230 | 1322 | pub fn addUnNode( |
| 1231 | 1323 | gz: *GenZir, |
| 1232 | 1324 | tag: zir.Inst.Tag, |
| ... | ... | @@ -1435,13 +1527,6 @@ pub const Scope = struct { |
| 1435 | 1527 | return new_index; |
| 1436 | 1528 | } |
| 1437 | 1529 | |
| 1438 | pub fn addConst(gz: *GenZir, typed_value: *TypedValue) !zir.Inst.Ref { | |
| 1439 | return gz.add(.{ | |
| 1440 | .tag = .@"const", | |
| 1441 | .data = .{ .@"const" = typed_value }, | |
| 1442 | }); | |
| 1443 | } | |
| 1444 | ||
| 1445 | 1530 | pub fn add(gz: *GenZir, inst: zir.Inst) !zir.Inst.Ref { |
| 1446 | 1531 | return gz.astgen.indexToRef(try gz.addAsIndex(inst)); |
| 1447 | 1532 | } |
| ... | ... | @@ -1572,6 +1657,7 @@ pub const SrcLoc = struct { |
| 1572 | 1657 | .byte_offset, |
| 1573 | 1658 | .token_offset, |
| 1574 | 1659 | .node_offset, |
| 1660 | .node_offset_back2tok, | |
| 1575 | 1661 | .node_offset_var_decl_ty, |
| 1576 | 1662 | .node_offset_for_cond, |
| 1577 | 1663 | .node_offset_builtin_call_arg0, |
| ... | ... | @@ -1633,6 +1719,14 @@ pub const SrcLoc = struct { |
| 1633 | 1719 | const token_starts = tree.tokens.items(.start); |
| 1634 | 1720 | return token_starts[tok_index]; |
| 1635 | 1721 | }, |
| 1722 | .node_offset_back2tok => |node_off| { | |
| 1723 | const decl = src_loc.container.decl; | |
| 1724 | const node = decl.relativeToNodeIndex(node_off); | |
| 1725 | const tree = decl.container.file_scope.base.tree(); | |
| 1726 | const tok_index = tree.firstToken(node) - 2; | |
| 1727 | const token_starts = tree.tokens.items(.start); | |
| 1728 | return token_starts[tok_index]; | |
| 1729 | }, | |
| 1636 | 1730 | .node_offset_var_decl_ty => |node_off| { |
| 1637 | 1731 | const decl = src_loc.container.decl; |
| 1638 | 1732 | const node = decl.relativeToNodeIndex(node_off); |
| ... | ... | @@ -1747,7 +1841,10 @@ pub const SrcLoc = struct { |
| 1747 | 1841 | const node_datas = tree.nodes.items(.data); |
| 1748 | 1842 | const node_tags = tree.nodes.items(.tag); |
| 1749 | 1843 | const node = decl.relativeToNodeIndex(node_off); |
| 1750 | const tok_index = node_datas[node].rhs; | |
| 1844 | const tok_index = switch (node_tags[node]) { | |
| 1845 | .field_access => node_datas[node].rhs, | |
| 1846 | else => tree.firstToken(node) - 2, | |
| 1847 | }; | |
| 1751 | 1848 | const token_starts = tree.tokens.items(.start); |
| 1752 | 1849 | return token_starts[tok_index]; |
| 1753 | 1850 | }, |
| ... | ... | @@ -1988,6 +2085,10 @@ pub const LazySrcLoc = union(enum) { |
| 1988 | 2085 | /// from its containing Decl node AST index. |
| 1989 | 2086 | /// The Decl is determined contextually. |
| 1990 | 2087 | node_offset: i32, |
| 2088 | /// The source location points to two tokens left of the first token of an AST node, | |
| 2089 | /// which is this value offset from its containing Decl node AST index. | |
| 2090 | /// The Decl is determined contextually. | |
| 2091 | node_offset_back2tok: i32, | |
| 1991 | 2092 | /// The source location points to a variable declaration type expression, |
| 1992 | 2093 | /// found by taking this AST node index offset from the containing |
| 1993 | 2094 | /// Decl AST node, which points to a variable declaration AST node. Next, navigate |
| ... | ... | @@ -2026,10 +2127,10 @@ pub const LazySrcLoc = union(enum) { |
| 2026 | 2127 | /// to the callee expression. |
| 2027 | 2128 | /// The Decl is determined contextually. |
| 2028 | 2129 | node_offset_call_func: i32, |
| 2029 | /// The source location points to the field name of a field access expression, | |
| 2030 | /// found by taking this AST node index offset from the containing | |
| 2031 | /// Decl AST node, which points to a field access AST node. Next, navigate | |
| 2032 | /// to the field name token. | |
| 2130 | /// The payload is offset from the containing Decl AST node. | |
| 2131 | /// The source location points to the field name of: | |
| 2132 | /// * a field access expression (`a.b`), or | |
| 2133 | /// * the operand ("b" node) of a field initialization expression (`.a = b`) | |
| 2033 | 2134 | /// The Decl is determined contextually. |
| 2034 | 2135 | node_offset_field_name: i32, |
| 2035 | 2136 | /// The source location points to the pointer of a pointer deref expression, |
| ... | ... | @@ -2114,6 +2215,7 @@ pub const LazySrcLoc = union(enum) { |
| 2114 | 2215 | .byte_offset, |
| 2115 | 2216 | .token_offset, |
| 2116 | 2217 | .node_offset, |
| 2218 | .node_offset_back2tok, | |
| 2117 | 2219 | .node_offset_var_decl_ty, |
| 2118 | 2220 | .node_offset_for_cond, |
| 2119 | 2221 | .node_offset_builtin_call_arg0, |
| ... | ... | @@ -2156,6 +2258,7 @@ pub const LazySrcLoc = union(enum) { |
| 2156 | 2258 | .byte_offset, |
| 2157 | 2259 | .token_offset, |
| 2158 | 2260 | .node_offset, |
| 2261 | .node_offset_back2tok, | |
| 2159 | 2262 | .node_offset_var_decl_ty, |
| 2160 | 2263 | .node_offset_for_cond, |
| 2161 | 2264 | .node_offset_builtin_call_arg0, |
| ... | ... | @@ -2189,6 +2292,20 @@ pub const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 2189 | 2292 | pub fn deinit(mod: *Module) void { |
| 2190 | 2293 | const gpa = mod.gpa; |
| 2191 | 2294 | |
| 2295 | // The callsite of `Compilation.create` owns the `root_pkg`, however | |
| 2296 | // Module owns the builtin and std packages that it adds. | |
| 2297 | if (mod.root_pkg.table.remove("builtin")) |entry| { | |
| 2298 | gpa.free(entry.key); | |
| 2299 | entry.value.destroy(gpa); | |
| 2300 | } | |
| 2301 | if (mod.root_pkg.table.remove("std")) |entry| { | |
| 2302 | gpa.free(entry.key); | |
| 2303 | entry.value.destroy(gpa); | |
| 2304 | } | |
| 2305 | if (mod.root_pkg.table.remove("root")) |entry| { | |
| 2306 | gpa.free(entry.key); | |
| 2307 | } | |
| 2308 | ||
| 2192 | 2309 | mod.compile_log_text.deinit(gpa); |
| 2193 | 2310 | |
| 2194 | 2311 | mod.zig_cache_artifact_directory.handle.close(); |
| ... | ... | @@ -2288,7 +2405,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { |
| 2288 | 2405 | // We don't perform a deletion here, because this Decl or another one |
| 2289 | 2406 | // may end up referencing it before the update is complete. |
| 2290 | 2407 | dep.deletion_flag = true; |
| 2291 | try mod.deletion_set.append(mod.gpa, dep); | |
| 2408 | try mod.deletion_set.put(mod.gpa, dep, {}); | |
| 2292 | 2409 | } |
| 2293 | 2410 | } |
| 2294 | 2411 | decl.dependencies.clearRetainingCapacity(); |
| ... | ... | @@ -2351,7 +2468,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { |
| 2351 | 2468 | const tree = try mod.getAstTree(decl.container.file_scope); |
| 2352 | 2469 | const node_tags = tree.nodes.items(.tag); |
| 2353 | 2470 | const node_datas = tree.nodes.items(.data); |
| 2354 | const decl_node = tree.rootDecls()[decl.src_index]; | |
| 2471 | const decl_node = decl.src_node; | |
| 2355 | 2472 | switch (node_tags[decl_node]) { |
| 2356 | 2473 | .fn_decl => { |
| 2357 | 2474 | const fn_proto = node_datas[decl_node].lhs; |
| ... | ... | @@ -2406,6 +2523,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool { |
| 2406 | 2523 | |
| 2407 | 2524 | const block_expr = node_datas[decl_node].lhs; |
| 2408 | 2525 | _ = try AstGen.comptimeExpr(&gen_scope, &gen_scope.base, .none, block_expr); |
| 2526 | _ = try gen_scope.addBreak(.break_inline, 0, .void_value); | |
| 2409 | 2527 | |
| 2410 | 2528 | const code = try gen_scope.finish(); |
| 2411 | 2529 | if (std.builtin.mode == .Debug and mod.comp.verbose_ir) { |
| ... | ... | @@ -3087,12 +3205,19 @@ fn astgenAndSemaVarDecl( |
| 3087 | 3205 | return type_changed; |
| 3088 | 3206 | } |
| 3089 | 3207 | |
| 3090 | pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !void { | |
| 3091 | try depender.dependencies.ensureCapacity(mod.gpa, depender.dependencies.items().len + 1); | |
| 3092 | try dependee.dependants.ensureCapacity(mod.gpa, dependee.dependants.items().len + 1); | |
| 3208 | /// Returns the depender's index of the dependee. | |
| 3209 | pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !u32 { | |
| 3210 | try depender.dependencies.ensureCapacity(mod.gpa, depender.dependencies.count() + 1); | |
| 3211 | try dependee.dependants.ensureCapacity(mod.gpa, dependee.dependants.count() + 1); | |
| 3212 | ||
| 3213 | if (dependee.deletion_flag) { | |
| 3214 | dependee.deletion_flag = false; | |
| 3215 | mod.deletion_set.removeAssertDiscard(dependee); | |
| 3216 | } | |
| 3093 | 3217 | |
| 3094 | depender.dependencies.putAssumeCapacity(dependee, {}); | |
| 3095 | 3218 | dependee.dependants.putAssumeCapacity(depender, {}); |
| 3219 | const gop = depender.dependencies.getOrPutAssumeCapacity(dependee); | |
| 3220 | return @intCast(u32, gop.index); | |
| 3096 | 3221 | } |
| 3097 | 3222 | |
| 3098 | 3223 | pub fn getAstTree(mod: *Module, root_scope: *Scope.File) !*const ast.Tree { |
| ... | ... | @@ -3117,17 +3242,19 @@ pub fn getAstTree(mod: *Module, root_scope: *Scope.File) !*const ast.Tree { |
| 3117 | 3242 | var msg = std.ArrayList(u8).init(mod.gpa); |
| 3118 | 3243 | defer msg.deinit(); |
| 3119 | 3244 | |
| 3245 | const token_starts = tree.tokens.items(.start); | |
| 3246 | ||
| 3120 | 3247 | try tree.renderError(parse_err, msg.writer()); |
| 3121 | 3248 | const err_msg = try mod.gpa.create(ErrorMsg); |
| 3122 | 3249 | err_msg.* = .{ |
| 3123 | 3250 | .src_loc = .{ |
| 3124 | 3251 | .container = .{ .file_scope = root_scope }, |
| 3125 | .lazy = .{ .token_abs = parse_err.token }, | |
| 3252 | .lazy = .{ .byte_abs = token_starts[parse_err.token] }, | |
| 3126 | 3253 | }, |
| 3127 | 3254 | .msg = msg.toOwnedSlice(), |
| 3128 | 3255 | }; |
| 3129 | 3256 | |
| 3130 | mod.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg); | |
| 3257 | mod.failed_files.putAssumeCapacityNoClobber(root_scope, err_msg); | |
| 3131 | 3258 | root_scope.status = .unloaded_parse_failure; |
| 3132 | 3259 | return error.AnalysisFail; |
| 3133 | 3260 | } |
| ... | ... | @@ -3167,7 +3294,15 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 3167 | 3294 | deleted_decls.putAssumeCapacityNoClobber(entry.key, {}); |
| 3168 | 3295 | } |
| 3169 | 3296 | |
| 3170 | for (decls) |decl_node, decl_i| switch (node_tags[decl_node]) { | |
| 3297 | // Keep track of decls that are invalidated from the update. Ultimately, | |
| 3298 | // the goal is to queue up `analyze_decl` tasks in the work queue for | |
| 3299 | // the outdated decls, but we cannot queue up the tasks until after | |
| 3300 | // we find out which ones have been deleted, otherwise there would be | |
| 3301 | // deleted Decl pointers in the work queue. | |
| 3302 | var outdated_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa); | |
| 3303 | defer outdated_decls.deinit(); | |
| 3304 | ||
| 3305 | for (decls) |decl_node| switch (node_tags[decl_node]) { | |
| 3171 | 3306 | .fn_decl => { |
| 3172 | 3307 | const fn_proto = node_datas[decl_node].lhs; |
| 3173 | 3308 | const body = node_datas[decl_node].rhs; |
| ... | ... | @@ -3177,8 +3312,8 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 3177 | 3312 | try mod.semaContainerFn( |
| 3178 | 3313 | container_scope, |
| 3179 | 3314 | &deleted_decls, |
| 3315 | &outdated_decls, | |
| 3180 | 3316 | decl_node, |
| 3181 | decl_i, | |
| 3182 | 3317 | tree.*, |
| 3183 | 3318 | body, |
| 3184 | 3319 | tree.fnProtoSimple(&params, fn_proto), |
| ... | ... | @@ -3187,8 +3322,8 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 3187 | 3322 | .fn_proto_multi => try mod.semaContainerFn( |
| 3188 | 3323 | container_scope, |
| 3189 | 3324 | &deleted_decls, |
| 3325 | &outdated_decls, | |
| 3190 | 3326 | decl_node, |
| 3191 | decl_i, | |
| 3192 | 3327 | tree.*, |
| 3193 | 3328 | body, |
| 3194 | 3329 | tree.fnProtoMulti(fn_proto), |
| ... | ... | @@ -3198,8 +3333,8 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 3198 | 3333 | try mod.semaContainerFn( |
| 3199 | 3334 | container_scope, |
| 3200 | 3335 | &deleted_decls, |
| 3336 | &outdated_decls, | |
| 3201 | 3337 | decl_node, |
| 3202 | decl_i, | |
| 3203 | 3338 | tree.*, |
| 3204 | 3339 | body, |
| 3205 | 3340 | tree.fnProtoOne(&params, fn_proto), |
| ... | ... | @@ -3208,8 +3343,8 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 3208 | 3343 | .fn_proto => try mod.semaContainerFn( |
| 3209 | 3344 | container_scope, |
| 3210 | 3345 | &deleted_decls, |
| 3346 | &outdated_decls, | |
| 3211 | 3347 | decl_node, |
| 3212 | decl_i, | |
| 3213 | 3348 | tree.*, |
| 3214 | 3349 | body, |
| 3215 | 3350 | tree.fnProto(fn_proto), |
| ... | ... | @@ -3222,8 +3357,8 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 3222 | 3357 | try mod.semaContainerFn( |
| 3223 | 3358 | container_scope, |
| 3224 | 3359 | &deleted_decls, |
| 3360 | &outdated_decls, | |
| 3225 | 3361 | decl_node, |
| 3226 | decl_i, | |
| 3227 | 3362 | tree.*, |
| 3228 | 3363 | 0, |
| 3229 | 3364 | tree.fnProtoSimple(&params, decl_node), |
| ... | ... | @@ -3232,8 +3367,8 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 3232 | 3367 | .fn_proto_multi => try mod.semaContainerFn( |
| 3233 | 3368 | container_scope, |
| 3234 | 3369 | &deleted_decls, |
| 3370 | &outdated_decls, | |
| 3235 | 3371 | decl_node, |
| 3236 | decl_i, | |
| 3237 | 3372 | tree.*, |
| 3238 | 3373 | 0, |
| 3239 | 3374 | tree.fnProtoMulti(decl_node), |
| ... | ... | @@ -3243,8 +3378,8 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 3243 | 3378 | try mod.semaContainerFn( |
| 3244 | 3379 | container_scope, |
| 3245 | 3380 | &deleted_decls, |
| 3381 | &outdated_decls, | |
| 3246 | 3382 | decl_node, |
| 3247 | decl_i, | |
| 3248 | 3383 | tree.*, |
| 3249 | 3384 | 0, |
| 3250 | 3385 | tree.fnProtoOne(&params, decl_node), |
| ... | ... | @@ -3253,8 +3388,8 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 3253 | 3388 | .fn_proto => try mod.semaContainerFn( |
| 3254 | 3389 | container_scope, |
| 3255 | 3390 | &deleted_decls, |
| 3391 | &outdated_decls, | |
| 3256 | 3392 | decl_node, |
| 3257 | decl_i, | |
| 3258 | 3393 | tree.*, |
| 3259 | 3394 | 0, |
| 3260 | 3395 | tree.fnProto(decl_node), |
| ... | ... | @@ -3263,32 +3398,32 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 3263 | 3398 | .global_var_decl => try mod.semaContainerVar( |
| 3264 | 3399 | container_scope, |
| 3265 | 3400 | &deleted_decls, |
| 3401 | &outdated_decls, | |
| 3266 | 3402 | decl_node, |
| 3267 | decl_i, | |
| 3268 | 3403 | tree.*, |
| 3269 | 3404 | tree.globalVarDecl(decl_node), |
| 3270 | 3405 | ), |
| 3271 | 3406 | .local_var_decl => try mod.semaContainerVar( |
| 3272 | 3407 | container_scope, |
| 3273 | 3408 | &deleted_decls, |
| 3409 | &outdated_decls, | |
| 3274 | 3410 | decl_node, |
| 3275 | decl_i, | |
| 3276 | 3411 | tree.*, |
| 3277 | 3412 | tree.localVarDecl(decl_node), |
| 3278 | 3413 | ), |
| 3279 | 3414 | .simple_var_decl => try mod.semaContainerVar( |
| 3280 | 3415 | container_scope, |
| 3281 | 3416 | &deleted_decls, |
| 3417 | &outdated_decls, | |
| 3282 | 3418 | decl_node, |
| 3283 | decl_i, | |
| 3284 | 3419 | tree.*, |
| 3285 | 3420 | tree.simpleVarDecl(decl_node), |
| 3286 | 3421 | ), |
| 3287 | 3422 | .aligned_var_decl => try mod.semaContainerVar( |
| 3288 | 3423 | container_scope, |
| 3289 | 3424 | &deleted_decls, |
| 3425 | &outdated_decls, | |
| 3290 | 3426 | decl_node, |
| 3291 | decl_i, | |
| 3292 | 3427 | tree.*, |
| 3293 | 3428 | tree.alignedVarDecl(decl_node), |
| 3294 | 3429 | ), |
| ... | ... | @@ -3301,49 +3436,48 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void { |
| 3301 | 3436 | const name_hash = container_scope.fullyQualifiedNameHash(name); |
| 3302 | 3437 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); |
| 3303 | 3438 | |
| 3304 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | |
| 3439 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_node, name_hash, contents_hash); | |
| 3305 | 3440 | container_scope.decls.putAssumeCapacity(new_decl, {}); |
| 3306 | 3441 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 3307 | 3442 | }, |
| 3308 | 3443 | |
| 3309 | .container_field_init => try mod.semaContainerField( | |
| 3310 | container_scope, | |
| 3311 | &deleted_decls, | |
| 3312 | decl_node, | |
| 3313 | decl_i, | |
| 3314 | tree.*, | |
| 3315 | tree.containerFieldInit(decl_node), | |
| 3316 | ), | |
| 3317 | .container_field_align => try mod.semaContainerField( | |
| 3318 | container_scope, | |
| 3319 | &deleted_decls, | |
| 3320 | decl_node, | |
| 3321 | decl_i, | |
| 3322 | tree.*, | |
| 3323 | tree.containerFieldAlign(decl_node), | |
| 3324 | ), | |
| 3325 | .container_field => try mod.semaContainerField( | |
| 3326 | container_scope, | |
| 3327 | &deleted_decls, | |
| 3328 | decl_node, | |
| 3329 | decl_i, | |
| 3330 | tree.*, | |
| 3331 | tree.containerField(decl_node), | |
| 3332 | ), | |
| 3444 | // Container fields are handled in AstGen. | |
| 3445 | .container_field_init, | |
| 3446 | .container_field_align, | |
| 3447 | .container_field, | |
| 3448 | => continue, | |
| 3333 | 3449 | |
| 3334 | 3450 | .test_decl => { |
| 3335 | log.err("TODO: analyze test decl", .{}); | |
| 3451 | if (mod.comp.bin_file.options.is_test) { | |
| 3452 | log.err("TODO: analyze test decl", .{}); | |
| 3453 | } | |
| 3336 | 3454 | }, |
| 3337 | 3455 | .@"usingnamespace" => { |
| 3338 | 3456 | log.err("TODO: analyze usingnamespace decl", .{}); |
| 3339 | 3457 | }, |
| 3340 | 3458 | else => unreachable, |
| 3341 | 3459 | }; |
| 3342 | // Handle explicitly deleted decls from the source code. Not to be confused | |
| 3343 | // with when we delete decls because they are no longer referenced. | |
| 3460 | // Handle explicitly deleted decls from the source code. This is one of two | |
| 3461 | // places that Decl deletions happen. The other is in `Compilation`, after | |
| 3462 | // `performAllTheWork`, where we iterate over `Module.deletion_set` and | |
| 3463 | // delete Decls which are no longer referenced. | |
| 3464 | // If a Decl is explicitly deleted from source, and also no longer referenced, | |
| 3465 | // it may be both in this `deleted_decls` set, as well as in the | |
| 3466 | // `Module.deletion_set`. To avoid deleting it twice, we remove it from the | |
| 3467 | // deletion set at this time. | |
| 3344 | 3468 | for (deleted_decls.items()) |entry| { |
| 3345 | log.debug("noticed '{s}' deleted from source", .{entry.key.name}); | |
| 3346 | try mod.deleteDecl(entry.key); | |
| 3469 | const decl = entry.key; | |
| 3470 | log.debug("'{s}' deleted from source", .{decl.name}); | |
| 3471 | if (decl.deletion_flag) { | |
| 3472 | log.debug("'{s}' redundantly in deletion set; removing", .{decl.name}); | |
| 3473 | mod.deletion_set.removeAssertDiscard(decl); | |
| 3474 | } | |
| 3475 | try mod.deleteDecl(decl, &outdated_decls); | |
| 3476 | } | |
| 3477 | // Finally we can queue up re-analysis tasks after we have processed | |
| 3478 | // the deleted decls. | |
| 3479 | for (outdated_decls.items()) |entry| { | |
| 3480 | try mod.markOutdatedDecl(entry.key); | |
| 3347 | 3481 | } |
| 3348 | 3482 | } |
| 3349 | 3483 | |
| ... | ... | @@ -3351,8 +3485,8 @@ fn semaContainerFn( |
| 3351 | 3485 | mod: *Module, |
| 3352 | 3486 | container_scope: *Scope.Container, |
| 3353 | 3487 | deleted_decls: *std.AutoArrayHashMap(*Decl, void), |
| 3488 | outdated_decls: *std.AutoArrayHashMap(*Decl, void), | |
| 3354 | 3489 | decl_node: ast.Node.Index, |
| 3355 | decl_i: usize, | |
| 3356 | 3490 | tree: ast.Tree, |
| 3357 | 3491 | body_node: ast.Node.Index, |
| 3358 | 3492 | fn_proto: ast.full.FnProto, |
| ... | ... | @@ -3361,28 +3495,34 @@ fn semaContainerFn( |
| 3361 | 3495 | defer tracy.end(); |
| 3362 | 3496 | |
| 3363 | 3497 | // We will create a Decl for it regardless of analysis status. |
| 3364 | const name_tok = fn_proto.name_token orelse { | |
| 3498 | const name_token = fn_proto.name_token orelse { | |
| 3365 | 3499 | // This problem will go away with #1717. |
| 3366 | 3500 | @panic("TODO missing function name"); |
| 3367 | 3501 | }; |
| 3368 | const name = tree.tokenSlice(name_tok); // TODO use identifierTokenString | |
| 3502 | const name = tree.tokenSlice(name_token); // TODO use identifierTokenString | |
| 3369 | 3503 | const name_hash = container_scope.fullyQualifiedNameHash(name); |
| 3370 | 3504 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node)); |
| 3371 | 3505 | if (mod.decl_table.get(name_hash)) |decl| { |
| 3372 | 3506 | // Update the AST Node index of the decl, even if its contents are unchanged, it may |
| 3373 | 3507 | // have been re-ordered. |
| 3374 | decl.src_index = decl_i; | |
| 3508 | const prev_src_node = decl.src_node; | |
| 3509 | decl.src_node = decl_node; | |
| 3375 | 3510 | if (deleted_decls.swapRemove(decl) == null) { |
| 3376 | 3511 | decl.analysis = .sema_failure; |
| 3377 | 3512 | const msg = try ErrorMsg.create(mod.gpa, .{ |
| 3378 | 3513 | .container = .{ .file_scope = container_scope.file_scope }, |
| 3379 | .lazy = .{ .token_abs = name_tok }, | |
| 3514 | .lazy = .{ .token_abs = name_token }, | |
| 3380 | 3515 | }, "redefinition of '{s}'", .{decl.name}); |
| 3381 | 3516 | errdefer msg.destroy(mod.gpa); |
| 3517 | const other_src_loc: SrcLoc = .{ | |
| 3518 | .container = .{ .file_scope = decl.container.file_scope }, | |
| 3519 | .lazy = .{ .node_abs = prev_src_node }, | |
| 3520 | }; | |
| 3521 | try mod.errNoteNonLazy(other_src_loc, msg, "previous definition here", .{}); | |
| 3382 | 3522 | try mod.failed_decls.putNoClobber(mod.gpa, decl, msg); |
| 3383 | 3523 | } else { |
| 3384 | 3524 | if (!srcHashEql(decl.contents_hash, contents_hash)) { |
| 3385 | try mod.markOutdatedDecl(decl); | |
| 3525 | try outdated_decls.put(decl, {}); | |
| 3386 | 3526 | decl.contents_hash = contents_hash; |
| 3387 | 3527 | } else switch (mod.comp.bin_file.tag) { |
| 3388 | 3528 | .coff => { |
| ... | ... | @@ -3402,7 +3542,7 @@ fn semaContainerFn( |
| 3402 | 3542 | } |
| 3403 | 3543 | } |
| 3404 | 3544 | } else { |
| 3405 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | |
| 3545 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_node, name_hash, contents_hash); | |
| 3406 | 3546 | container_scope.decls.putAssumeCapacity(new_decl, {}); |
| 3407 | 3547 | if (fn_proto.extern_export_token) |maybe_export_token| { |
| 3408 | 3548 | const token_tags = tree.tokens.items(.tag); |
| ... | ... | @@ -3410,6 +3550,7 @@ fn semaContainerFn( |
| 3410 | 3550 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 3411 | 3551 | } |
| 3412 | 3552 | } |
| 3553 | new_decl.is_pub = fn_proto.visib_token != null; | |
| 3413 | 3554 | } |
| 3414 | 3555 | } |
| 3415 | 3556 | |
| ... | ... | @@ -3417,8 +3558,8 @@ fn semaContainerVar( |
| 3417 | 3558 | mod: *Module, |
| 3418 | 3559 | container_scope: *Scope.Container, |
| 3419 | 3560 | deleted_decls: *std.AutoArrayHashMap(*Decl, void), |
| 3561 | outdated_decls: *std.AutoArrayHashMap(*Decl, void), | |
| 3420 | 3562 | decl_node: ast.Node.Index, |
| 3421 | decl_i: usize, | |
| 3422 | 3563 | tree: ast.Tree, |
| 3423 | 3564 | var_decl: ast.full.VarDecl, |
| 3424 | 3565 | ) !void { |
| ... | ... | @@ -3432,21 +3573,27 @@ fn semaContainerVar( |
| 3432 | 3573 | if (mod.decl_table.get(name_hash)) |decl| { |
| 3433 | 3574 | // Update the AST Node index of the decl, even if its contents are unchanged, it may |
| 3434 | 3575 | // have been re-ordered. |
| 3435 | decl.src_index = decl_i; | |
| 3576 | const prev_src_node = decl.src_node; | |
| 3577 | decl.src_node = decl_node; | |
| 3436 | 3578 | if (deleted_decls.swapRemove(decl) == null) { |
| 3437 | 3579 | decl.analysis = .sema_failure; |
| 3438 | const err_msg = try ErrorMsg.create(mod.gpa, .{ | |
| 3580 | const msg = try ErrorMsg.create(mod.gpa, .{ | |
| 3439 | 3581 | .container = .{ .file_scope = container_scope.file_scope }, |
| 3440 | 3582 | .lazy = .{ .token_abs = name_token }, |
| 3441 | 3583 | }, "redefinition of '{s}'", .{decl.name}); |
| 3442 | errdefer err_msg.destroy(mod.gpa); | |
| 3443 | try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg); | |
| 3584 | errdefer msg.destroy(mod.gpa); | |
| 3585 | const other_src_loc: SrcLoc = .{ | |
| 3586 | .container = .{ .file_scope = decl.container.file_scope }, | |
| 3587 | .lazy = .{ .node_abs = prev_src_node }, | |
| 3588 | }; | |
| 3589 | try mod.errNoteNonLazy(other_src_loc, msg, "previous definition here", .{}); | |
| 3590 | try mod.failed_decls.putNoClobber(mod.gpa, decl, msg); | |
| 3444 | 3591 | } else if (!srcHashEql(decl.contents_hash, contents_hash)) { |
| 3445 | try mod.markOutdatedDecl(decl); | |
| 3592 | try outdated_decls.put(decl, {}); | |
| 3446 | 3593 | decl.contents_hash = contents_hash; |
| 3447 | 3594 | } |
| 3448 | 3595 | } else { |
| 3449 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_i, name_hash, contents_hash); | |
| 3596 | const new_decl = try mod.createNewDecl(&container_scope.base, name, decl_node, name_hash, contents_hash); | |
| 3450 | 3597 | container_scope.decls.putAssumeCapacity(new_decl, {}); |
| 3451 | 3598 | if (var_decl.extern_export_token) |maybe_export_token| { |
| 3452 | 3599 | const token_tags = tree.tokens.items(.tag); |
| ... | ... | @@ -3454,35 +3601,31 @@ fn semaContainerVar( |
| 3454 | 3601 | mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); |
| 3455 | 3602 | } |
| 3456 | 3603 | } |
| 3604 | new_decl.is_pub = var_decl.visib_token != null; | |
| 3457 | 3605 | } |
| 3458 | 3606 | } |
| 3459 | 3607 | |
| 3460 | fn semaContainerField( | |
| 3608 | pub fn deleteDecl( | |
| 3461 | 3609 | mod: *Module, |
| 3462 | container_scope: *Scope.Container, | |
| 3463 | deleted_decls: *std.AutoArrayHashMap(*Decl, void), | |
| 3464 | decl_node: ast.Node.Index, | |
| 3465 | decl_i: usize, | |
| 3466 | tree: ast.Tree, | |
| 3467 | field: ast.full.ContainerField, | |
| 3610 | decl: *Decl, | |
| 3611 | outdated_decls: ?*std.AutoArrayHashMap(*Decl, void), | |
| 3468 | 3612 | ) !void { |
| 3469 | 3613 | const tracy = trace(@src()); |
| 3470 | 3614 | defer tracy.end(); |
| 3471 | 3615 | |
| 3472 | log.err("TODO: analyze container field", .{}); | |
| 3473 | } | |
| 3474 | ||
| 3475 | pub fn deleteDecl(mod: *Module, decl: *Decl) !void { | |
| 3476 | const tracy = trace(@src()); | |
| 3477 | defer tracy.end(); | |
| 3616 | log.debug("deleting decl '{s}'", .{decl.name}); | |
| 3478 | 3617 | |
| 3479 | try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.items.len + decl.dependencies.items().len); | |
| 3618 | if (outdated_decls) |map| { | |
| 3619 | _ = map.swapRemove(decl); | |
| 3620 | try map.ensureCapacity(map.count() + decl.dependants.count()); | |
| 3621 | } | |
| 3622 | try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.count() + | |
| 3623 | decl.dependencies.count()); | |
| 3480 | 3624 | |
| 3481 | 3625 | // Remove from the namespace it resides in. In the case of an anonymous Decl it will |
| 3482 | 3626 | // not be present in the set, and this does nothing. |
| 3483 | 3627 | decl.container.removeDecl(decl); |
| 3484 | 3628 | |
| 3485 | log.debug("deleting decl '{s}'", .{decl.name}); | |
| 3486 | 3629 | const name_hash = decl.fullyQualifiedNameHash(); |
| 3487 | 3630 | mod.decl_table.removeAssertDiscard(name_hash); |
| 3488 | 3631 | // Remove itself from its dependencies, because we are about to destroy the decl pointer. |
| ... | ... | @@ -3493,16 +3636,22 @@ pub fn deleteDecl(mod: *Module, decl: *Decl) !void { |
| 3493 | 3636 | // We don't recursively perform a deletion here, because during the update, |
| 3494 | 3637 | // another reference to it may turn up. |
| 3495 | 3638 | dep.deletion_flag = true; |
| 3496 | mod.deletion_set.appendAssumeCapacity(dep); | |
| 3639 | mod.deletion_set.putAssumeCapacity(dep, {}); | |
| 3497 | 3640 | } |
| 3498 | 3641 | } |
| 3499 | // Anything that depends on this deleted decl certainly needs to be re-analyzed. | |
| 3642 | // Anything that depends on this deleted decl needs to be re-analyzed. | |
| 3500 | 3643 | for (decl.dependants.items()) |entry| { |
| 3501 | 3644 | const dep = entry.key; |
| 3502 | 3645 | dep.removeDependency(decl); |
| 3503 | if (dep.analysis != .outdated) { | |
| 3504 | // TODO Move this failure possibility to the top of the function. | |
| 3505 | try mod.markOutdatedDecl(dep); | |
| 3646 | if (outdated_decls) |map| { | |
| 3647 | map.putAssumeCapacity(dep, {}); | |
| 3648 | } else if (std.debug.runtime_safety) { | |
| 3649 | // If `outdated_decls` is `null`, it means we're being called from | |
| 3650 | // `Compilation` after `performAllTheWork` and we cannot queue up any | |
| 3651 | // more work. `dep` must necessarily be another Decl that is no longer | |
| 3652 | // being referenced, and will be in the `deletion_set`. Otherwise, | |
| 3653 | // something has gone wrong. | |
| 3654 | assert(mod.deletion_set.contains(dep)); | |
| 3506 | 3655 | } |
| 3507 | 3656 | } |
| 3508 | 3657 | if (mod.failed_decls.swapRemove(decl)) |entry| { |
| ... | ... | @@ -3638,7 +3787,7 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { |
| 3638 | 3787 | fn allocateNewDecl( |
| 3639 | 3788 | mod: *Module, |
| 3640 | 3789 | scope: *Scope, |
| 3641 | src_index: usize, | |
| 3790 | src_node: ast.Node.Index, | |
| 3642 | 3791 | contents_hash: std.zig.SrcHash, |
| 3643 | 3792 | ) !*Decl { |
| 3644 | 3793 | // If we have emit-h then we must allocate a bigger structure to store the emit-h state. |
| ... | ... | @@ -3654,7 +3803,7 @@ fn allocateNewDecl( |
| 3654 | 3803 | new_decl.* = .{ |
| 3655 | 3804 | .name = "", |
| 3656 | 3805 | .container = scope.namespace(), |
| 3657 | .src_index = src_index, | |
| 3806 | .src_node = src_node, | |
| 3658 | 3807 | .typed_value = .{ .never_succeeded = {} }, |
| 3659 | 3808 | .analysis = .unreferenced, |
| 3660 | 3809 | .deletion_flag = false, |
| ... | ... | @@ -3664,7 +3813,7 @@ fn allocateNewDecl( |
| 3664 | 3813 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, |
| 3665 | 3814 | .macho => .{ .macho = link.File.MachO.TextBlock.empty }, |
| 3666 | 3815 | .c => .{ .c = link.File.C.DeclBlock.empty }, |
| 3667 | .wasm => .{ .wasm = {} }, | |
| 3816 | .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, | |
| 3668 | 3817 | .spirv => .{ .spirv = {} }, |
| 3669 | 3818 | }, |
| 3670 | 3819 | .fn_link = switch (mod.comp.bin_file.tag) { |
| ... | ... | @@ -3672,7 +3821,7 @@ fn allocateNewDecl( |
| 3672 | 3821 | .elf => .{ .elf = link.File.Elf.SrcFn.empty }, |
| 3673 | 3822 | .macho => .{ .macho = link.File.MachO.SrcFn.empty }, |
| 3674 | 3823 | .c => .{ .c = link.File.C.FnBlock.empty }, |
| 3675 | .wasm => .{ .wasm = null }, | |
| 3824 | .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, | |
| 3676 | 3825 | .spirv => .{ .spirv = .{} }, |
| 3677 | 3826 | }, |
| 3678 | 3827 | .generation = 0, |
| ... | ... | @@ -3685,12 +3834,12 @@ fn createNewDecl( |
| 3685 | 3834 | mod: *Module, |
| 3686 | 3835 | scope: *Scope, |
| 3687 | 3836 | decl_name: []const u8, |
| 3688 | src_index: usize, | |
| 3837 | src_node: ast.Node.Index, | |
| 3689 | 3838 | name_hash: Scope.NameHash, |
| 3690 | 3839 | contents_hash: std.zig.SrcHash, |
| 3691 | 3840 | ) !*Decl { |
| 3692 | 3841 | try mod.decl_table.ensureCapacity(mod.gpa, mod.decl_table.items().len + 1); |
| 3693 | const new_decl = try mod.allocateNewDecl(scope, src_index, contents_hash); | |
| 3842 | const new_decl = try mod.allocateNewDecl(scope, src_node, contents_hash); | |
| 3694 | 3843 | errdefer mod.gpa.destroy(new_decl); |
| 3695 | 3844 | new_decl.name = try mem.dupeZ(mod.gpa, u8, decl_name); |
| 3696 | 3845 | mod.decl_table.putAssumeCapacityNoClobber(name_hash, new_decl); |
| ... | ... | @@ -3903,7 +4052,7 @@ pub fn createAnonymousDecl( |
| 3903 | 4052 | defer mod.gpa.free(name); |
| 3904 | 4053 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); |
| 3905 | 4054 | const src_hash: std.zig.SrcHash = undefined; |
| 3906 | const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash); | |
| 4055 | const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_node, name_hash, src_hash); | |
| 3907 | 4056 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 3908 | 4057 | |
| 3909 | 4058 | decl_arena_state.* = decl_arena.state; |
| ... | ... | @@ -3939,7 +4088,7 @@ pub fn createContainerDecl( |
| 3939 | 4088 | defer mod.gpa.free(name); |
| 3940 | 4089 | const name_hash = scope.namespace().fullyQualifiedNameHash(name); |
| 3941 | 4090 | const src_hash: std.zig.SrcHash = undefined; |
| 3942 | const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_index, name_hash, src_hash); | |
| 4091 | const new_decl = try mod.createNewDecl(scope, name, scope_decl.src_node, name_hash, src_hash); | |
| 3943 | 4092 | const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); |
| 3944 | 4093 | |
| 3945 | 4094 | decl_arena_state.* = decl_arena.state; |
| ... | ... | @@ -4003,13 +4152,23 @@ pub fn errNote( |
| 4003 | 4152 | parent: *ErrorMsg, |
| 4004 | 4153 | comptime format: []const u8, |
| 4005 | 4154 | args: anytype, |
| 4155 | ) error{OutOfMemory}!void { | |
| 4156 | return mod.errNoteNonLazy(src.toSrcLoc(scope), parent, format, args); | |
| 4157 | } | |
| 4158 | ||
| 4159 | pub fn errNoteNonLazy( | |
| 4160 | mod: *Module, | |
| 4161 | src_loc: SrcLoc, | |
| 4162 | parent: *ErrorMsg, | |
| 4163 | comptime format: []const u8, | |
| 4164 | args: anytype, | |
| 4006 | 4165 | ) error{OutOfMemory}!void { |
| 4007 | 4166 | const msg = try std.fmt.allocPrint(mod.gpa, format, args); |
| 4008 | 4167 | errdefer mod.gpa.free(msg); |
| 4009 | 4168 | |
| 4010 | 4169 | parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1); |
| 4011 | 4170 | parent.notes[parent.notes.len - 1] = .{ |
| 4012 | .src_loc = src.toSrcLoc(scope), | |
| 4171 | .src_loc = src_loc, | |
| 4013 | 4172 | .msg = msg, |
| 4014 | 4173 | }; |
| 4015 | 4174 | } |
| ... | ... | @@ -4412,7 +4571,29 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) |
| 4412 | 4571 | var buf: ArrayListUnmanaged(u8) = .{}; |
| 4413 | 4572 | defer buf.deinit(mod.gpa); |
| 4414 | 4573 | try parseStrLit(mod, scope, token, &buf, ident_name, 1); |
| 4415 | return buf.toOwnedSlice(mod.gpa); | |
| 4574 | const duped = try scope.arena().dupe(u8, buf.items); | |
| 4575 | return duped; | |
| 4576 | } | |
| 4577 | ||
| 4578 | /// `scope` is only used for error reporting. | |
| 4579 | /// The string is stored in `arena` regardless of whether it uses @"" syntax. | |
| 4580 | pub fn identifierTokenStringTreeArena( | |
| 4581 | mod: *Module, | |
| 4582 | scope: *Scope, | |
| 4583 | token: ast.TokenIndex, | |
| 4584 | tree: *const ast.Tree, | |
| 4585 | arena: *Allocator, | |
| 4586 | ) InnerError![]u8 { | |
| 4587 | const token_tags = tree.tokens.items(.tag); | |
| 4588 | assert(token_tags[token] == .identifier); | |
| 4589 | const ident_name = tree.tokenSlice(token); | |
| 4590 | if (!mem.startsWith(u8, ident_name, "@")) { | |
| 4591 | return arena.dupe(u8, ident_name); | |
| 4592 | } | |
| 4593 | var buf: ArrayListUnmanaged(u8) = .{}; | |
| 4594 | defer buf.deinit(mod.gpa); | |
| 4595 | try parseStrLit(mod, scope, token, &buf, ident_name, 1); | |
| 4596 | return arena.dupe(u8, buf.items); | |
| 4416 | 4597 | } |
| 4417 | 4598 | |
| 4418 | 4599 | /// Given an identifier token, obtain the string for it (possibly parsing as a string |
| ... | ... | @@ -4502,3 +4683,10 @@ pub fn parseStrLit( |
| 4502 | 4683 | }, |
| 4503 | 4684 | } |
| 4504 | 4685 | } |
| 4686 | ||
| 4687 | pub fn unloadFile(mod: *Module, file_scope: *Scope.File) void { | |
| 4688 | if (file_scope.status == .unloaded_parse_failure) { | |
| 4689 | mod.failed_files.swapRemove(file_scope).?.value.destroy(mod.gpa); | |
| 4690 | } | |
| 4691 | file_scope.unload(mod.gpa); | |
| 4692 | } |
src/Package.zig+68-8| ... | ... | @@ -4,18 +4,29 @@ const std = @import("std"); |
| 4 | 4 | const fs = std.fs; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | const Allocator = mem.Allocator; |
| 7 | const assert = std.debug.assert; | |
| 7 | 8 | |
| 8 | 9 | const Compilation = @import("Compilation.zig"); |
| 10 | const Module = @import("Module.zig"); | |
| 9 | 11 | |
| 10 | 12 | pub const Table = std.StringHashMapUnmanaged(*Package); |
| 11 | 13 | |
| 14 | pub const root_namespace_hash: Module.Scope.NameHash = .{ | |
| 15 | 0, 0, 6, 6, 6, 0, 0, 0, | |
| 16 | 6, 9, 0, 0, 0, 4, 2, 0, | |
| 17 | }; | |
| 18 | ||
| 12 | 19 | root_src_directory: Compilation.Directory, |
| 13 | 20 | /// Relative to `root_src_directory`. May contain path separators. |
| 14 | 21 | root_src_path: []const u8, |
| 15 | 22 | table: Table = .{}, |
| 16 | 23 | parent: ?*Package = null, |
| 24 | namespace_hash: Module.Scope.NameHash, | |
| 25 | /// Whether to free `root_src_directory` on `destroy`. | |
| 26 | root_src_directory_owned: bool = false, | |
| 17 | 27 | |
| 18 | 28 | /// Allocate a Package. No references to the slices passed are kept. |
| 29 | /// Don't forget to set `namespace_hash` later. | |
| 19 | 30 | pub fn create( |
| 20 | 31 | gpa: *Allocator, |
| 21 | 32 | /// Null indicates the current working directory |
| ... | ... | @@ -38,27 +49,69 @@ pub fn create( |
| 38 | 49 | .handle = if (owned_dir_path) |p| try fs.cwd().openDir(p, .{}) else fs.cwd(), |
| 39 | 50 | }, |
| 40 | 51 | .root_src_path = owned_src_path, |
| 52 | .root_src_directory_owned = true, | |
| 53 | .namespace_hash = undefined, | |
| 41 | 54 | }; |
| 42 | 55 | |
| 43 | 56 | return ptr; |
| 44 | 57 | } |
| 45 | 58 | |
| 46 | /// Free all memory associated with this package and recursively call destroy | |
| 47 | /// on all packages in its table | |
| 59 | pub fn createWithDir( | |
| 60 | gpa: *Allocator, | |
| 61 | directory: Compilation.Directory, | |
| 62 | /// Relative to `directory`. If null, means `directory` is the root src dir | |
| 63 | /// and is owned externally. | |
| 64 | root_src_dir_path: ?[]const u8, | |
| 65 | /// Relative to root_src_dir_path | |
| 66 | root_src_path: []const u8, | |
| 67 | ) !*Package { | |
| 68 | const ptr = try gpa.create(Package); | |
| 69 | errdefer gpa.destroy(ptr); | |
| 70 | ||
| 71 | const owned_src_path = try gpa.dupe(u8, root_src_path); | |
| 72 | errdefer gpa.free(owned_src_path); | |
| 73 | ||
| 74 | if (root_src_dir_path) |p| { | |
| 75 | const owned_dir_path = try directory.join(gpa, &[1][]const u8{p}); | |
| 76 | errdefer gpa.free(owned_dir_path); | |
| 77 | ||
| 78 | ptr.* = .{ | |
| 79 | .root_src_directory = .{ | |
| 80 | .path = owned_dir_path, | |
| 81 | .handle = try directory.handle.openDir(p, .{}), | |
| 82 | }, | |
| 83 | .root_src_directory_owned = true, | |
| 84 | .root_src_path = owned_src_path, | |
| 85 | .namespace_hash = undefined, | |
| 86 | }; | |
| 87 | } else { | |
| 88 | ptr.* = .{ | |
| 89 | .root_src_directory = directory, | |
| 90 | .root_src_directory_owned = false, | |
| 91 | .root_src_path = owned_src_path, | |
| 92 | .namespace_hash = undefined, | |
| 93 | }; | |
| 94 | } | |
| 95 | return ptr; | |
| 96 | } | |
| 97 | ||
| 98 | /// Free all memory associated with this package. It does not destroy any packages | |
| 99 | /// inside its table; the caller is responsible for calling destroy() on them. | |
| 48 | 100 | pub fn destroy(pkg: *Package, gpa: *Allocator) void { |
| 49 | 101 | gpa.free(pkg.root_src_path); |
| 50 | 102 | |
| 51 | // If root_src_directory.path is null then the handle is the cwd() | |
| 52 | // which shouldn't be closed. | |
| 53 | if (pkg.root_src_directory.path) |p| { | |
| 54 | gpa.free(p); | |
| 55 | pkg.root_src_directory.handle.close(); | |
| 103 | if (pkg.root_src_directory_owned) { | |
| 104 | // If root_src_directory.path is null then the handle is the cwd() | |
| 105 | // which shouldn't be closed. | |
| 106 | if (pkg.root_src_directory.path) |p| { | |
| 107 | gpa.free(p); | |
| 108 | pkg.root_src_directory.handle.close(); | |
| 109 | } | |
| 56 | 110 | } |
| 57 | 111 | |
| 58 | 112 | { |
| 59 | 113 | var it = pkg.table.iterator(); |
| 60 | 114 | while (it.next()) |kv| { |
| 61 | kv.value.destroy(gpa); | |
| 62 | 115 | gpa.free(kv.key); |
| 63 | 116 | } |
| 64 | 117 | } |
| ... | ... | @@ -72,3 +125,10 @@ pub fn add(pkg: *Package, gpa: *Allocator, name: []const u8, package: *Package) |
| 72 | 125 | const name_dupe = try mem.dupe(gpa, u8, name); |
| 73 | 126 | pkg.table.putAssumeCapacityNoClobber(name_dupe, package); |
| 74 | 127 | } |
| 128 | ||
| 129 | pub fn addAndAdopt(parent: *Package, gpa: *Allocator, name: []const u8, child: *Package) !void { | |
| 130 | assert(child.parent == null); // make up your mind, who is the parent?? | |
| 131 | child.parent = parent; | |
| 132 | child.namespace_hash = std.zig.hashName(parent.namespace_hash, ":", name); | |
| 133 | return parent.add(gpa, name, child); | |
| 134 | } |
src/Sema.zig+734-133| ... | ... | @@ -168,7 +168,6 @@ pub fn analyzeBody( |
| 168 | 168 | .cmp_lte => try sema.zirCmp(block, inst, .lte), |
| 169 | 169 | .cmp_neq => try sema.zirCmp(block, inst, .neq), |
| 170 | 170 | .coerce_result_ptr => try sema.zirCoerceResultPtr(block, inst), |
| 171 | .@"const" => try sema.zirConst(block, inst), | |
| 172 | 171 | .decl_ref => try sema.zirDeclRef(block, inst), |
| 173 | 172 | .decl_val => try sema.zirDeclVal(block, inst), |
| 174 | 173 | .load => try sema.zirLoad(block, inst), |
| ... | ... | @@ -179,6 +178,8 @@ pub fn analyzeBody( |
| 179 | 178 | .elem_val_node => try sema.zirElemValNode(block, inst), |
| 180 | 179 | .enum_literal => try sema.zirEnumLiteral(block, inst), |
| 181 | 180 | .enum_literal_small => try sema.zirEnumLiteralSmall(block, inst), |
| 181 | .enum_to_int => try sema.zirEnumToInt(block, inst), | |
| 182 | .int_to_enum => try sema.zirIntToEnum(block, inst), | |
| 182 | 183 | .err_union_code => try sema.zirErrUnionCode(block, inst), |
| 183 | 184 | .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst), |
| 184 | 185 | .err_union_payload_safe => try sema.zirErrUnionPayload(block, inst, true), |
| ... | ... | @@ -198,9 +199,12 @@ pub fn analyzeBody( |
| 198 | 199 | .fn_type_cc => try sema.zirFnTypeCc(block, inst, false), |
| 199 | 200 | .fn_type_cc_var_args => try sema.zirFnTypeCc(block, inst, true), |
| 200 | 201 | .fn_type_var_args => try sema.zirFnType(block, inst, true), |
| 202 | .has_decl => try sema.zirHasDecl(block, inst), | |
| 201 | 203 | .import => try sema.zirImport(block, inst), |
| 202 | 204 | .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst), |
| 203 | 205 | .int => try sema.zirInt(block, inst), |
| 206 | .float => try sema.zirFloat(block, inst), | |
| 207 | .float128 => try sema.zirFloat128(block, inst), | |
| 204 | 208 | .int_type => try sema.zirIntType(block, inst), |
| 205 | 209 | .intcast => try sema.zirIntcast(block, inst), |
| 206 | 210 | .is_err => try sema.zirIsErr(block, inst), |
| ... | ... | @@ -255,16 +259,20 @@ pub fn analyzeBody( |
| 255 | 259 | .switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true), |
| 256 | 260 | .switch_capture_else => try sema.zirSwitchCaptureElse(block, inst, false), |
| 257 | 261 | .switch_capture_else_ref => try sema.zirSwitchCaptureElse(block, inst, true), |
| 262 | .type_info => try sema.zirTypeInfo(block, inst), | |
| 258 | 263 | .typeof => try sema.zirTypeof(block, inst), |
| 259 | 264 | .typeof_elem => try sema.zirTypeofElem(block, inst), |
| 260 | 265 | .typeof_peer => try sema.zirTypeofPeer(block, inst), |
| 261 | 266 | .xor => try sema.zirBitwise(block, inst, .xor), |
| 262 | 267 | .struct_init_empty => try sema.zirStructInitEmpty(block, inst), |
| 268 | .struct_init => try sema.zirStructInit(block, inst), | |
| 269 | .field_type => try sema.zirFieldType(block, inst), | |
| 263 | 270 | |
| 264 | 271 | .struct_decl => try sema.zirStructDecl(block, inst, .Auto), |
| 265 | 272 | .struct_decl_packed => try sema.zirStructDecl(block, inst, .Packed), |
| 266 | 273 | .struct_decl_extern => try sema.zirStructDecl(block, inst, .Extern), |
| 267 | .enum_decl => try sema.zirEnumDecl(block, inst), | |
| 274 | .enum_decl => try sema.zirEnumDecl(block, inst, false), | |
| 275 | .enum_decl_nonexhaustive => try sema.zirEnumDecl(block, inst, true), | |
| 268 | 276 | .union_decl => try sema.zirUnionDecl(block, inst), |
| 269 | 277 | .opaque_decl => try sema.zirOpaqueDecl(block, inst), |
| 270 | 278 | |
| ... | ... | @@ -338,6 +346,10 @@ pub fn analyzeBody( |
| 338 | 346 | try sema.zirValidateStructInitPtr(block, inst); |
| 339 | 347 | continue; |
| 340 | 348 | }, |
| 349 | .@"export" => { | |
| 350 | try sema.zirExport(block, inst); | |
| 351 | continue; | |
| 352 | }, | |
| 341 | 353 | |
| 342 | 354 | // Special case instructions to handle comptime control flow. |
| 343 | 355 | .repeat_inline => { |
| ... | ... | @@ -498,18 +510,6 @@ fn resolveInstConst( |
| 498 | 510 | }; |
| 499 | 511 | } |
| 500 | 512 | |
| 501 | fn zirConst(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | |
| 502 | const tracy = trace(@src()); | |
| 503 | defer tracy.end(); | |
| 504 | ||
| 505 | const tv_ptr = sema.code.instructions.items(.data)[inst].@"const"; | |
| 506 | // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions | |
| 507 | // after analysis. This happens, for example, with variable declaration initialization | |
| 508 | // expressions. | |
| 509 | const typed_value_copy = try tv_ptr.copy(sema.arena); | |
| 510 | return sema.mod.constInst(sema.arena, .unneeded, typed_value_copy); | |
| 511 | } | |
| 512 | ||
| 513 | 513 | fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { |
| 514 | 514 | const tracy = trace(@src()); |
| 515 | 515 | defer tracy.end(); |
| ... | ... | @@ -600,6 +600,11 @@ fn zirStructDecl( |
| 600 | 600 | |
| 601 | 601 | const struct_obj = try new_decl_arena.allocator.create(Module.Struct); |
| 602 | 602 | const struct_ty = try Type.Tag.@"struct".create(&new_decl_arena.allocator, struct_obj); |
| 603 | const struct_val = try Value.Tag.ty.create(&new_decl_arena.allocator, struct_ty); | |
| 604 | const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{ | |
| 605 | .ty = Type.initTag(.type), | |
| 606 | .val = struct_val, | |
| 607 | }); | |
| 603 | 608 | struct_obj.* = .{ |
| 604 | 609 | .owner_decl = sema.owner_decl, |
| 605 | 610 | .fields = fields_map, |
| ... | ... | @@ -607,16 +612,18 @@ fn zirStructDecl( |
| 607 | 612 | .container = .{ |
| 608 | 613 | .ty = struct_ty, |
| 609 | 614 | .file_scope = block.getFileScope(), |
| 615 | .parent_name_hash = new_decl.fullyQualifiedNameHash(), | |
| 610 | 616 | }, |
| 611 | 617 | }; |
| 612 | const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{ | |
| 613 | .ty = Type.initTag(.type), | |
| 614 | .val = try Value.Tag.ty.create(gpa, struct_ty), | |
| 615 | }); | |
| 616 | 618 | return sema.analyzeDeclVal(block, src, new_decl); |
| 617 | 619 | } |
| 618 | 620 | |
| 619 | fn zirEnumDecl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | |
| 621 | fn zirEnumDecl( | |
| 622 | sema: *Sema, | |
| 623 | block: *Scope.Block, | |
| 624 | inst: zir.Inst.Index, | |
| 625 | nonexhaustive: bool, | |
| 626 | ) InnerError!*Inst { | |
| 620 | 627 | const tracy = trace(@src()); |
| 621 | 628 | defer tracy.end(); |
| 622 | 629 | |
| ... | ... | @@ -787,8 +794,8 @@ fn zirAllocInferred( |
| 787 | 794 | const tracy = trace(@src()); |
| 788 | 795 | defer tracy.end(); |
| 789 | 796 | |
| 790 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | |
| 791 | const src = inst_data.src(); | |
| 797 | const src_node = sema.code.instructions.items(.data)[inst].node; | |
| 798 | const src: LazySrcLoc = .{ .node_offset = src_node }; | |
| 792 | 799 | |
| 793 | 800 | const val_payload = try sema.arena.create(Value.Payload.InferredAlloc); |
| 794 | 801 | val_payload.* = .{ |
| ... | ... | @@ -837,12 +844,100 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Ind |
| 837 | 844 | const tracy = trace(@src()); |
| 838 | 845 | defer tracy.end(); |
| 839 | 846 | |
| 840 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 841 | const src = inst_data.src(); | |
| 842 | const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index); | |
| 843 | const instrs = sema.code.extra[extra.end..][0..extra.data.body_len]; | |
| 847 | const gpa = sema.gpa; | |
| 848 | const mod = sema.mod; | |
| 849 | const validate_inst = sema.code.instructions.items(.data)[inst].pl_node; | |
| 850 | const struct_init_src = validate_inst.src(); | |
| 851 | const validate_extra = sema.code.extraData(zir.Inst.Block, validate_inst.payload_index); | |
| 852 | const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len]; | |
| 853 | ||
| 854 | const struct_obj: *Module.Struct = s: { | |
| 855 | const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node; | |
| 856 | const field_ptr_extra = sema.code.extraData(zir.Inst.Field, field_ptr_data.payload_index).data; | |
| 857 | const object_ptr = try sema.resolveInst(field_ptr_extra.lhs); | |
| 858 | break :s object_ptr.ty.elemType().castTag(.@"struct").?.data; | |
| 859 | }; | |
| 860 | ||
| 861 | // Maps field index to field_ptr index of where it was already initialized. | |
| 862 | const found_fields = try gpa.alloc(zir.Inst.Index, struct_obj.fields.entries.items.len); | |
| 863 | defer gpa.free(found_fields); | |
| 864 | ||
| 865 | mem.set(zir.Inst.Index, found_fields, 0); | |
| 866 | ||
| 867 | for (instrs) |field_ptr| { | |
| 868 | const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node; | |
| 869 | const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_ptr_data.src_node }; | |
| 870 | const field_ptr_extra = sema.code.extraData(zir.Inst.Field, field_ptr_data.payload_index).data; | |
| 871 | const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start); | |
| 872 | const field_index = struct_obj.fields.getIndex(field_name) orelse | |
| 873 | return sema.failWithBadFieldAccess(block, struct_obj, field_src, field_name); | |
| 874 | if (found_fields[field_index] != 0) { | |
| 875 | const other_field_ptr = found_fields[field_index]; | |
| 876 | const other_field_ptr_data = sema.code.instructions.items(.data)[other_field_ptr].pl_node; | |
| 877 | const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_ptr_data.src_node }; | |
| 878 | const msg = msg: { | |
| 879 | const msg = try mod.errMsg(&block.base, field_src, "duplicate field", .{}); | |
| 880 | errdefer msg.destroy(gpa); | |
| 881 | try mod.errNote(&block.base, other_field_src, msg, "other field here", .{}); | |
| 882 | break :msg msg; | |
| 883 | }; | |
| 884 | return mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 885 | } | |
| 886 | found_fields[field_index] = field_ptr; | |
| 887 | } | |
| 888 | ||
| 889 | var root_msg: ?*Module.ErrorMsg = null; | |
| 890 | ||
| 891 | for (found_fields) |field_ptr, i| { | |
| 892 | if (field_ptr != 0) continue; | |
| 844 | 893 | |
| 845 | log.warn("TODO implement zirValidateStructInitPtr (compile errors for missing/dupe fields)", .{}); | |
| 894 | const field_name = struct_obj.fields.entries.items[i].key; | |
| 895 | const template = "mising struct field: {s}"; | |
| 896 | const args = .{field_name}; | |
| 897 | if (root_msg) |msg| { | |
| 898 | try mod.errNote(&block.base, struct_init_src, msg, template, args); | |
| 899 | } else { | |
| 900 | root_msg = try mod.errMsg(&block.base, struct_init_src, template, args); | |
| 901 | } | |
| 902 | } | |
| 903 | if (root_msg) |msg| { | |
| 904 | const fqn = try struct_obj.getFullyQualifiedName(gpa); | |
| 905 | defer gpa.free(fqn); | |
| 906 | try mod.errNoteNonLazy( | |
| 907 | struct_obj.srcLoc(), | |
| 908 | msg, | |
| 909 | "struct '{s}' declared here", | |
| 910 | .{fqn}, | |
| 911 | ); | |
| 912 | return mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 913 | } | |
| 914 | } | |
| 915 | ||
| 916 | fn failWithBadFieldAccess( | |
| 917 | sema: *Sema, | |
| 918 | block: *Scope.Block, | |
| 919 | struct_obj: *Module.Struct, | |
| 920 | field_src: LazySrcLoc, | |
| 921 | field_name: []const u8, | |
| 922 | ) InnerError { | |
| 923 | const mod = sema.mod; | |
| 924 | const gpa = sema.gpa; | |
| 925 | ||
| 926 | const fqn = try struct_obj.getFullyQualifiedName(gpa); | |
| 927 | defer gpa.free(fqn); | |
| 928 | ||
| 929 | const msg = msg: { | |
| 930 | const msg = try mod.errMsg( | |
| 931 | &block.base, | |
| 932 | field_src, | |
| 933 | "no field named '{s}' in struct '{s}'", | |
| 934 | .{ field_name, fqn }, | |
| 935 | ); | |
| 936 | errdefer msg.destroy(gpa); | |
| 937 | try mod.errNoteNonLazy(struct_obj.srcLoc(), msg, "struct declared here", .{}); | |
| 938 | break :msg msg; | |
| 939 | }; | |
| 940 | return mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 846 | 941 | } |
| 847 | 942 | |
| 848 | 943 | fn zirStoreToBlockPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { |
| ... | ... | @@ -981,6 +1076,31 @@ fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*In |
| 981 | 1076 | return sema.mod.constIntUnsigned(sema.arena, .unneeded, Type.initTag(.comptime_int), int); |
| 982 | 1077 | } |
| 983 | 1078 | |
| 1079 | fn zirFloat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | |
| 1080 | const arena = sema.arena; | |
| 1081 | const inst_data = sema.code.instructions.items(.data)[inst].float; | |
| 1082 | const src = inst_data.src(); | |
| 1083 | const number = inst_data.number; | |
| 1084 | ||
| 1085 | return sema.mod.constInst(arena, src, .{ | |
| 1086 | .ty = Type.initTag(.comptime_float), | |
| 1087 | .val = try Value.Tag.float_32.create(arena, number), | |
| 1088 | }); | |
| 1089 | } | |
| 1090 | ||
| 1091 | fn zirFloat128(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | |
| 1092 | const arena = sema.arena; | |
| 1093 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 1094 | const extra = sema.code.extraData(zir.Inst.Float128, inst_data.payload_index).data; | |
| 1095 | const src = inst_data.src(); | |
| 1096 | const number = extra.get(); | |
| 1097 | ||
| 1098 | return sema.mod.constInst(arena, src, .{ | |
| 1099 | .ty = Type.initTag(.comptime_float), | |
| 1100 | .val = try Value.Tag.float_128.create(arena, number), | |
| 1101 | }); | |
| 1102 | } | |
| 1103 | ||
| 984 | 1104 | fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index { |
| 985 | 1105 | const tracy = trace(@src()); |
| 986 | 1106 | defer tracy.end(); |
| ... | ... | @@ -1222,6 +1342,28 @@ fn analyzeBlockBody( |
| 1222 | 1342 | return &merges.block_inst.base; |
| 1223 | 1343 | } |
| 1224 | 1344 | |
| 1345 | fn zirExport(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { | |
| 1346 | const tracy = trace(@src()); | |
| 1347 | defer tracy.end(); | |
| 1348 | ||
| 1349 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 1350 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | |
| 1351 | const src = inst_data.src(); | |
| 1352 | const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 1353 | const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 1354 | ||
| 1355 | // TODO (see corresponding TODO in AstGen) this is supposed to be a `decl_ref` | |
| 1356 | // instruction, which could reference any decl, which is then supposed to get | |
| 1357 | // exported, regardless of whether or not it is a function. | |
| 1358 | const target_fn = try sema.resolveInstConst(block, lhs_src, extra.lhs); | |
| 1359 | // TODO (see corresponding TODO in AstGen) this is supposed to be | |
| 1360 | // `std.builtin.ExportOptions`, not a string. | |
| 1361 | const export_name = try sema.resolveConstString(block, rhs_src, extra.rhs); | |
| 1362 | ||
| 1363 | const actual_fn = target_fn.val.castTag(.function).?.data; | |
| 1364 | try sema.mod.analyzeExport(&block.base, src, export_name, actual_fn.owner_decl); | |
| 1365 | } | |
| 1366 | ||
| 1225 | 1367 | fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!void { |
| 1226 | 1368 | const tracy = trace(@src()); |
| 1227 | 1369 | defer tracy.end(); |
| ... | ... | @@ -1291,22 +1433,16 @@ fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerE |
| 1291 | 1433 | } |
| 1292 | 1434 | |
| 1293 | 1435 | fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { |
| 1294 | const tracy = trace(@src()); | |
| 1295 | defer tracy.end(); | |
| 1296 | ||
| 1297 | 1436 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 1298 | 1437 | const src = inst_data.src(); |
| 1299 | const decl = sema.code.decls[inst_data.payload_index]; | |
| 1438 | const decl = sema.owner_decl.dependencies.entries.items[inst_data.payload_index].key; | |
| 1300 | 1439 | return sema.analyzeDeclRef(block, src, decl); |
| 1301 | 1440 | } |
| 1302 | 1441 | |
| 1303 | 1442 | fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { |
| 1304 | const tracy = trace(@src()); | |
| 1305 | defer tracy.end(); | |
| 1306 | ||
| 1307 | 1443 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 1308 | 1444 | const src = inst_data.src(); |
| 1309 | const decl = sema.code.decls[inst_data.payload_index]; | |
| 1445 | const decl = sema.owner_decl.dependencies.entries.items[inst_data.payload_index].key; | |
| 1310 | 1446 | return sema.analyzeDeclVal(block, src, decl); |
| 1311 | 1447 | } |
| 1312 | 1448 | |
| ... | ... | @@ -1763,6 +1899,143 @@ fn zirEnumLiteralSmall(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) I |
| 1763 | 1899 | }); |
| 1764 | 1900 | } |
| 1765 | 1901 | |
| 1902 | fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | |
| 1903 | const mod = sema.mod; | |
| 1904 | const arena = sema.arena; | |
| 1905 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | |
| 1906 | const src = inst_data.src(); | |
| 1907 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 1908 | const operand = try sema.resolveInst(inst_data.operand); | |
| 1909 | ||
| 1910 | const enum_tag: *Inst = switch (operand.ty.zigTypeTag()) { | |
| 1911 | .Enum => operand, | |
| 1912 | .Union => { | |
| 1913 | //if (!operand.ty.unionHasTag()) { | |
| 1914 | // return mod.fail( | |
| 1915 | // &block.base, | |
| 1916 | // operand_src, | |
| 1917 | // "untagged union '{}' cannot be converted to integer", | |
| 1918 | // .{dest_ty_src}, | |
| 1919 | // ); | |
| 1920 | //} | |
| 1921 | return mod.fail(&block.base, operand_src, "TODO zirEnumToInt for tagged unions", .{}); | |
| 1922 | }, | |
| 1923 | else => { | |
| 1924 | return mod.fail(&block.base, operand_src, "expected enum or tagged union, found {}", .{ | |
| 1925 | operand.ty, | |
| 1926 | }); | |
| 1927 | }, | |
| 1928 | }; | |
| 1929 | ||
| 1930 | var int_tag_type_buffer: Type.Payload.Bits = undefined; | |
| 1931 | const int_tag_ty = try enum_tag.ty.intTagType(&int_tag_type_buffer).copy(arena); | |
| 1932 | ||
| 1933 | if (enum_tag.ty.onePossibleValue()) |opv| { | |
| 1934 | return mod.constInst(arena, src, .{ | |
| 1935 | .ty = int_tag_ty, | |
| 1936 | .val = opv, | |
| 1937 | }); | |
| 1938 | } | |
| 1939 | ||
| 1940 | if (enum_tag.value()) |enum_tag_val| { | |
| 1941 | if (enum_tag_val.castTag(.enum_field_index)) |enum_field_payload| { | |
| 1942 | const field_index = enum_field_payload.data; | |
| 1943 | switch (enum_tag.ty.tag()) { | |
| 1944 | .enum_full => { | |
| 1945 | const enum_full = enum_tag.ty.castTag(.enum_full).?.data; | |
| 1946 | if (enum_full.values.count() != 0) { | |
| 1947 | const val = enum_full.values.entries.items[field_index].key; | |
| 1948 | return mod.constInst(arena, src, .{ | |
| 1949 | .ty = int_tag_ty, | |
| 1950 | .val = val, | |
| 1951 | }); | |
| 1952 | } else { | |
| 1953 | // Field index and integer values are the same. | |
| 1954 | const val = try Value.Tag.int_u64.create(arena, field_index); | |
| 1955 | return mod.constInst(arena, src, .{ | |
| 1956 | .ty = int_tag_ty, | |
| 1957 | .val = val, | |
| 1958 | }); | |
| 1959 | } | |
| 1960 | }, | |
| 1961 | .enum_simple => { | |
| 1962 | // Field index and integer values are the same. | |
| 1963 | const val = try Value.Tag.int_u64.create(arena, field_index); | |
| 1964 | return mod.constInst(arena, src, .{ | |
| 1965 | .ty = int_tag_ty, | |
| 1966 | .val = val, | |
| 1967 | }); | |
| 1968 | }, | |
| 1969 | else => unreachable, | |
| 1970 | } | |
| 1971 | } else { | |
| 1972 | // Assume it is already an integer and return it directly. | |
| 1973 | return mod.constInst(arena, src, .{ | |
| 1974 | .ty = int_tag_ty, | |
| 1975 | .val = enum_tag_val, | |
| 1976 | }); | |
| 1977 | } | |
| 1978 | } | |
| 1979 | ||
| 1980 | try sema.requireRuntimeBlock(block, src); | |
| 1981 | return block.addUnOp(src, int_tag_ty, .bitcast, enum_tag); | |
| 1982 | } | |
| 1983 | ||
| 1984 | fn zirIntToEnum(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | |
| 1985 | const mod = sema.mod; | |
| 1986 | const target = mod.getTarget(); | |
| 1987 | const arena = sema.arena; | |
| 1988 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 1989 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | |
| 1990 | const src = inst_data.src(); | |
| 1991 | const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 1992 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 1993 | const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); | |
| 1994 | const operand = try sema.resolveInst(extra.rhs); | |
| 1995 | ||
| 1996 | if (dest_ty.zigTypeTag() != .Enum) { | |
| 1997 | return mod.fail(&block.base, dest_ty_src, "expected enum, found {}", .{dest_ty}); | |
| 1998 | } | |
| 1999 | ||
| 2000 | if (dest_ty.isNonexhaustiveEnum()) { | |
| 2001 | if (operand.value()) |int_val| { | |
| 2002 | return mod.constInst(arena, src, .{ | |
| 2003 | .ty = dest_ty, | |
| 2004 | .val = int_val, | |
| 2005 | }); | |
| 2006 | } | |
| 2007 | } | |
| 2008 | ||
| 2009 | if (try sema.resolveDefinedValue(block, operand_src, operand)) |int_val| { | |
| 2010 | if (!dest_ty.enumHasInt(int_val, target)) { | |
| 2011 | const msg = msg: { | |
| 2012 | const msg = try mod.errMsg( | |
| 2013 | &block.base, | |
| 2014 | src, | |
| 2015 | "enum '{}' has no tag with value {}", | |
| 2016 | .{ dest_ty, int_val }, | |
| 2017 | ); | |
| 2018 | errdefer msg.destroy(sema.gpa); | |
| 2019 | try mod.errNoteNonLazy( | |
| 2020 | dest_ty.declSrcLoc(), | |
| 2021 | msg, | |
| 2022 | "enum declared here", | |
| 2023 | .{}, | |
| 2024 | ); | |
| 2025 | break :msg msg; | |
| 2026 | }; | |
| 2027 | return mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 2028 | } | |
| 2029 | return mod.constInst(arena, src, .{ | |
| 2030 | .ty = dest_ty, | |
| 2031 | .val = int_val, | |
| 2032 | }); | |
| 2033 | } | |
| 2034 | ||
| 2035 | try sema.requireRuntimeBlock(block, src); | |
| 2036 | return block.addUnOp(src, dest_ty, .bitcast, operand); | |
| 2037 | } | |
| 2038 | ||
| 1766 | 2039 | /// Pointer in, pointer out. |
| 1767 | 2040 | fn zirOptionalPayloadPtr( |
| 1768 | 2041 | sema: *Sema, |
| ... | ... | @@ -2139,7 +2412,10 @@ fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerErro |
| 2139 | 2412 | const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data; |
| 2140 | 2413 | const field_name = sema.code.nullTerminatedString(extra.field_name_start); |
| 2141 | 2414 | const object = try sema.resolveInst(extra.lhs); |
| 2142 | const object_ptr = try sema.analyzeRef(block, src, object); | |
| 2415 | const object_ptr = if (object.ty.zigTypeTag() == .Pointer) | |
| 2416 | object | |
| 2417 | else | |
| 2418 | try sema.analyzeRef(block, src, object); | |
| 2143 | 2419 | const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); |
| 2144 | 2420 | return sema.analyzeLoad(block, src, result_ptr, result_ptr.src); |
| 2145 | 2421 | } |
| ... | ... | @@ -2292,7 +2568,10 @@ fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError |
| 2292 | 2568 | |
| 2293 | 2569 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; |
| 2294 | 2570 | const array = try sema.resolveInst(bin_inst.lhs); |
| 2295 | const array_ptr = try sema.analyzeRef(block, sema.src, array); | |
| 2571 | const array_ptr = if (array.ty.zigTypeTag() == .Pointer) | |
| 2572 | array | |
| 2573 | else | |
| 2574 | try sema.analyzeRef(block, sema.src, array); | |
| 2296 | 2575 | const elem_index = try sema.resolveInst(bin_inst.rhs); |
| 2297 | 2576 | const result_ptr = try sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src); |
| 2298 | 2577 | return sema.analyzeLoad(block, sema.src, result_ptr, sema.src); |
| ... | ... | @@ -2307,7 +2586,10 @@ fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerE |
| 2307 | 2586 | const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; |
| 2308 | 2587 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; |
| 2309 | 2588 | const array = try sema.resolveInst(extra.lhs); |
| 2310 | const array_ptr = try sema.analyzeRef(block, src, array); | |
| 2589 | const array_ptr = if (array.ty.zigTypeTag() == .Pointer) | |
| 2590 | array | |
| 2591 | else | |
| 2592 | try sema.analyzeRef(block, src, array); | |
| 2311 | 2593 | const elem_index = try sema.resolveInst(extra.rhs); |
| 2312 | 2594 | const result_ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src); |
| 2313 | 2595 | return sema.analyzeLoad(block, src, result_ptr, src); |
| ... | ... | @@ -2492,6 +2774,8 @@ fn analyzeSwitch( |
| 2492 | 2774 | src_node_offset: i32, |
| 2493 | 2775 | ) InnerError!*Inst { |
| 2494 | 2776 | const gpa = sema.gpa; |
| 2777 | const mod = sema.mod; | |
| 2778 | ||
| 2495 | 2779 | const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) { |
| 2496 | 2780 | .none => .{ .body = &.{}, .end = extra_end }, |
| 2497 | 2781 | .under, .@"else" => blk: { |
| ... | ... | @@ -2509,16 +2793,16 @@ fn analyzeSwitch( |
| 2509 | 2793 | const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset }; |
| 2510 | 2794 | |
| 2511 | 2795 | // Validate usage of '_' prongs. |
| 2512 | if (special_prong == .under and !operand.ty.isExhaustiveEnum()) { | |
| 2796 | if (special_prong == .under and !operand.ty.isNonexhaustiveEnum()) { | |
| 2513 | 2797 | const msg = msg: { |
| 2514 | const msg = try sema.mod.errMsg( | |
| 2798 | const msg = try mod.errMsg( | |
| 2515 | 2799 | &block.base, |
| 2516 | 2800 | src, |
| 2517 | 2801 | "'_' prong only allowed when switching on non-exhaustive enums", |
| 2518 | 2802 | .{}, |
| 2519 | 2803 | ); |
| 2520 | 2804 | errdefer msg.destroy(gpa); |
| 2521 | try sema.mod.errNote( | |
| 2805 | try mod.errNote( | |
| 2522 | 2806 | &block.base, |
| 2523 | 2807 | special_prong_src, |
| 2524 | 2808 | msg, |
| ... | ... | @@ -2527,14 +2811,123 @@ fn analyzeSwitch( |
| 2527 | 2811 | ); |
| 2528 | 2812 | break :msg msg; |
| 2529 | 2813 | }; |
| 2530 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 2814 | return mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 2531 | 2815 | } |
| 2532 | 2816 | |
| 2533 | 2817 | // Validate for duplicate items, missing else prong, and invalid range. |
| 2534 | 2818 | switch (operand.ty.zigTypeTag()) { |
| 2535 | .Enum => return sema.mod.fail(&block.base, src, "TODO validate switch .Enum", .{}), | |
| 2536 | .ErrorSet => return sema.mod.fail(&block.base, src, "TODO validate switch .ErrorSet", .{}), | |
| 2537 | .Union => return sema.mod.fail(&block.base, src, "TODO validate switch .Union", .{}), | |
| 2819 | .Enum => { | |
| 2820 | var seen_fields = try gpa.alloc(?AstGen.SwitchProngSrc, operand.ty.enumFieldCount()); | |
| 2821 | defer gpa.free(seen_fields); | |
| 2822 | ||
| 2823 | mem.set(?AstGen.SwitchProngSrc, seen_fields, null); | |
| 2824 | ||
| 2825 | var extra_index: usize = special.end; | |
| 2826 | { | |
| 2827 | var scalar_i: u32 = 0; | |
| 2828 | while (scalar_i < scalar_cases_len) : (scalar_i += 1) { | |
| 2829 | const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]); | |
| 2830 | extra_index += 1; | |
| 2831 | const body_len = sema.code.extra[extra_index]; | |
| 2832 | extra_index += 1; | |
| 2833 | const body = sema.code.extra[extra_index..][0..body_len]; | |
| 2834 | extra_index += body_len; | |
| 2835 | ||
| 2836 | try sema.validateSwitchItemEnum( | |
| 2837 | block, | |
| 2838 | seen_fields, | |
| 2839 | item_ref, | |
| 2840 | src_node_offset, | |
| 2841 | .{ .scalar = scalar_i }, | |
| 2842 | ); | |
| 2843 | } | |
| 2844 | } | |
| 2845 | { | |
| 2846 | var multi_i: u32 = 0; | |
| 2847 | while (multi_i < multi_cases_len) : (multi_i += 1) { | |
| 2848 | const items_len = sema.code.extra[extra_index]; | |
| 2849 | extra_index += 1; | |
| 2850 | const ranges_len = sema.code.extra[extra_index]; | |
| 2851 | extra_index += 1; | |
| 2852 | const body_len = sema.code.extra[extra_index]; | |
| 2853 | extra_index += 1; | |
| 2854 | const items = sema.code.refSlice(extra_index, items_len); | |
| 2855 | extra_index += items_len + body_len; | |
| 2856 | ||
| 2857 | for (items) |item_ref, item_i| { | |
| 2858 | try sema.validateSwitchItemEnum( | |
| 2859 | block, | |
| 2860 | seen_fields, | |
| 2861 | item_ref, | |
| 2862 | src_node_offset, | |
| 2863 | .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } }, | |
| 2864 | ); | |
| 2865 | } | |
| 2866 | ||
| 2867 | try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset); | |
| 2868 | } | |
| 2869 | } | |
| 2870 | const all_tags_handled = for (seen_fields) |seen_src| { | |
| 2871 | if (seen_src == null) break false; | |
| 2872 | } else true; | |
| 2873 | ||
| 2874 | switch (special_prong) { | |
| 2875 | .none => { | |
| 2876 | if (!all_tags_handled) { | |
| 2877 | const msg = msg: { | |
| 2878 | const msg = try mod.errMsg( | |
| 2879 | &block.base, | |
| 2880 | src, | |
| 2881 | "switch must handle all possibilities", | |
| 2882 | .{}, | |
| 2883 | ); | |
| 2884 | errdefer msg.destroy(sema.gpa); | |
| 2885 | for (seen_fields) |seen_src, i| { | |
| 2886 | if (seen_src != null) continue; | |
| 2887 | ||
| 2888 | const field_name = operand.ty.enumFieldName(i); | |
| 2889 | ||
| 2890 | // TODO have this point to the tag decl instead of here | |
| 2891 | try mod.errNote( | |
| 2892 | &block.base, | |
| 2893 | src, | |
| 2894 | msg, | |
| 2895 | "unhandled enumeration value: '{s}'", | |
| 2896 | .{field_name}, | |
| 2897 | ); | |
| 2898 | } | |
| 2899 | try mod.errNoteNonLazy( | |
| 2900 | operand.ty.declSrcLoc(), | |
| 2901 | msg, | |
| 2902 | "enum '{}' declared here", | |
| 2903 | .{operand.ty}, | |
| 2904 | ); | |
| 2905 | break :msg msg; | |
| 2906 | }; | |
| 2907 | return mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 2908 | } | |
| 2909 | }, | |
| 2910 | .under => { | |
| 2911 | if (all_tags_handled) return mod.fail( | |
| 2912 | &block.base, | |
| 2913 | special_prong_src, | |
| 2914 | "unreachable '_' prong; all cases already handled", | |
| 2915 | .{}, | |
| 2916 | ); | |
| 2917 | }, | |
| 2918 | .@"else" => { | |
| 2919 | if (all_tags_handled) return mod.fail( | |
| 2920 | &block.base, | |
| 2921 | special_prong_src, | |
| 2922 | "unreachable else prong; all cases already handled", | |
| 2923 | .{}, | |
| 2924 | ); | |
| 2925 | }, | |
| 2926 | } | |
| 2927 | }, | |
| 2928 | ||
| 2929 | .ErrorSet => return mod.fail(&block.base, src, "TODO validate switch .ErrorSet", .{}), | |
| 2930 | .Union => return mod.fail(&block.base, src, "TODO validate switch .Union", .{}), | |
| 2538 | 2931 | .Int, .ComptimeInt => { |
| 2539 | 2932 | var range_set = RangeSet.init(gpa); |
| 2540 | 2933 | defer range_set.deinit(); |
| ... | ... | @@ -2607,11 +3000,11 @@ fn analyzeSwitch( |
| 2607 | 3000 | var arena = std.heap.ArenaAllocator.init(gpa); |
| 2608 | 3001 | defer arena.deinit(); |
| 2609 | 3002 | |
| 2610 | const min_int = try operand.ty.minInt(&arena, sema.mod.getTarget()); | |
| 2611 | const max_int = try operand.ty.maxInt(&arena, sema.mod.getTarget()); | |
| 3003 | const min_int = try operand.ty.minInt(&arena, mod.getTarget()); | |
| 3004 | const max_int = try operand.ty.maxInt(&arena, mod.getTarget()); | |
| 2612 | 3005 | if (try range_set.spans(min_int, max_int)) { |
| 2613 | 3006 | if (special_prong == .@"else") { |
| 2614 | return sema.mod.fail( | |
| 3007 | return mod.fail( | |
| 2615 | 3008 | &block.base, |
| 2616 | 3009 | special_prong_src, |
| 2617 | 3010 | "unreachable else prong; all cases already handled", |
| ... | ... | @@ -2622,7 +3015,7 @@ fn analyzeSwitch( |
| 2622 | 3015 | } |
| 2623 | 3016 | } |
| 2624 | 3017 | if (special_prong != .@"else") { |
| 2625 | return sema.mod.fail( | |
| 3018 | return mod.fail( | |
| 2626 | 3019 | &block.base, |
| 2627 | 3020 | src, |
| 2628 | 3021 | "switch must handle all possibilities", |
| ... | ... | @@ -2685,7 +3078,7 @@ fn analyzeSwitch( |
| 2685 | 3078 | switch (special_prong) { |
| 2686 | 3079 | .@"else" => { |
| 2687 | 3080 | if (true_count + false_count == 2) { |
| 2688 | return sema.mod.fail( | |
| 3081 | return mod.fail( | |
| 2689 | 3082 | &block.base, |
| 2690 | 3083 | src, |
| 2691 | 3084 | "unreachable else prong; all cases already handled", |
| ... | ... | @@ -2695,7 +3088,7 @@ fn analyzeSwitch( |
| 2695 | 3088 | }, |
| 2696 | 3089 | .under, .none => { |
| 2697 | 3090 | if (true_count + false_count < 2) { |
| 2698 | return sema.mod.fail( | |
| 3091 | return mod.fail( | |
| 2699 | 3092 | &block.base, |
| 2700 | 3093 | src, |
| 2701 | 3094 | "switch must handle all possibilities", |
| ... | ... | @@ -2707,7 +3100,7 @@ fn analyzeSwitch( |
| 2707 | 3100 | }, |
| 2708 | 3101 | .EnumLiteral, .Void, .Fn, .Pointer, .Type => { |
| 2709 | 3102 | if (special_prong != .@"else") { |
| 2710 | return sema.mod.fail( | |
| 3103 | return mod.fail( | |
| 2711 | 3104 | &block.base, |
| 2712 | 3105 | src, |
| 2713 | 3106 | "else prong required when switching on type '{}'", |
| ... | ... | @@ -2779,7 +3172,7 @@ fn analyzeSwitch( |
| 2779 | 3172 | .AnyFrame, |
| 2780 | 3173 | .ComptimeFloat, |
| 2781 | 3174 | .Float, |
| 2782 | => return sema.mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{ | |
| 3175 | => return mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{ | |
| 2783 | 3176 | operand.ty, |
| 2784 | 3177 | }), |
| 2785 | 3178 | } |
| ... | ... | @@ -3054,7 +3447,7 @@ fn resolveSwitchItemVal( |
| 3054 | 3447 | switch_node_offset: i32, |
| 3055 | 3448 | switch_prong_src: AstGen.SwitchProngSrc, |
| 3056 | 3449 | range_expand: AstGen.SwitchProngSrc.RangeExpand, |
| 3057 | ) InnerError!Value { | |
| 3450 | ) InnerError!TypedValue { | |
| 3058 | 3451 | const item = try sema.resolveInst(item_ref); |
| 3059 | 3452 | // We have to avoid the other helper functions here because we cannot construct a LazySrcLoc |
| 3060 | 3453 | // because we only have the switch AST node. Only if we know for sure we need to report |
| ... | ... | @@ -3064,7 +3457,7 @@ fn resolveSwitchItemVal( |
| 3064 | 3457 | const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand); |
| 3065 | 3458 | return sema.failWithUseOfUndef(block, src); |
| 3066 | 3459 | } |
| 3067 | return val; | |
| 3460 | return TypedValue{ .ty = item.ty, .val = val }; | |
| 3068 | 3461 | } |
| 3069 | 3462 | const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand); |
| 3070 | 3463 | return sema.failWithNeededComptime(block, src); |
| ... | ... | @@ -3079,8 +3472,8 @@ fn validateSwitchRange( |
| 3079 | 3472 | src_node_offset: i32, |
| 3080 | 3473 | switch_prong_src: AstGen.SwitchProngSrc, |
| 3081 | 3474 | ) InnerError!void { |
| 3082 | const first_val = try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first); | |
| 3083 | const last_val = try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last); | |
| 3475 | const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val; | |
| 3476 | const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val; | |
| 3084 | 3477 | const maybe_prev_src = try range_set.add(first_val, last_val, switch_prong_src); |
| 3085 | 3478 | return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); |
| 3086 | 3479 | } |
| ... | ... | @@ -3093,11 +3486,46 @@ fn validateSwitchItem( |
| 3093 | 3486 | src_node_offset: i32, |
| 3094 | 3487 | switch_prong_src: AstGen.SwitchProngSrc, |
| 3095 | 3488 | ) InnerError!void { |
| 3096 | const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); | |
| 3489 | const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val; | |
| 3097 | 3490 | const maybe_prev_src = try range_set.add(item_val, item_val, switch_prong_src); |
| 3098 | 3491 | return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); |
| 3099 | 3492 | } |
| 3100 | 3493 | |
| 3494 | fn validateSwitchItemEnum( | |
| 3495 | sema: *Sema, | |
| 3496 | block: *Scope.Block, | |
| 3497 | seen_fields: []?AstGen.SwitchProngSrc, | |
| 3498 | item_ref: zir.Inst.Ref, | |
| 3499 | src_node_offset: i32, | |
| 3500 | switch_prong_src: AstGen.SwitchProngSrc, | |
| 3501 | ) InnerError!void { | |
| 3502 | const mod = sema.mod; | |
| 3503 | const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); | |
| 3504 | const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val) orelse { | |
| 3505 | const msg = msg: { | |
| 3506 | const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none); | |
| 3507 | const msg = try mod.errMsg( | |
| 3508 | &block.base, | |
| 3509 | src, | |
| 3510 | "enum '{}' has no tag with value '{}'", | |
| 3511 | .{ item_tv.ty, item_tv.val }, | |
| 3512 | ); | |
| 3513 | errdefer msg.destroy(sema.gpa); | |
| 3514 | try mod.errNoteNonLazy( | |
| 3515 | item_tv.ty.declSrcLoc(), | |
| 3516 | msg, | |
| 3517 | "enum declared here", | |
| 3518 | .{}, | |
| 3519 | ); | |
| 3520 | break :msg msg; | |
| 3521 | }; | |
| 3522 | return mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 3523 | }; | |
| 3524 | const maybe_prev_src = seen_fields[field_index]; | |
| 3525 | seen_fields[field_index] = switch_prong_src; | |
| 3526 | return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset); | |
| 3527 | } | |
| 3528 | ||
| 3101 | 3529 | fn validateSwitchDupe( |
| 3102 | 3530 | sema: *Sema, |
| 3103 | 3531 | block: *Scope.Block, |
| ... | ... | @@ -3106,17 +3534,18 @@ fn validateSwitchDupe( |
| 3106 | 3534 | src_node_offset: i32, |
| 3107 | 3535 | ) InnerError!void { |
| 3108 | 3536 | const prev_prong_src = maybe_prev_src orelse return; |
| 3537 | const mod = sema.mod; | |
| 3109 | 3538 | const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none); |
| 3110 | 3539 | const prev_src = prev_prong_src.resolve(block.src_decl, src_node_offset, .none); |
| 3111 | 3540 | const msg = msg: { |
| 3112 | const msg = try sema.mod.errMsg( | |
| 3541 | const msg = try mod.errMsg( | |
| 3113 | 3542 | &block.base, |
| 3114 | 3543 | src, |
| 3115 | 3544 | "duplicate switch value", |
| 3116 | 3545 | .{}, |
| 3117 | 3546 | ); |
| 3118 | 3547 | errdefer msg.destroy(sema.gpa); |
| 3119 | try sema.mod.errNote( | |
| 3548 | try mod.errNote( | |
| 3120 | 3549 | &block.base, |
| 3121 | 3550 | prev_src, |
| 3122 | 3551 | msg, |
| ... | ... | @@ -3125,7 +3554,7 @@ fn validateSwitchDupe( |
| 3125 | 3554 | ); |
| 3126 | 3555 | break :msg msg; |
| 3127 | 3556 | }; |
| 3128 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 3557 | return mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 3129 | 3558 | } |
| 3130 | 3559 | |
| 3131 | 3560 | fn validateSwitchItemBool( |
| ... | ... | @@ -3137,7 +3566,7 @@ fn validateSwitchItemBool( |
| 3137 | 3566 | src_node_offset: i32, |
| 3138 | 3567 | switch_prong_src: AstGen.SwitchProngSrc, |
| 3139 | 3568 | ) InnerError!void { |
| 3140 | const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); | |
| 3569 | const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val; | |
| 3141 | 3570 | if (item_val.toBool()) { |
| 3142 | 3571 | true_count.* += 1; |
| 3143 | 3572 | } else { |
| ... | ... | @@ -3159,7 +3588,7 @@ fn validateSwitchItemSparse( |
| 3159 | 3588 | src_node_offset: i32, |
| 3160 | 3589 | switch_prong_src: AstGen.SwitchProngSrc, |
| 3161 | 3590 | ) InnerError!void { |
| 3162 | const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none); | |
| 3591 | const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val; | |
| 3163 | 3592 | const entry = (try seen_values.fetchPut(item_val, switch_prong_src)) orelse return; |
| 3164 | 3593 | return sema.validateSwitchDupe(block, entry.value, switch_prong_src, src_node_offset); |
| 3165 | 3594 | } |
| ... | ... | @@ -3197,6 +3626,34 @@ fn validateSwitchNoRange( |
| 3197 | 3626 | return sema.mod.failWithOwnedErrorMsg(&block.base, msg); |
| 3198 | 3627 | } |
| 3199 | 3628 | |
| 3629 | fn zirHasDecl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | |
| 3630 | const tracy = trace(@src()); | |
| 3631 | defer tracy.end(); | |
| 3632 | ||
| 3633 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 3634 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | |
| 3635 | const src = inst_data.src(); | |
| 3636 | const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 3637 | const rhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 3638 | const container_type = try sema.resolveType(block, lhs_src, extra.lhs); | |
| 3639 | const decl_name = try sema.resolveConstString(block, rhs_src, extra.rhs); | |
| 3640 | const mod = sema.mod; | |
| 3641 | const arena = sema.arena; | |
| 3642 | ||
| 3643 | const container_scope = container_type.getContainerScope() orelse return mod.fail( | |
| 3644 | &block.base, | |
| 3645 | lhs_src, | |
| 3646 | "expected struct, enum, union, or opaque, found '{}'", | |
| 3647 | .{container_type}, | |
| 3648 | ); | |
| 3649 | if (mod.lookupDeclName(&container_scope.base, decl_name)) |decl| { | |
| 3650 | // TODO if !decl.is_pub and inDifferentFiles() return false | |
| 3651 | return mod.constBool(arena, src, true); | |
| 3652 | } else { | |
| 3653 | return mod.constBool(arena, src, false); | |
| 3654 | } | |
| 3655 | } | |
| 3656 | ||
| 3200 | 3657 | fn zirImport(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { |
| 3201 | 3658 | const tracy = trace(@src()); |
| 3202 | 3659 | defer tracy.end(); |
| ... | ... | @@ -3448,6 +3905,7 @@ fn analyzeArithmetic( |
| 3448 | 3905 | .subwrap => .subwrap, |
| 3449 | 3906 | .mul => .mul, |
| 3450 | 3907 | .mulwrap => .mulwrap, |
| 3908 | .div => .div, | |
| 3451 | 3909 | else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(zir_tag)}), |
| 3452 | 3910 | }; |
| 3453 | 3911 | |
| ... | ... | @@ -3539,9 +3997,13 @@ fn zirCmp( |
| 3539 | 3997 | const tracy = trace(@src()); |
| 3540 | 3998 | defer tracy.end(); |
| 3541 | 3999 | |
| 4000 | const mod = sema.mod; | |
| 4001 | ||
| 3542 | 4002 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 3543 | 4003 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; |
| 3544 | 4004 | const src: LazySrcLoc = inst_data.src(); |
| 4005 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; | |
| 4006 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; | |
| 3545 | 4007 | const lhs = try sema.resolveInst(extra.lhs); |
| 3546 | 4008 | const rhs = try sema.resolveInst(extra.rhs); |
| 3547 | 4009 | |
| ... | ... | @@ -3553,7 +4015,7 @@ fn zirCmp( |
| 3553 | 4015 | const rhs_ty_tag = rhs.ty.zigTypeTag(); |
| 3554 | 4016 | if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) { |
| 3555 | 4017 | // null == null, null != null |
| 3556 | return sema.mod.constBool(sema.arena, src, op == .eq); | |
| 4018 | return mod.constBool(sema.arena, src, op == .eq); | |
| 3557 | 4019 | } else if (is_equality_cmp and |
| 3558 | 4020 | ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or |
| 3559 | 4021 | rhs_ty_tag == .Null and lhs_ty_tag == .Optional)) |
| ... | ... | @@ -3564,23 +4026,23 @@ fn zirCmp( |
| 3564 | 4026 | } else if (is_equality_cmp and |
| 3565 | 4027 | ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr()))) |
| 3566 | 4028 | { |
| 3567 | return sema.mod.fail(&block.base, src, "TODO implement C pointer cmp", .{}); | |
| 4029 | return mod.fail(&block.base, src, "TODO implement C pointer cmp", .{}); | |
| 3568 | 4030 | } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { |
| 3569 | 4031 | const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty; |
| 3570 | return sema.mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type}); | |
| 4032 | return mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type}); | |
| 3571 | 4033 | } else if (is_equality_cmp and |
| 3572 | 4034 | ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or |
| 3573 | 4035 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) |
| 3574 | 4036 | { |
| 3575 | return sema.mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | |
| 4037 | return mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | |
| 3576 | 4038 | } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { |
| 3577 | 4039 | if (!is_equality_cmp) { |
| 3578 | return sema.mod.fail(&block.base, src, "{s} operator not allowed for errors", .{@tagName(op)}); | |
| 4040 | return mod.fail(&block.base, src, "{s} operator not allowed for errors", .{@tagName(op)}); | |
| 3579 | 4041 | } |
| 3580 | 4042 | if (rhs.value()) |rval| { |
| 3581 | 4043 | if (lhs.value()) |lval| { |
| 3582 | 4044 | // TODO optimisation oppurtunity: evaluate if std.mem.eql is faster with the names, or calling to Module.getErrorValue to get the values and then compare them is faster |
| 3583 | return sema.mod.constBool(sema.arena, src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq)); | |
| 4045 | return mod.constBool(sema.arena, src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq)); | |
| 3584 | 4046 | } |
| 3585 | 4047 | } |
| 3586 | 4048 | try sema.requireRuntimeBlock(block, src); |
| ... | ... | @@ -3592,11 +4054,36 @@ fn zirCmp( |
| 3592 | 4054 | return sema.cmpNumeric(block, src, lhs, rhs, op); |
| 3593 | 4055 | } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { |
| 3594 | 4056 | if (!is_equality_cmp) { |
| 3595 | return sema.mod.fail(&block.base, src, "{s} operator not allowed for types", .{@tagName(op)}); | |
| 4057 | return mod.fail(&block.base, src, "{s} operator not allowed for types", .{@tagName(op)}); | |
| 3596 | 4058 | } |
| 3597 | return sema.mod.constBool(sema.arena, src, lhs.value().?.eql(rhs.value().?) == (op == .eq)); | |
| 4059 | return mod.constBool(sema.arena, src, lhs.value().?.eql(rhs.value().?) == (op == .eq)); | |
| 3598 | 4060 | } |
| 3599 | return sema.mod.fail(&block.base, src, "TODO implement more cmp analysis", .{}); | |
| 4061 | ||
| 4062 | const instructions = &[_]*Inst{ lhs, rhs }; | |
| 4063 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions); | |
| 4064 | if (!resolved_type.isSelfComparable(is_equality_cmp)) { | |
| 4065 | return mod.fail(&block.base, src, "operator not allowed for type '{}'", .{resolved_type}); | |
| 4066 | } | |
| 4067 | ||
| 4068 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); | |
| 4069 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); | |
| 4070 | try sema.requireRuntimeBlock(block, src); // TODO try to do it at comptime | |
| 4071 | const bool_type = Type.initTag(.bool); // TODO handle vectors | |
| 4072 | const tag: Inst.Tag = switch (op) { | |
| 4073 | .lt => .cmp_lt, | |
| 4074 | .lte => .cmp_lte, | |
| 4075 | .eq => .cmp_eq, | |
| 4076 | .gte => .cmp_gte, | |
| 4077 | .gt => .cmp_gt, | |
| 4078 | .neq => .cmp_neq, | |
| 4079 | }; | |
| 4080 | return block.addBinOp(src, bool_type, tag, casted_lhs, casted_rhs); | |
| 4081 | } | |
| 4082 | ||
| 4083 | fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | |
| 4084 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | |
| 4085 | const src = inst_data.src(); | |
| 4086 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirTypeInfo", .{}); | |
| 3600 | 4087 | } |
| 3601 | 4088 | |
| 3602 | 4089 | fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { |
| ... | ... | @@ -3740,7 +4227,7 @@ fn zirBoolBr( |
| 3740 | 4227 | _ = try rhs_block.addBr(src, block_inst, rhs_result); |
| 3741 | 4228 | |
| 3742 | 4229 | const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) }; |
| 3743 | const tzir_else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, rhs_block.instructions.items) }; | |
| 4230 | const tzir_else_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, else_block.instructions.items) }; | |
| 3744 | 4231 | _ = try child_block.addCondBr(src, lhs, tzir_then_body, tzir_else_body); |
| 3745 | 4232 | |
| 3746 | 4233 | block_inst.body = .{ |
| ... | ... | @@ -4016,6 +4503,18 @@ fn zirStructInitEmpty(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) In |
| 4016 | 4503 | }); |
| 4017 | 4504 | } |
| 4018 | 4505 | |
| 4506 | fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | |
| 4507 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 4508 | const src = inst_data.src(); | |
| 4509 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirStructInit", .{}); | |
| 4510 | } | |
| 4511 | ||
| 4512 | fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | |
| 4513 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 4514 | const src = inst_data.src(); | |
| 4515 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirFieldType", .{}); | |
| 4516 | } | |
| 4517 | ||
| 4019 | 4518 | fn requireFunctionBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { |
| 4020 | 4519 | if (sema.func == null) { |
| 4021 | 4520 | return sema.mod.fail(&block.base, src, "instruction illegal outside function body", .{}); |
| ... | ... | @@ -4123,22 +4622,25 @@ fn namedFieldPtr( |
| 4123 | 4622 | field_name: []const u8, |
| 4124 | 4623 | field_name_src: LazySrcLoc, |
| 4125 | 4624 | ) InnerError!*Inst { |
| 4625 | const mod = sema.mod; | |
| 4626 | const arena = sema.arena; | |
| 4627 | ||
| 4126 | 4628 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { |
| 4127 | 4629 | .Pointer => object_ptr.ty.elemType(), |
| 4128 | else => return sema.mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}), | |
| 4630 | else => return mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}), | |
| 4129 | 4631 | }; |
| 4130 | 4632 | switch (elem_ty.zigTypeTag()) { |
| 4131 | 4633 | .Array => { |
| 4132 | 4634 | if (mem.eql(u8, field_name, "len")) { |
| 4133 | return sema.mod.constInst(sema.arena, src, .{ | |
| 4635 | return mod.constInst(arena, src, .{ | |
| 4134 | 4636 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), |
| 4135 | 4637 | .val = try Value.Tag.ref_val.create( |
| 4136 | sema.arena, | |
| 4137 | try Value.Tag.int_u64.create(sema.arena, elem_ty.arrayLen()), | |
| 4638 | arena, | |
| 4639 | try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()), | |
| 4138 | 4640 | ), |
| 4139 | 4641 | }); |
| 4140 | 4642 | } else { |
| 4141 | return sema.mod.fail( | |
| 4643 | return mod.fail( | |
| 4142 | 4644 | &block.base, |
| 4143 | 4645 | field_name_src, |
| 4144 | 4646 | "no member named '{s}' in '{}'", |
| ... | ... | @@ -4151,15 +4653,15 @@ fn namedFieldPtr( |
| 4151 | 4653 | switch (ptr_child.zigTypeTag()) { |
| 4152 | 4654 | .Array => { |
| 4153 | 4655 | if (mem.eql(u8, field_name, "len")) { |
| 4154 | return sema.mod.constInst(sema.arena, src, .{ | |
| 4656 | return mod.constInst(arena, src, .{ | |
| 4155 | 4657 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), |
| 4156 | 4658 | .val = try Value.Tag.ref_val.create( |
| 4157 | sema.arena, | |
| 4158 | try Value.Tag.int_u64.create(sema.arena, ptr_child.arrayLen()), | |
| 4659 | arena, | |
| 4660 | try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()), | |
| 4159 | 4661 | ), |
| 4160 | 4662 | }); |
| 4161 | 4663 | } else { |
| 4162 | return sema.mod.fail( | |
| 4664 | return mod.fail( | |
| 4163 | 4665 | &block.base, |
| 4164 | 4666 | field_name_src, |
| 4165 | 4667 | "no member named '{s}' in '{}'", |
| ... | ... | @@ -4174,7 +4676,7 @@ fn namedFieldPtr( |
| 4174 | 4676 | _ = try sema.resolveConstValue(block, object_ptr.src, object_ptr); |
| 4175 | 4677 | const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr.src); |
| 4176 | 4678 | const val = result.value().?; |
| 4177 | const child_type = try val.toType(sema.arena); | |
| 4679 | const child_type = try val.toType(arena); | |
| 4178 | 4680 | switch (child_type.zigTypeTag()) { |
| 4179 | 4681 | .ErrorSet => { |
| 4180 | 4682 | // TODO resolve inferred error sets |
| ... | ... | @@ -4188,42 +4690,92 @@ fn namedFieldPtr( |
| 4188 | 4690 | break :blk name; |
| 4189 | 4691 | } |
| 4190 | 4692 | } |
| 4191 | return sema.mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{ | |
| 4693 | return mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{ | |
| 4192 | 4694 | field_name, |
| 4193 | 4695 | child_type, |
| 4194 | 4696 | }); |
| 4195 | } else (try sema.mod.getErrorValue(field_name)).key; | |
| 4697 | } else (try mod.getErrorValue(field_name)).key; | |
| 4196 | 4698 | |
| 4197 | return sema.mod.constInst(sema.arena, src, .{ | |
| 4198 | .ty = try sema.mod.simplePtrType(sema.arena, child_type, false, .One), | |
| 4699 | return mod.constInst(arena, src, .{ | |
| 4700 | .ty = try mod.simplePtrType(arena, child_type, false, .One), | |
| 4199 | 4701 | .val = try Value.Tag.ref_val.create( |
| 4200 | sema.arena, | |
| 4201 | try Value.Tag.@"error".create(sema.arena, .{ | |
| 4702 | arena, | |
| 4703 | try Value.Tag.@"error".create(arena, .{ | |
| 4202 | 4704 | .name = name, |
| 4203 | 4705 | }), |
| 4204 | 4706 | ), |
| 4205 | 4707 | }); |
| 4206 | 4708 | }, |
| 4207 | .Struct => { | |
| 4208 | const container_scope = child_type.getContainerScope(); | |
| 4209 | if (sema.mod.lookupDeclName(&container_scope.base, field_name)) |decl| { | |
| 4210 | // TODO if !decl.is_pub and inDifferentFiles() "{} is private" | |
| 4211 | return sema.analyzeDeclRef(block, src, decl); | |
| 4212 | } | |
| 4709 | .Struct, .Opaque, .Union => { | |
| 4710 | if (child_type.getContainerScope()) |container_scope| { | |
| 4711 | if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| { | |
| 4712 | if (!decl.is_pub and !(decl.container.file_scope == block.base.namespace().file_scope)) | |
| 4713 | return mod.fail(&block.base, src, "'{s}' is private", .{field_name}); | |
| 4714 | return sema.analyzeDeclRef(block, src, decl); | |
| 4715 | } | |
| 4213 | 4716 | |
| 4214 | if (container_scope.file_scope == sema.mod.root_scope) { | |
| 4215 | return sema.mod.fail(&block.base, src, "root source file has no member called '{s}'", .{field_name}); | |
| 4216 | } else { | |
| 4217 | return sema.mod.fail(&block.base, src, "container '{}' has no member called '{s}'", .{ child_type, field_name }); | |
| 4717 | // TODO this will give false positives for structs inside the root file | |
| 4718 | if (container_scope.file_scope == mod.root_scope) { | |
| 4719 | return mod.fail( | |
| 4720 | &block.base, | |
| 4721 | src, | |
| 4722 | "root source file has no member named '{s}'", | |
| 4723 | .{field_name}, | |
| 4724 | ); | |
| 4725 | } | |
| 4726 | } | |
| 4727 | // TODO add note: declared here | |
| 4728 | const kw_name = switch (child_type.zigTypeTag()) { | |
| 4729 | .Struct => "struct", | |
| 4730 | .Opaque => "opaque", | |
| 4731 | .Union => "union", | |
| 4732 | else => unreachable, | |
| 4733 | }; | |
| 4734 | return mod.fail(&block.base, src, "{s} '{}' has no member named '{s}'", .{ | |
| 4735 | kw_name, child_type, field_name, | |
| 4736 | }); | |
| 4737 | }, | |
| 4738 | .Enum => { | |
| 4739 | if (child_type.getContainerScope()) |container_scope| { | |
| 4740 | if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| { | |
| 4741 | if (!decl.is_pub and !(decl.container.file_scope == block.base.namespace().file_scope)) | |
| 4742 | return mod.fail(&block.base, src, "'{s}' is private", .{field_name}); | |
| 4743 | return sema.analyzeDeclRef(block, src, decl); | |
| 4744 | } | |
| 4218 | 4745 | } |
| 4746 | const field_index = child_type.enumFieldIndex(field_name) orelse { | |
| 4747 | const msg = msg: { | |
| 4748 | const msg = try mod.errMsg( | |
| 4749 | &block.base, | |
| 4750 | src, | |
| 4751 | "enum '{}' has no member named '{s}'", | |
| 4752 | .{ child_type, field_name }, | |
| 4753 | ); | |
| 4754 | errdefer msg.destroy(sema.gpa); | |
| 4755 | try mod.errNoteNonLazy( | |
| 4756 | child_type.declSrcLoc(), | |
| 4757 | msg, | |
| 4758 | "enum declared here", | |
| 4759 | .{}, | |
| 4760 | ); | |
| 4761 | break :msg msg; | |
| 4762 | }; | |
| 4763 | return mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 4764 | }; | |
| 4765 | const field_index_u32 = @intCast(u32, field_index); | |
| 4766 | const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32); | |
| 4767 | return mod.constInst(arena, src, .{ | |
| 4768 | .ty = try mod.simplePtrType(arena, child_type, false, .One), | |
| 4769 | .val = try Value.Tag.ref_val.create(arena, enum_val), | |
| 4770 | }); | |
| 4219 | 4771 | }, |
| 4220 | else => return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{child_type}), | |
| 4772 | else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}), | |
| 4221 | 4773 | } |
| 4222 | 4774 | }, |
| 4223 | 4775 | .Struct => return sema.analyzeStructFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty), |
| 4224 | 4776 | else => {}, |
| 4225 | 4777 | } |
| 4226 | return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty}); | |
| 4778 | return mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty}); | |
| 4227 | 4779 | } |
| 4228 | 4780 | |
| 4229 | 4781 | fn analyzeStructFieldPtr( |
| ... | ... | @@ -4241,12 +4793,8 @@ fn analyzeStructFieldPtr( |
| 4241 | 4793 | |
| 4242 | 4794 | const struct_obj = elem_ty.castTag(.@"struct").?.data; |
| 4243 | 4795 | |
| 4244 | const field_index = struct_obj.fields.getIndex(field_name) orelse { | |
| 4245 | // TODO note: struct S declared here | |
| 4246 | return mod.fail(&block.base, field_name_src, "no field named '{s}' in struct '{}'", .{ | |
| 4247 | field_name, elem_ty, | |
| 4248 | }); | |
| 4249 | }; | |
| 4796 | const field_index = struct_obj.fields.getIndex(field_name) orelse | |
| 4797 | return sema.failWithBadFieldAccess(block, struct_obj, field_name_src, field_name); | |
| 4250 | 4798 | const field = struct_obj.fields.entries.items[field_index].value; |
| 4251 | 4799 | const ptr_field_ty = try mod.simplePtrType(arena, field.ty, true, .One); |
| 4252 | 4800 | // TODO comptime field access |
| ... | ... | @@ -4262,37 +4810,51 @@ fn elemPtr( |
| 4262 | 4810 | elem_index: *Inst, |
| 4263 | 4811 | elem_index_src: LazySrcLoc, |
| 4264 | 4812 | ) InnerError!*Inst { |
| 4265 | const elem_ty = switch (array_ptr.ty.zigTypeTag()) { | |
| 4813 | const array_ty = switch (array_ptr.ty.zigTypeTag()) { | |
| 4266 | 4814 | .Pointer => array_ptr.ty.elemType(), |
| 4267 | 4815 | else => return sema.mod.fail(&block.base, array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}), |
| 4268 | 4816 | }; |
| 4269 | if (!elem_ty.isIndexable()) { | |
| 4270 | return sema.mod.fail(&block.base, src, "array access of non-array type '{}'", .{elem_ty}); | |
| 4817 | if (!array_ty.isIndexable()) { | |
| 4818 | return sema.mod.fail(&block.base, src, "array access of non-array type '{}'", .{array_ty}); | |
| 4271 | 4819 | } |
| 4272 | ||
| 4273 | if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) { | |
| 4820 | if (array_ty.isSinglePointer() and array_ty.elemType().zigTypeTag() == .Array) { | |
| 4274 | 4821 | // we have to deref the ptr operand to get the actual array pointer |
| 4275 | 4822 | const array_ptr_deref = try sema.analyzeLoad(block, src, array_ptr, array_ptr.src); |
| 4276 | if (array_ptr_deref.value()) |array_ptr_val| { | |
| 4277 | if (elem_index.value()) |index_val| { | |
| 4278 | // Both array pointer and index are compile-time known. | |
| 4279 | const index_u64 = index_val.toUnsignedInt(); | |
| 4280 | // @intCast here because it would have been impossible to construct a value that | |
| 4281 | // required a larger index. | |
| 4282 | const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64)); | |
| 4283 | const pointee_type = elem_ty.elemType().elemType(); | |
| 4284 | ||
| 4285 | return sema.mod.constInst(sema.arena, src, .{ | |
| 4286 | .ty = try Type.Tag.single_const_pointer.create(sema.arena, pointee_type), | |
| 4287 | .val = elem_ptr, | |
| 4288 | }); | |
| 4289 | } | |
| 4290 | } | |
| 4823 | return sema.elemPtrArray(block, src, array_ptr_deref, elem_index, elem_index_src); | |
| 4824 | } | |
| 4825 | if (array_ty.zigTypeTag() == .Array) { | |
| 4826 | return sema.elemPtrArray(block, src, array_ptr, elem_index, elem_index_src); | |
| 4291 | 4827 | } |
| 4292 | 4828 | |
| 4293 | 4829 | return sema.mod.fail(&block.base, src, "TODO implement more analyze elemptr", .{}); |
| 4294 | 4830 | } |
| 4295 | 4831 | |
| 4832 | fn elemPtrArray( | |
| 4833 | sema: *Sema, | |
| 4834 | block: *Scope.Block, | |
| 4835 | src: LazySrcLoc, | |
| 4836 | array_ptr: *Inst, | |
| 4837 | elem_index: *Inst, | |
| 4838 | elem_index_src: LazySrcLoc, | |
| 4839 | ) InnerError!*Inst { | |
| 4840 | if (array_ptr.value()) |array_ptr_val| { | |
| 4841 | if (elem_index.value()) |index_val| { | |
| 4842 | // Both array pointer and index are compile-time known. | |
| 4843 | const index_u64 = index_val.toUnsignedInt(); | |
| 4844 | // @intCast here because it would have been impossible to construct a value that | |
| 4845 | // required a larger index. | |
| 4846 | const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64)); | |
| 4847 | const pointee_type = array_ptr.ty.elemType().elemType(); | |
| 4848 | ||
| 4849 | return sema.mod.constInst(sema.arena, src, .{ | |
| 4850 | .ty = try Type.Tag.single_const_pointer.create(sema.arena, pointee_type), | |
| 4851 | .val = elem_ptr, | |
| 4852 | }); | |
| 4853 | } | |
| 4854 | } | |
| 4855 | return sema.mod.fail(&block.base, src, "TODO implement more analyze elemptr for arrays", .{}); | |
| 4856 | } | |
| 4857 | ||
| 4296 | 4858 | fn coerce( |
| 4297 | 4859 | sema: *Sema, |
| 4298 | 4860 | block: *Scope.Block, |
| ... | ... | @@ -4312,10 +4874,13 @@ fn coerce( |
| 4312 | 4874 | return sema.bitcast(block, dest_type, inst); |
| 4313 | 4875 | } |
| 4314 | 4876 | |
| 4877 | const mod = sema.mod; | |
| 4878 | const arena = sema.arena; | |
| 4879 | ||
| 4315 | 4880 | // undefined to anything |
| 4316 | 4881 | if (inst.value()) |val| { |
| 4317 | 4882 | if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) { |
| 4318 | return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = val }); | |
| 4883 | return mod.constInst(arena, inst_src, .{ .ty = dest_type, .val = val }); | |
| 4319 | 4884 | } |
| 4320 | 4885 | } |
| 4321 | 4886 | assert(inst.ty.zigTypeTag() != .Undefined); |
| ... | ... | @@ -4329,13 +4894,13 @@ fn coerce( |
| 4329 | 4894 | if (try sema.coerceNum(block, dest_type, inst)) |some| |
| 4330 | 4895 | return some; |
| 4331 | 4896 | |
| 4332 | const target = sema.mod.getTarget(); | |
| 4897 | const target = mod.getTarget(); | |
| 4333 | 4898 | |
| 4334 | 4899 | switch (dest_type.zigTypeTag()) { |
| 4335 | 4900 | .Optional => { |
| 4336 | 4901 | // null to ?T |
| 4337 | 4902 | if (inst.ty.zigTypeTag() == .Null) { |
| 4338 | return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) }); | |
| 4903 | return mod.constInst(arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) }); | |
| 4339 | 4904 | } |
| 4340 | 4905 | |
| 4341 | 4906 | // T to ?T |
| ... | ... | @@ -4421,10 +4986,40 @@ fn coerce( |
| 4421 | 4986 | } |
| 4422 | 4987 | } |
| 4423 | 4988 | }, |
| 4989 | .Enum => { | |
| 4990 | // enum literal to enum | |
| 4991 | if (inst.ty.zigTypeTag() == .EnumLiteral) { | |
| 4992 | const val = try sema.resolveConstValue(block, inst_src, inst); | |
| 4993 | const bytes = val.castTag(.enum_literal).?.data; | |
| 4994 | const field_index = dest_type.enumFieldIndex(bytes) orelse { | |
| 4995 | const msg = msg: { | |
| 4996 | const msg = try mod.errMsg( | |
| 4997 | &block.base, | |
| 4998 | inst_src, | |
| 4999 | "enum '{}' has no field named '{s}'", | |
| 5000 | .{ dest_type, bytes }, | |
| 5001 | ); | |
| 5002 | errdefer msg.destroy(sema.gpa); | |
| 5003 | try mod.errNoteNonLazy( | |
| 5004 | dest_type.declSrcLoc(), | |
| 5005 | msg, | |
| 5006 | "enum declared here", | |
| 5007 | .{}, | |
| 5008 | ); | |
| 5009 | break :msg msg; | |
| 5010 | }; | |
| 5011 | return mod.failWithOwnedErrorMsg(&block.base, msg); | |
| 5012 | }; | |
| 5013 | return mod.constInst(arena, inst_src, .{ | |
| 5014 | .ty = dest_type, | |
| 5015 | .val = try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)), | |
| 5016 | }); | |
| 5017 | } | |
| 5018 | }, | |
| 4424 | 5019 | else => {}, |
| 4425 | 5020 | } |
| 4426 | 5021 | |
| 4427 | return sema.mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty }); | |
| 5022 | return mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty }); | |
| 4428 | 5023 | } |
| 4429 | 5024 | |
| 4430 | 5025 | const InMemoryCoercionResult = enum { |
| ... | ... | @@ -4542,7 +5137,7 @@ fn analyzeDeclVal(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl |
| 4542 | 5137 | } |
| 4543 | 5138 | |
| 4544 | 5139 | fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst { |
| 4545 | try sema.mod.declareDeclDependency(sema.owner_decl, decl); | |
| 5140 | _ = try sema.mod.declareDeclDependency(sema.owner_decl, decl); | |
| 4546 | 5141 | sema.mod.ensureDeclAnalyzed(decl) catch |err| { |
| 4547 | 5142 | if (sema.func) |func| { |
| 4548 | 5143 | func.state = .dependency_failure; |
| ... | ... | @@ -4742,9 +5337,9 @@ fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_strin |
| 4742 | 5337 | try std.fs.path.resolve(sema.gpa, &[_][]const u8{ cur_pkg_dir_path, target_string }); |
| 4743 | 5338 | errdefer sema.gpa.free(resolved_path); |
| 4744 | 5339 | |
| 4745 | if (sema.mod.import_table.get(resolved_path)) |some| { | |
| 5340 | if (sema.mod.import_table.get(resolved_path)) |cached_import| { | |
| 4746 | 5341 | sema.gpa.free(resolved_path); |
| 4747 | return some; | |
| 5342 | return cached_import; | |
| 4748 | 5343 | } |
| 4749 | 5344 | |
| 4750 | 5345 | if (found_pkg == null) { |
| ... | ... | @@ -4762,6 +5357,11 @@ fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_strin |
| 4762 | 5357 | const struct_ty = try Type.Tag.empty_struct.create(sema.gpa, &file_scope.root_container); |
| 4763 | 5358 | errdefer sema.gpa.destroy(struct_ty.castTag(.empty_struct).?); |
| 4764 | 5359 | |
| 5360 | const container_name_hash: Scope.NameHash = if (found_pkg) |pkg| | |
| 5361 | pkg.namespace_hash | |
| 5362 | else | |
| 5363 | std.zig.hashName(cur_pkg.namespace_hash, "/", resolved_path); | |
| 5364 | ||
| 4765 | 5365 | file_scope.* = .{ |
| 4766 | 5366 | .sub_file_path = resolved_path, |
| 4767 | 5367 | .source = .{ .unloaded = {} }, |
| ... | ... | @@ -4772,6 +5372,7 @@ fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_strin |
| 4772 | 5372 | .file_scope = file_scope, |
| 4773 | 5373 | .decls = .{}, |
| 4774 | 5374 | .ty = struct_ty, |
| 5375 | .parent_name_hash = container_name_hash, | |
| 4775 | 5376 | }, |
| 4776 | 5377 | }; |
| 4777 | 5378 | sema.mod.analyzeContainer(&file_scope.root_container) catch |err| switch (err) { |
src/clang.zig+27| ... | ... | @@ -300,6 +300,14 @@ pub const ConstantExpr = opaque {}; |
| 300 | 300 | |
| 301 | 301 | pub const ContinueStmt = opaque {}; |
| 302 | 302 | |
| 303 | pub const ConvertVectorExpr = opaque { | |
| 304 | pub const getSrcExpr = ZigClangConvertVectorExpr_getSrcExpr; | |
| 305 | extern fn ZigClangConvertVectorExpr_getSrcExpr(*const ConvertVectorExpr) *const Expr; | |
| 306 | ||
| 307 | pub const getTypeSourceInfo_getType = ZigClangConvertVectorExpr_getTypeSourceInfo_getType; | |
| 308 | extern fn ZigClangConvertVectorExpr_getTypeSourceInfo_getType(*const ConvertVectorExpr) QualType; | |
| 309 | }; | |
| 310 | ||
| 303 | 311 | pub const DecayedType = opaque { |
| 304 | 312 | pub const getDecayedType = ZigClangDecayedType_getDecayedType; |
| 305 | 313 | extern fn ZigClangDecayedType_getDecayedType(*const DecayedType) QualType; |
| ... | ... | @@ -748,6 +756,14 @@ pub const ReturnStmt = opaque { |
| 748 | 756 | extern fn ZigClangReturnStmt_getRetValue(*const ReturnStmt) ?*const Expr; |
| 749 | 757 | }; |
| 750 | 758 | |
| 759 | pub const ShuffleVectorExpr = opaque { | |
| 760 | pub const getNumSubExprs = ZigClangShuffleVectorExpr_getNumSubExprs; | |
| 761 | extern fn ZigClangShuffleVectorExpr_getNumSubExprs(*const ShuffleVectorExpr) c_uint; | |
| 762 | ||
| 763 | pub const getExpr = ZigClangShuffleVectorExpr_getExpr; | |
| 764 | extern fn ZigClangShuffleVectorExpr_getExpr(*const ShuffleVectorExpr, c_uint) *const Expr; | |
| 765 | }; | |
| 766 | ||
| 751 | 767 | pub const SourceManager = opaque { |
| 752 | 768 | pub const getSpellingLoc = ZigClangSourceManager_getSpellingLoc; |
| 753 | 769 | extern fn ZigClangSourceManager_getSpellingLoc(*const SourceManager, Loc: SourceLocation) SourceLocation; |
| ... | ... | @@ -837,6 +853,9 @@ pub const Type = opaque { |
| 837 | 853 | pub const isRecordType = ZigClangType_isRecordType; |
| 838 | 854 | extern fn ZigClangType_isRecordType(*const Type) bool; |
| 839 | 855 | |
| 856 | pub const isVectorType = ZigClangType_isVectorType; | |
| 857 | extern fn ZigClangType_isVectorType(*const Type) bool; | |
| 858 | ||
| 840 | 859 | pub const isIncompleteOrZeroLengthArrayType = ZigClangType_isIncompleteOrZeroLengthArrayType; |
| 841 | 860 | extern fn ZigClangType_isIncompleteOrZeroLengthArrayType(*const Type, *const ASTContext) bool; |
| 842 | 861 | |
| ... | ... | @@ -937,6 +956,14 @@ pub const VarDecl = opaque { |
| 937 | 956 | extern fn ZigClangVarDecl_getTypeSourceInfo_getType(*const VarDecl) QualType; |
| 938 | 957 | }; |
| 939 | 958 | |
| 959 | pub const VectorType = opaque { | |
| 960 | pub const getElementType = ZigClangVectorType_getElementType; | |
| 961 | extern fn ZigClangVectorType_getElementType(*const VectorType) QualType; | |
| 962 | ||
| 963 | pub const getNumElements = ZigClangVectorType_getNumElements; | |
| 964 | extern fn ZigClangVectorType_getNumElements(*const VectorType) c_uint; | |
| 965 | }; | |
| 966 | ||
| 940 | 967 | pub const WhileStmt = opaque { |
| 941 | 968 | pub const getCond = ZigClangWhileStmt_getCond; |
| 942 | 969 | extern fn ZigClangWhileStmt_getCond(*const WhileStmt) *const Expr; |
src/codegen.zig+74-14| ... | ... | @@ -417,7 +417,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 417 | 417 | const node_datas = tree.nodes.items(.data); |
| 418 | 418 | const token_starts = tree.tokens.items(.start); |
| 419 | 419 | |
| 420 | const fn_decl = tree.rootDecls()[module_fn.owner_decl.src_index]; | |
| 420 | const fn_decl = module_fn.owner_decl.src_node; | |
| 421 | 421 | assert(node_tags[fn_decl] == .fn_decl); |
| 422 | 422 | const block = node_datas[fn_decl].rhs; |
| 423 | 423 | const lbrace_src = token_starts[tree.firstToken(block)]; |
| ... | ... | @@ -855,6 +855,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 855 | 855 | .not => return self.genNot(inst.castTag(.not).?), |
| 856 | 856 | .mul => return self.genMul(inst.castTag(.mul).?), |
| 857 | 857 | .mulwrap => return self.genMulWrap(inst.castTag(.mulwrap).?), |
| 858 | .div => return self.genDiv(inst.castTag(.div).?), | |
| 858 | 859 | .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?), |
| 859 | 860 | .ref => return self.genRef(inst.castTag(.ref).?), |
| 860 | 861 | .ret => return self.genRet(inst.castTag(.ret).?), |
| ... | ... | @@ -1092,6 +1093,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1092 | 1093 | } |
| 1093 | 1094 | } |
| 1094 | 1095 | |
| 1096 | fn genDiv(self: *Self, inst: *ir.Inst.BinOp) !MCValue { | |
| 1097 | // No side effects, so if it's unreferenced, do nothing. | |
| 1098 | if (inst.base.isUnused()) | |
| 1099 | return MCValue.dead; | |
| 1100 | switch (arch) { | |
| 1101 | else => return self.fail(inst.base.src, "TODO implement div for {}", .{self.target.cpu.arch}), | |
| 1102 | } | |
| 1103 | } | |
| 1104 | ||
| 1095 | 1105 | fn genBitAnd(self: *Self, inst: *ir.Inst.BinOp) !MCValue { |
| 1096 | 1106 | // No side effects, so if it's unreferenced, do nothing. |
| 1097 | 1107 | if (inst.base.isUnused()) |
| ... | ... | @@ -1735,7 +1745,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1735 | 1745 | |
| 1736 | 1746 | switch (result) { |
| 1737 | 1747 | .register => |reg| { |
| 1738 | try self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base); | |
| 1748 | try self.register_manager.registers.ensureCapacity(self.gpa, self.register_manager.registers.count() + 1); | |
| 1749 | self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base); | |
| 1739 | 1750 | }, |
| 1740 | 1751 | else => {}, |
| 1741 | 1752 | } |
| ... | ... | @@ -1783,8 +1794,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1783 | 1794 | switch (mc_arg) { |
| 1784 | 1795 | .none => continue, |
| 1785 | 1796 | .register => |reg| { |
| 1797 | try self.register_manager.getRegWithoutTracking(reg); | |
| 1786 | 1798 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); |
| 1787 | // TODO interact with the register allocator to mark the instruction as moved. | |
| 1788 | 1799 | }, |
| 1789 | 1800 | .stack_offset => { |
| 1790 | 1801 | // Here we need to emit instructions like this: |
| ... | ... | @@ -1925,8 +1936,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1925 | 1936 | .compare_flags_signed => unreachable, |
| 1926 | 1937 | .compare_flags_unsigned => unreachable, |
| 1927 | 1938 | .register => |reg| { |
| 1939 | try self.register_manager.getRegWithoutTracking(reg); | |
| 1928 | 1940 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); |
| 1929 | // TODO interact with the register allocator to mark the instruction as moved. | |
| 1930 | 1941 | }, |
| 1931 | 1942 | .stack_offset => { |
| 1932 | 1943 | return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{}); |
| ... | ... | @@ -1988,8 +1999,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1988 | 1999 | .compare_flags_signed => unreachable, |
| 1989 | 2000 | .compare_flags_unsigned => unreachable, |
| 1990 | 2001 | .register => |reg| { |
| 2002 | try self.register_manager.getRegWithoutTracking(reg); | |
| 1991 | 2003 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); |
| 1992 | // TODO interact with the register allocator to mark the instruction as moved. | |
| 1993 | 2004 | }, |
| 1994 | 2005 | .stack_offset => { |
| 1995 | 2006 | return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{}); |
| ... | ... | @@ -2039,8 +2050,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2039 | 2050 | switch (mc_arg) { |
| 2040 | 2051 | .none => continue, |
| 2041 | 2052 | .register => |reg| { |
| 2053 | try self.register_manager.getRegWithoutTracking(reg); | |
| 2042 | 2054 | try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); |
| 2043 | // TODO interact with the register allocator to mark the instruction as moved. | |
| 2044 | 2055 | }, |
| 2045 | 2056 | .stack_offset => { |
| 2046 | 2057 | // Here we need to emit instructions like this: |
| ... | ... | @@ -2704,8 +2715,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2704 | 2715 | const reg_name = input[1 .. input.len - 1]; |
| 2705 | 2716 | const reg = parseRegName(reg_name) orelse |
| 2706 | 2717 | return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); |
| 2707 | const arg = try self.resolveInst(inst.args[i]); | |
| 2708 | try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); | |
| 2718 | ||
| 2719 | const arg = inst.args[i]; | |
| 2720 | const arg_mcv = try self.resolveInst(arg); | |
| 2721 | try self.register_manager.getRegWithoutTracking(reg); | |
| 2722 | try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv); | |
| 2709 | 2723 | } |
| 2710 | 2724 | |
| 2711 | 2725 | if (mem.eql(u8, inst.asm_source, "svc #0")) { |
| ... | ... | @@ -2734,8 +2748,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2734 | 2748 | const reg_name = input[1 .. input.len - 1]; |
| 2735 | 2749 | const reg = parseRegName(reg_name) orelse |
| 2736 | 2750 | return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); |
| 2737 | const arg = try self.resolveInst(inst.args[i]); | |
| 2738 | try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); | |
| 2751 | ||
| 2752 | const arg = inst.args[i]; | |
| 2753 | const arg_mcv = try self.resolveInst(arg); | |
| 2754 | try self.register_manager.getRegWithoutTracking(reg); | |
| 2755 | try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv); | |
| 2739 | 2756 | } |
| 2740 | 2757 | |
| 2741 | 2758 | if (mem.eql(u8, inst.asm_source, "svc #0")) { |
| ... | ... | @@ -2766,8 +2783,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2766 | 2783 | const reg_name = input[1 .. input.len - 1]; |
| 2767 | 2784 | const reg = parseRegName(reg_name) orelse |
| 2768 | 2785 | return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); |
| 2769 | const arg = try self.resolveInst(inst.args[i]); | |
| 2770 | try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); | |
| 2786 | ||
| 2787 | const arg = inst.args[i]; | |
| 2788 | const arg_mcv = try self.resolveInst(arg); | |
| 2789 | try self.register_manager.getRegWithoutTracking(reg); | |
| 2790 | try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv); | |
| 2771 | 2791 | } |
| 2772 | 2792 | |
| 2773 | 2793 | if (mem.eql(u8, inst.asm_source, "ecall")) { |
| ... | ... | @@ -2796,8 +2816,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2796 | 2816 | const reg_name = input[1 .. input.len - 1]; |
| 2797 | 2817 | const reg = parseRegName(reg_name) orelse |
| 2798 | 2818 | return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); |
| 2799 | const arg = try self.resolveInst(inst.args[i]); | |
| 2800 | try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); | |
| 2819 | ||
| 2820 | const arg = inst.args[i]; | |
| 2821 | const arg_mcv = try self.resolveInst(arg); | |
| 2822 | try self.register_manager.getRegWithoutTracking(reg); | |
| 2823 | try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv); | |
| 2801 | 2824 | } |
| 2802 | 2825 | |
| 2803 | 2826 | if (mem.eql(u8, inst.asm_source, "syscall")) { |
| ... | ... | @@ -3302,6 +3325,43 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3302 | 3325 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32()); |
| 3303 | 3326 | } |
| 3304 | 3327 | }, |
| 3328 | .stack_offset => |unadjusted_off| { | |
| 3329 | // TODO: maybe addressing from sp instead of fp | |
| 3330 | const abi_size = ty.abiSize(self.target.*); | |
| 3331 | const adj_off = unadjusted_off + abi_size; | |
| 3332 | ||
| 3333 | const rn: Register = switch (arch) { | |
| 3334 | .aarch64, .aarch64_be => .x29, | |
| 3335 | .aarch64_32 => .w29, | |
| 3336 | else => unreachable, | |
| 3337 | }; | |
| 3338 | ||
| 3339 | const offset = if (math.cast(i9, adj_off)) |imm| | |
| 3340 | Instruction.LoadStoreOffset.imm_post_index(-imm) | |
| 3341 | else |_| | |
| 3342 | Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off })); | |
| 3343 | ||
| 3344 | switch (abi_size) { | |
| 3345 | 1, 2 => { | |
| 3346 | const ldr = switch (abi_size) { | |
| 3347 | 1 => Instruction.ldrb, | |
| 3348 | 2 => Instruction.ldrh, | |
| 3349 | else => unreachable, // unexpected abi size | |
| 3350 | }; | |
| 3351 | ||
| 3352 | writeInt(u32, try self.code.addManyAsArray(4), ldr(reg, rn, .{ | |
| 3353 | .offset = offset, | |
| 3354 | }).toU32()); | |
| 3355 | }, | |
| 3356 | 4, 8 => { | |
| 3357 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ | |
| 3358 | .rn = rn, | |
| 3359 | .offset = offset, | |
| 3360 | } }).toU32()); | |
| 3361 | }, | |
| 3362 | else => return self.fail(src, "TODO implement genSetReg other types abi_size={}", .{abi_size}), | |
| 3363 | } | |
| 3364 | }, | |
| 3305 | 3365 | else => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}), |
| 3306 | 3366 | }, |
| 3307 | 3367 | .riscv64 => switch (mcv) { |
src/codegen/aarch64.zig+209-56| ... | ... | @@ -200,7 +200,7 @@ test "FloatingPointRegister.toX" { |
| 200 | 200 | |
| 201 | 201 | /// Represents an instruction in the AArch64 instruction set |
| 202 | 202 | pub const Instruction = union(enum) { |
| 203 | MoveWideImmediate: packed struct { | |
| 203 | move_wide_immediate: packed struct { | |
| 204 | 204 | rd: u5, |
| 205 | 205 | imm16: u16, |
| 206 | 206 | hw: u2, |
| ... | ... | @@ -208,14 +208,14 @@ pub const Instruction = union(enum) { |
| 208 | 208 | opc: u2, |
| 209 | 209 | sf: u1, |
| 210 | 210 | }, |
| 211 | PCRelativeAddress: packed struct { | |
| 211 | pc_relative_address: packed struct { | |
| 212 | 212 | rd: u5, |
| 213 | 213 | immhi: u19, |
| 214 | 214 | fixed: u5 = 0b10000, |
| 215 | 215 | immlo: u2, |
| 216 | 216 | op: u1, |
| 217 | 217 | }, |
| 218 | LoadStoreRegister: packed struct { | |
| 218 | load_store_register: packed struct { | |
| 219 | 219 | rt: u5, |
| 220 | 220 | rn: u5, |
| 221 | 221 | offset: u12, |
| ... | ... | @@ -225,7 +225,7 @@ pub const Instruction = union(enum) { |
| 225 | 225 | fixed: u3 = 0b111, |
| 226 | 226 | size: u2, |
| 227 | 227 | }, |
| 228 | LoadStorePairOfRegisters: packed struct { | |
| 228 | load_store_register_pair: packed struct { | |
| 229 | 229 | rt1: u5, |
| 230 | 230 | rn: u5, |
| 231 | 231 | rt2: u5, |
| ... | ... | @@ -235,20 +235,20 @@ pub const Instruction = union(enum) { |
| 235 | 235 | fixed: u5 = 0b101_0_0, |
| 236 | 236 | opc: u2, |
| 237 | 237 | }, |
| 238 | LoadLiteral: packed struct { | |
| 238 | load_literal: packed struct { | |
| 239 | 239 | rt: u5, |
| 240 | 240 | imm19: u19, |
| 241 | 241 | fixed: u6 = 0b011_0_00, |
| 242 | 242 | opc: u2, |
| 243 | 243 | }, |
| 244 | ExceptionGeneration: packed struct { | |
| 244 | exception_generation: packed struct { | |
| 245 | 245 | ll: u2, |
| 246 | 246 | op2: u3, |
| 247 | 247 | imm16: u16, |
| 248 | 248 | opc: u3, |
| 249 | 249 | fixed: u8 = 0b1101_0100, |
| 250 | 250 | }, |
| 251 | UnconditionalBranchRegister: packed struct { | |
| 251 | unconditional_branch_register: packed struct { | |
| 252 | 252 | op4: u5, |
| 253 | 253 | rn: u5, |
| 254 | 254 | op3: u6, |
| ... | ... | @@ -256,15 +256,15 @@ pub const Instruction = union(enum) { |
| 256 | 256 | opc: u4, |
| 257 | 257 | fixed: u7 = 0b1101_011, |
| 258 | 258 | }, |
| 259 | UnconditionalBranchImmediate: packed struct { | |
| 259 | unconditional_branch_immediate: packed struct { | |
| 260 | 260 | imm26: u26, |
| 261 | 261 | fixed: u5 = 0b00101, |
| 262 | 262 | op: u1, |
| 263 | 263 | }, |
| 264 | NoOperation: packed struct { | |
| 264 | no_operation: packed struct { | |
| 265 | 265 | fixed: u32 = 0b1101010100_0_00_011_0010_0000_000_11111, |
| 266 | 266 | }, |
| 267 | LogicalShiftedRegister: packed struct { | |
| 267 | logical_shifted_register: packed struct { | |
| 268 | 268 | rd: u5, |
| 269 | 269 | rn: u5, |
| 270 | 270 | imm6: u6, |
| ... | ... | @@ -275,7 +275,7 @@ pub const Instruction = union(enum) { |
| 275 | 275 | opc: u2, |
| 276 | 276 | sf: u1, |
| 277 | 277 | }, |
| 278 | AddSubtractImmediate: packed struct { | |
| 278 | add_subtract_immediate: packed struct { | |
| 279 | 279 | rd: u5, |
| 280 | 280 | rn: u5, |
| 281 | 281 | imm12: u12, |
| ... | ... | @@ -285,6 +285,20 @@ pub const Instruction = union(enum) { |
| 285 | 285 | op: u1, |
| 286 | 286 | sf: u1, |
| 287 | 287 | }, |
| 288 | conditional_branch: struct { | |
| 289 | cond: u4, | |
| 290 | o0: u1, | |
| 291 | imm19: u19, | |
| 292 | o1: u1, | |
| 293 | fixed: u7 = 0b0101010, | |
| 294 | }, | |
| 295 | compare_and_branch: struct { | |
| 296 | rt: u5, | |
| 297 | imm19: u19, | |
| 298 | op: u1, | |
| 299 | fixed: u6 = 0b011010, | |
| 300 | sf: u1, | |
| 301 | }, | |
| 288 | 302 | |
| 289 | 303 | pub const Shift = struct { |
| 290 | 304 | shift: Type = .lsl, |
| ... | ... | @@ -303,19 +317,73 @@ pub const Instruction = union(enum) { |
| 303 | 317 | }; |
| 304 | 318 | }; |
| 305 | 319 | |
| 320 | pub const Condition = enum(u4) { | |
| 321 | /// Integer: Equal | |
| 322 | /// Floating point: Equal | |
| 323 | eq, | |
| 324 | /// Integer: Not equal | |
| 325 | /// Floating point: Not equal or unordered | |
| 326 | ne, | |
| 327 | /// Integer: Carry set | |
| 328 | /// Floating point: Greater than, equal, or unordered | |
| 329 | cs, | |
| 330 | /// Integer: Carry clear | |
| 331 | /// Floating point: Less than | |
| 332 | cc, | |
| 333 | /// Integer: Minus, negative | |
| 334 | /// Floating point: Less than | |
| 335 | mi, | |
| 336 | /// Integer: Plus, positive or zero | |
| 337 | /// Floating point: Greater than, equal, or unordered | |
| 338 | pl, | |
| 339 | /// Integer: Overflow | |
| 340 | /// Floating point: Unordered | |
| 341 | vs, | |
| 342 | /// Integer: No overflow | |
| 343 | /// Floating point: Ordered | |
| 344 | vc, | |
| 345 | /// Integer: Unsigned higher | |
| 346 | /// Floating point: Greater than, or unordered | |
| 347 | hi, | |
| 348 | /// Integer: Unsigned lower or same | |
| 349 | /// Floating point: Less than or equal | |
| 350 | ls, | |
| 351 | /// Integer: Signed greater than or equal | |
| 352 | /// Floating point: Greater than or equal | |
| 353 | ge, | |
| 354 | /// Integer: Signed less than | |
| 355 | /// Floating point: Less than, or unordered | |
| 356 | lt, | |
| 357 | /// Integer: Signed greater than | |
| 358 | /// Floating point: Greater than | |
| 359 | gt, | |
| 360 | /// Integer: Signed less than or equal | |
| 361 | /// Floating point: Less than, equal, or unordered | |
| 362 | le, | |
| 363 | /// Integer: Always | |
| 364 | /// Floating point: Always | |
| 365 | al, | |
| 366 | /// Integer: Always | |
| 367 | /// Floating point: Always | |
| 368 | nv, | |
| 369 | }; | |
| 370 | ||
| 306 | 371 | pub fn toU32(self: Instruction) u32 { |
| 307 | 372 | return switch (self) { |
| 308 | .MoveWideImmediate => |v| @bitCast(u32, v), | |
| 309 | .PCRelativeAddress => |v| @bitCast(u32, v), | |
| 310 | .LoadStoreRegister => |v| @bitCast(u32, v), | |
| 311 | .LoadStorePairOfRegisters => |v| @bitCast(u32, v), | |
| 312 | .LoadLiteral => |v| @bitCast(u32, v), | |
| 313 | .ExceptionGeneration => |v| @bitCast(u32, v), | |
| 314 | .UnconditionalBranchRegister => |v| @bitCast(u32, v), | |
| 315 | .UnconditionalBranchImmediate => |v| @bitCast(u32, v), | |
| 316 | .NoOperation => |v| @bitCast(u32, v), | |
| 317 | .LogicalShiftedRegister => |v| @bitCast(u32, v), | |
| 318 | .AddSubtractImmediate => |v| @bitCast(u32, v), | |
| 373 | .move_wide_immediate => |v| @bitCast(u32, v), | |
| 374 | .pc_relative_address => |v| @bitCast(u32, v), | |
| 375 | .load_store_register => |v| @bitCast(u32, v), | |
| 376 | .load_store_register_pair => |v| @bitCast(u32, v), | |
| 377 | .load_literal => |v| @bitCast(u32, v), | |
| 378 | .exception_generation => |v| @bitCast(u32, v), | |
| 379 | .unconditional_branch_register => |v| @bitCast(u32, v), | |
| 380 | .unconditional_branch_immediate => |v| @bitCast(u32, v), | |
| 381 | .no_operation => |v| @bitCast(u32, v), | |
| 382 | .logical_shifted_register => |v| @bitCast(u32, v), | |
| 383 | .add_subtract_immediate => |v| @bitCast(u32, v), | |
| 384 | // TODO once packed structs work, this can be refactored | |
| 385 | .conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25), | |
| 386 | .compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31), | |
| 319 | 387 | }; |
| 320 | 388 | } |
| 321 | 389 | |
| ... | ... | @@ -329,7 +397,7 @@ pub const Instruction = union(enum) { |
| 329 | 397 | 32 => { |
| 330 | 398 | assert(shift % 16 == 0 and shift <= 16); |
| 331 | 399 | return Instruction{ |
| 332 | .MoveWideImmediate = .{ | |
| 400 | .move_wide_immediate = .{ | |
| 333 | 401 | .rd = rd.id(), |
| 334 | 402 | .imm16 = imm16, |
| 335 | 403 | .hw = @intCast(u2, shift / 16), |
| ... | ... | @@ -341,7 +409,7 @@ pub const Instruction = union(enum) { |
| 341 | 409 | 64 => { |
| 342 | 410 | assert(shift % 16 == 0 and shift <= 48); |
| 343 | 411 | return Instruction{ |
| 344 | .MoveWideImmediate = .{ | |
| 412 | .move_wide_immediate = .{ | |
| 345 | 413 | .rd = rd.id(), |
| 346 | 414 | .imm16 = imm16, |
| 347 | 415 | .hw = @intCast(u2, shift / 16), |
| ... | ... | @@ -358,7 +426,7 @@ pub const Instruction = union(enum) { |
| 358 | 426 | assert(rd.size() == 64); |
| 359 | 427 | const imm21_u = @bitCast(u21, imm21); |
| 360 | 428 | return Instruction{ |
| 361 | .PCRelativeAddress = .{ | |
| 429 | .pc_relative_address = .{ | |
| 362 | 430 | .rd = rd.id(), |
| 363 | 431 | .immlo = @truncate(u2, imm21_u), |
| 364 | 432 | .immhi = @truncate(u19, imm21_u >> 2), |
| ... | ... | @@ -487,11 +555,17 @@ pub const Instruction = union(enum) { |
| 487 | 555 | /// Which kind of load/store to perform |
| 488 | 556 | const LoadStoreVariant = enum { |
| 489 | 557 | /// 32-bit or 64-bit |
| 490 | normal, | |
| 491 | /// 16-bit | |
| 492 | half, | |
| 493 | /// 8-bit | |
| 494 | byte, | |
| 558 | str, | |
| 559 | /// 16-bit, zero-extended | |
| 560 | strh, | |
| 561 | /// 8-bit, zero-extended | |
| 562 | strb, | |
| 563 | /// 32-bit or 64-bit | |
| 564 | ldr, | |
| 565 | /// 16-bit, zero-extended | |
| 566 | ldrh, | |
| 567 | /// 8-bit, zero-extended | |
| 568 | ldrb, | |
| 495 | 569 | }; |
| 496 | 570 | |
| 497 | 571 | fn loadStoreRegister( |
| ... | ... | @@ -499,7 +573,6 @@ pub const Instruction = union(enum) { |
| 499 | 573 | rn: Register, |
| 500 | 574 | offset: LoadStoreOffset, |
| 501 | 575 | variant: LoadStoreVariant, |
| 502 | load: bool, | |
| 503 | 576 | ) Instruction { |
| 504 | 577 | const off = offset.toU12(); |
| 505 | 578 | const op1: u2 = blk: { |
| ... | ... | @@ -512,9 +585,12 @@ pub const Instruction = union(enum) { |
| 512 | 585 | } |
| 513 | 586 | break :blk 0b00; |
| 514 | 587 | }; |
| 515 | const opc: u2 = if (load) 0b01 else 0b00; | |
| 588 | const opc: u2 = switch (variant) { | |
| 589 | .ldr, .ldrh, .ldrb => 0b01, | |
| 590 | .str, .strh, .strb => 0b00, | |
| 591 | }; | |
| 516 | 592 | return Instruction{ |
| 517 | .LoadStoreRegister = .{ | |
| 593 | .load_store_register = .{ | |
| 518 | 594 | .rt = rt.id(), |
| 519 | 595 | .rn = rn.id(), |
| 520 | 596 | .offset = off, |
| ... | ... | @@ -523,20 +599,20 @@ pub const Instruction = union(enum) { |
| 523 | 599 | .v = 0, |
| 524 | 600 | .size = blk: { |
| 525 | 601 | switch (variant) { |
| 526 | .normal => switch (rt.size()) { | |
| 602 | .ldr, .str => switch (rt.size()) { | |
| 527 | 603 | 32 => break :blk 0b10, |
| 528 | 604 | 64 => break :blk 0b11, |
| 529 | 605 | else => unreachable, // unexpected register size |
| 530 | 606 | }, |
| 531 | .half => break :blk 0b01, | |
| 532 | .byte => break :blk 0b00, | |
| 607 | .ldrh, .strh => break :blk 0b01, | |
| 608 | .ldrb, .strb => break :blk 0b00, | |
| 533 | 609 | } |
| 534 | 610 | }, |
| 535 | 611 | }, |
| 536 | 612 | }; |
| 537 | 613 | } |
| 538 | 614 | |
| 539 | fn loadStorePairOfRegisters( | |
| 615 | fn loadStoreRegisterPair( | |
| 540 | 616 | rt1: Register, |
| 541 | 617 | rt2: Register, |
| 542 | 618 | rn: Register, |
| ... | ... | @@ -549,7 +625,7 @@ pub const Instruction = union(enum) { |
| 549 | 625 | assert(-256 <= offset and offset <= 252); |
| 550 | 626 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 2)); |
| 551 | 627 | return Instruction{ |
| 552 | .LoadStorePairOfRegisters = .{ | |
| 628 | .load_store_register_pair = .{ | |
| 553 | 629 | .rt1 = rt1.id(), |
| 554 | 630 | .rn = rn.id(), |
| 555 | 631 | .rt2 = rt2.id(), |
| ... | ... | @@ -564,7 +640,7 @@ pub const Instruction = union(enum) { |
| 564 | 640 | assert(-512 <= offset and offset <= 504); |
| 565 | 641 | const imm7 = @truncate(u7, @bitCast(u9, offset >> 3)); |
| 566 | 642 | return Instruction{ |
| 567 | .LoadStorePairOfRegisters = .{ | |
| 643 | .load_store_register_pair = .{ | |
| 568 | 644 | .rt1 = rt1.id(), |
| 569 | 645 | .rn = rn.id(), |
| 570 | 646 | .rt2 = rt2.id(), |
| ... | ... | @@ -583,7 +659,7 @@ pub const Instruction = union(enum) { |
| 583 | 659 | switch (rt.size()) { |
| 584 | 660 | 32 => { |
| 585 | 661 | return Instruction{ |
| 586 | .LoadLiteral = .{ | |
| 662 | .load_literal = .{ | |
| 587 | 663 | .rt = rt.id(), |
| 588 | 664 | .imm19 = imm19, |
| 589 | 665 | .opc = 0b00, |
| ... | ... | @@ -592,7 +668,7 @@ pub const Instruction = union(enum) { |
| 592 | 668 | }, |
| 593 | 669 | 64 => { |
| 594 | 670 | return Instruction{ |
| 595 | .LoadLiteral = .{ | |
| 671 | .load_literal = .{ | |
| 596 | 672 | .rt = rt.id(), |
| 597 | 673 | .imm19 = imm19, |
| 598 | 674 | .opc = 0b01, |
| ... | ... | @@ -610,7 +686,7 @@ pub const Instruction = union(enum) { |
| 610 | 686 | imm16: u16, |
| 611 | 687 | ) Instruction { |
| 612 | 688 | return Instruction{ |
| 613 | .ExceptionGeneration = .{ | |
| 689 | .exception_generation = .{ | |
| 614 | 690 | .ll = ll, |
| 615 | 691 | .op2 = op2, |
| 616 | 692 | .imm16 = imm16, |
| ... | ... | @@ -629,7 +705,7 @@ pub const Instruction = union(enum) { |
| 629 | 705 | assert(rn.size() == 64); |
| 630 | 706 | |
| 631 | 707 | return Instruction{ |
| 632 | .UnconditionalBranchRegister = .{ | |
| 708 | .unconditional_branch_register = .{ | |
| 633 | 709 | .op4 = op4, |
| 634 | 710 | .rn = rn.id(), |
| 635 | 711 | .op3 = op3, |
| ... | ... | @@ -644,7 +720,7 @@ pub const Instruction = union(enum) { |
| 644 | 720 | offset: i28, |
| 645 | 721 | ) Instruction { |
| 646 | 722 | return Instruction{ |
| 647 | .UnconditionalBranchImmediate = .{ | |
| 723 | .unconditional_branch_immediate = .{ | |
| 648 | 724 | .imm26 = @bitCast(u26, @intCast(i26, offset >> 2)), |
| 649 | 725 | .op = op, |
| 650 | 726 | }, |
| ... | ... | @@ -663,7 +739,7 @@ pub const Instruction = union(enum) { |
| 663 | 739 | 32 => { |
| 664 | 740 | assert(shift.amount < 32); |
| 665 | 741 | return Instruction{ |
| 666 | .LogicalShiftedRegister = .{ | |
| 742 | .logical_shifted_register = .{ | |
| 667 | 743 | .rd = rd.id(), |
| 668 | 744 | .rn = rn.id(), |
| 669 | 745 | .imm6 = shift.amount, |
| ... | ... | @@ -677,7 +753,7 @@ pub const Instruction = union(enum) { |
| 677 | 753 | }, |
| 678 | 754 | 64 => { |
| 679 | 755 | return Instruction{ |
| 680 | .LogicalShiftedRegister = .{ | |
| 756 | .logical_shifted_register = .{ | |
| 681 | 757 | .rd = rd.id(), |
| 682 | 758 | .rn = rn.id(), |
| 683 | 759 | .imm6 = shift.amount, |
| ... | ... | @@ -702,7 +778,7 @@ pub const Instruction = union(enum) { |
| 702 | 778 | shift: bool, |
| 703 | 779 | ) Instruction { |
| 704 | 780 | return Instruction{ |
| 705 | .AddSubtractImmediate = .{ | |
| 781 | .add_subtract_immediate = .{ | |
| 706 | 782 | .rd = rd.id(), |
| 707 | 783 | .rn = rn.id(), |
| 708 | 784 | .imm12 = imm12, |
| ... | ... | @@ -718,6 +794,43 @@ pub const Instruction = union(enum) { |
| 718 | 794 | }; |
| 719 | 795 | } |
| 720 | 796 | |
| 797 | fn conditionalBranch( | |
| 798 | o0: u1, | |
| 799 | o1: u1, | |
| 800 | cond: Condition, | |
| 801 | offset: i21, | |
| 802 | ) Instruction { | |
| 803 | assert(offset & 0b11 == 0b00); | |
| 804 | return Instruction{ | |
| 805 | .conditional_branch = .{ | |
| 806 | .cond = @enumToInt(cond), | |
| 807 | .o0 = o0, | |
| 808 | .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), | |
| 809 | .o1 = o1, | |
| 810 | }, | |
| 811 | }; | |
| 812 | } | |
| 813 | ||
| 814 | fn compareAndBranch( | |
| 815 | op: u1, | |
| 816 | rt: Register, | |
| 817 | offset: i21, | |
| 818 | ) Instruction { | |
| 819 | assert(offset & 0b11 == 0b00); | |
| 820 | return Instruction{ | |
| 821 | .compare_and_branch = .{ | |
| 822 | .rt = rt.id(), | |
| 823 | .imm19 = @bitCast(u19, @intCast(i19, offset >> 2)), | |
| 824 | .op = op, | |
| 825 | .sf = switch (rt.size()) { | |
| 826 | 32 => 0b0, | |
| 827 | 64 => 0b1, | |
| 828 | else => unreachable, // unexpected register size | |
| 829 | }, | |
| 830 | }, | |
| 831 | }; | |
| 832 | } | |
| 833 | ||
| 721 | 834 | // Helper functions for assembly syntax functions |
| 722 | 835 | |
| 723 | 836 | // Move wide (immediate) |
| ... | ... | @@ -756,25 +869,33 @@ pub const Instruction = union(enum) { |
| 756 | 869 | |
| 757 | 870 | pub fn ldr(rt: Register, args: LdrArgs) Instruction { |
| 758 | 871 | switch (args) { |
| 759 | .register => |info| return loadStoreRegister(rt, info.rn, info.offset, .normal, true), | |
| 872 | .register => |info| return loadStoreRegister(rt, info.rn, info.offset, .ldr), | |
| 760 | 873 | .literal => |literal| return loadLiteral(rt, literal), |
| 761 | 874 | } |
| 762 | 875 | } |
| 763 | 876 | |
| 877 | pub fn ldrh(rt: Register, rn: Register, args: StrArgs) Instruction { | |
| 878 | return loadStoreRegister(rt, rn, args.offset, .ldrh); | |
| 879 | } | |
| 880 | ||
| 881 | pub fn ldrb(rt: Register, rn: Register, args: StrArgs) Instruction { | |
| 882 | return loadStoreRegister(rt, rn, args.offset, .ldrb); | |
| 883 | } | |
| 884 | ||
| 764 | 885 | pub const StrArgs = struct { |
| 765 | 886 | offset: LoadStoreOffset = LoadStoreOffset.none, |
| 766 | 887 | }; |
| 767 | 888 | |
| 768 | 889 | pub fn str(rt: Register, rn: Register, args: StrArgs) Instruction { |
| 769 | return loadStoreRegister(rt, rn, args.offset, .normal, false); | |
| 890 | return loadStoreRegister(rt, rn, args.offset, .str); | |
| 770 | 891 | } |
| 771 | 892 | |
| 772 | 893 | pub fn strh(rt: Register, rn: Register, args: StrArgs) Instruction { |
| 773 | return loadStoreRegister(rt, rn, args.offset, .half, false); | |
| 894 | return loadStoreRegister(rt, rn, args.offset, .strh); | |
| 774 | 895 | } |
| 775 | 896 | |
| 776 | 897 | pub fn strb(rt: Register, rn: Register, args: StrArgs) Instruction { |
| 777 | return loadStoreRegister(rt, rn, args.offset, .byte, false); | |
| 898 | return loadStoreRegister(rt, rn, args.offset, .strb); | |
| 778 | 899 | } |
| 779 | 900 | |
| 780 | 901 | // Load or store pair of registers |
| ... | ... | @@ -805,19 +926,19 @@ pub const Instruction = union(enum) { |
| 805 | 926 | }; |
| 806 | 927 | |
| 807 | 928 | pub fn ldp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { |
| 808 | return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true); | |
| 929 | return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), true); | |
| 809 | 930 | } |
| 810 | 931 | |
| 811 | 932 | pub fn ldnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { |
| 812 | return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, true); | |
| 933 | return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, true); | |
| 813 | 934 | } |
| 814 | 935 | |
| 815 | 936 | pub fn stp(rt1: Register, rt2: Register, rn: Register, offset: LoadStorePairOffset) Instruction { |
| 816 | return loadStorePairOfRegisters(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); | |
| 937 | return loadStoreRegisterPair(rt1, rt2, rn, offset.offset, @enumToInt(offset.encoding), false); | |
| 817 | 938 | } |
| 818 | 939 | |
| 819 | 940 | pub fn stnp(rt1: Register, rt2: Register, rn: Register, offset: i9) Instruction { |
| 820 | return loadStorePairOfRegisters(rt1, rt2, rn, offset, 0, false); | |
| 941 | return loadStoreRegisterPair(rt1, rt2, rn, offset, 0, false); | |
| 821 | 942 | } |
| 822 | 943 | |
| 823 | 944 | // Exception generation |
| ... | ... | @@ -869,7 +990,7 @@ pub const Instruction = union(enum) { |
| 869 | 990 | // Nop |
| 870 | 991 | |
| 871 | 992 | pub fn nop() Instruction { |
| 872 | return Instruction{ .NoOperation = .{} }; | |
| 993 | return Instruction{ .no_operation = .{} }; | |
| 873 | 994 | } |
| 874 | 995 | |
| 875 | 996 | // Logical (shifted register) |
| ... | ... | @@ -923,6 +1044,22 @@ pub const Instruction = union(enum) { |
| 923 | 1044 | pub fn subs(rd: Register, rn: Register, imm: u12, shift: bool) Instruction { |
| 924 | 1045 | return addSubtractImmediate(0b1, 0b1, rd, rn, imm, shift); |
| 925 | 1046 | } |
| 1047 | ||
| 1048 | // Conditional branch | |
| 1049 | ||
| 1050 | pub fn bCond(cond: Condition, offset: i21) Instruction { | |
| 1051 | return conditionalBranch(0b0, 0b0, cond, offset); | |
| 1052 | } | |
| 1053 | ||
| 1054 | // Compare and branch | |
| 1055 | ||
| 1056 | pub fn cbz(rt: Register, offset: i21) Instruction { | |
| 1057 | return compareAndBranch(0b0, rt, offset); | |
| 1058 | } | |
| 1059 | ||
| 1060 | pub fn cbnz(rt: Register, offset: i21) Instruction { | |
| 1061 | return compareAndBranch(0b1, rt, offset); | |
| 1062 | } | |
| 926 | 1063 | }; |
| 927 | 1064 | |
| 928 | 1065 | test { |
| ... | ... | @@ -1004,6 +1141,14 @@ test "serialize instructions" { |
| 1004 | 1141 | .inst = Instruction.ldr(.x2, .{ .literal = 0x1 }), |
| 1005 | 1142 | .expected = 0b01_011_0_00_0000000000000000001_00010, |
| 1006 | 1143 | }, |
| 1144 | .{ // ldrh x7, [x4], #0xaa | |
| 1145 | .inst = Instruction.ldrh(.x7, .x4, .{ .offset = Instruction.LoadStoreOffset.imm_post_index(0xaa) }), | |
| 1146 | .expected = 0b01_111_0_00_01_0_010101010_01_00100_00111, | |
| 1147 | }, | |
| 1148 | .{ // ldrb x9, [x15, #0xff]! | |
| 1149 | .inst = Instruction.ldrb(.x9, .x15, .{ .offset = Instruction.LoadStoreOffset.imm_pre_index(0xff) }), | |
| 1150 | .expected = 0b00_111_0_00_01_0_011111111_11_01111_01001, | |
| 1151 | }, | |
| 1007 | 1152 | .{ // str x2, [x1] |
| 1008 | 1153 | .inst = Instruction.str(.x2, .x1, .{}), |
| 1009 | 1154 | .expected = 0b11_111_0_01_00_000000000000_00001_00010, |
| ... | ... | @@ -1068,6 +1213,14 @@ test "serialize instructions" { |
| 1068 | 1213 | .inst = Instruction.subs(.x0, .x5, 11, true), |
| 1069 | 1214 | .expected = 0b1_1_1_100010_1_0000_0000_1011_00101_00000, |
| 1070 | 1215 | }, |
| 1216 | .{ // b.hi #-4 | |
| 1217 | .inst = Instruction.bCond(.hi, -4), | |
| 1218 | .expected = 0b0101010_0_1111111111111111111_0_1000, | |
| 1219 | }, | |
| 1220 | .{ // cbz x10, #40 | |
| 1221 | .inst = Instruction.cbz(.x10, 40), | |
| 1222 | .expected = 0b1_011010_0_0000000000000001010_01010, | |
| 1223 | }, | |
| 1071 | 1224 | }; |
| 1072 | 1225 | |
| 1073 | 1226 | for (testcases) |case| { |
src/codegen/c.zig+164-17| ... | ... | @@ -44,22 +44,34 @@ fn formatTypeAsCIdentifier( |
| 44 | 44 | var buffer = [1]u8{0} ** 128; |
| 45 | 45 | // We don't care if it gets cut off, it's still more unique than a number |
| 46 | 46 | var buf = std.fmt.bufPrint(&buffer, "{}", .{data}) catch &buffer; |
| 47 | return formatIdent(buf, "", .{}, writer); | |
| 48 | } | |
| 49 | ||
| 50 | pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) { | |
| 51 | return .{ .data = t }; | |
| 52 | } | |
| 47 | 53 | |
| 48 | for (buf) |c, i| { | |
| 54 | fn formatIdent( | |
| 55 | ident: []const u8, | |
| 56 | comptime fmt: []const u8, | |
| 57 | options: std.fmt.FormatOptions, | |
| 58 | writer: anytype, | |
| 59 | ) !void { | |
| 60 | for (ident) |c, i| { | |
| 49 | 61 | switch (c) { |
| 50 | 0 => return writer.writeAll(buf[0..i]), | |
| 51 | 'a'...'z', 'A'...'Z', '_', '$' => {}, | |
| 62 | 'a'...'z', 'A'...'Z', '_' => try writer.writeByte(c), | |
| 52 | 63 | '0'...'9' => if (i == 0) { |
| 53 | buf[i] = '_'; | |
| 64 | try writer.print("${x:2}", .{c}); | |
| 65 | } else { | |
| 66 | try writer.writeByte(c); | |
| 54 | 67 | }, |
| 55 | else => buf[i] = '_', | |
| 68 | else => try writer.print("${x:2}", .{c}), | |
| 56 | 69 | } |
| 57 | 70 | } |
| 58 | return writer.writeAll(buf); | |
| 59 | 71 | } |
| 60 | 72 | |
| 61 | pub fn typeToCIdentifier(t: Type) std.fmt.Formatter(formatTypeAsCIdentifier) { | |
| 62 | return .{ .data = t }; | |
| 73 | pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) { | |
| 74 | return .{ .data = ident }; | |
| 63 | 75 | } |
| 64 | 76 | |
| 65 | 77 | /// This data is available when outputting .c code for a Module. |
| ... | ... | @@ -160,7 +172,10 @@ pub const DeclGen = struct { |
| 160 | 172 | val: Value, |
| 161 | 173 | ) error{ OutOfMemory, AnalysisFail }!void { |
| 162 | 174 | if (val.isUndef()) { |
| 163 | return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: properly handle undefined in all cases (with debug safety?)", .{}); | |
| 175 | // This should lower to 0xaa bytes in safe modes, and for unsafe modes should | |
| 176 | // lower to leaving variables uninitialized (that might need to be implemented | |
| 177 | // outside of this function). | |
| 178 | return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement renderValue undef", .{}); | |
| 164 | 179 | } |
| 165 | 180 | switch (t.zigTypeTag()) { |
| 166 | 181 | .Int => { |
| ... | ... | @@ -276,6 +291,31 @@ pub const DeclGen = struct { |
| 276 | 291 | try writer.writeAll(", .error = 0 }"); |
| 277 | 292 | } |
| 278 | 293 | }, |
| 294 | .Enum => { | |
| 295 | switch (val.tag()) { | |
| 296 | .enum_field_index => { | |
| 297 | const field_index = val.castTag(.enum_field_index).?.data; | |
| 298 | switch (t.tag()) { | |
| 299 | .enum_simple => return writer.print("{d}", .{field_index}), | |
| 300 | .enum_full, .enum_nonexhaustive => { | |
| 301 | const enum_full = t.cast(Type.Payload.EnumFull).?.data; | |
| 302 | if (enum_full.values.count() != 0) { | |
| 303 | const tag_val = enum_full.values.entries.items[field_index].key; | |
| 304 | return dg.renderValue(writer, enum_full.tag_ty, tag_val); | |
| 305 | } else { | |
| 306 | return writer.print("{d}", .{field_index}); | |
| 307 | } | |
| 308 | }, | |
| 309 | else => unreachable, | |
| 310 | } | |
| 311 | }, | |
| 312 | else => { | |
| 313 | var int_tag_ty_buffer: Type.Payload.Bits = undefined; | |
| 314 | const int_tag_ty = t.intTagType(&int_tag_ty_buffer); | |
| 315 | return dg.renderValue(writer, int_tag_ty, val); | |
| 316 | }, | |
| 317 | } | |
| 318 | }, | |
| 279 | 319 | else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{ |
| 280 | 320 | @tagName(e), |
| 281 | 321 | }), |
| ... | ... | @@ -356,6 +396,9 @@ pub const DeclGen = struct { |
| 356 | 396 | else => unreachable, |
| 357 | 397 | } |
| 358 | 398 | }, |
| 399 | ||
| 400 | .Float => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Float", .{}), | |
| 401 | ||
| 359 | 402 | .Pointer => { |
| 360 | 403 | if (t.isSlice()) { |
| 361 | 404 | return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement slices", .{}); |
| ... | ... | @@ -430,10 +473,59 @@ pub const DeclGen = struct { |
| 430 | 473 | try w.writeAll(name); |
| 431 | 474 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); |
| 432 | 475 | }, |
| 433 | .Null, .Undefined => unreachable, // must be const or comptime | |
| 434 | else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type {s}", .{ | |
| 435 | @tagName(e), | |
| 436 | }), | |
| 476 | .Struct => { | |
| 477 | if (dg.typedefs.get(t)) |some| { | |
| 478 | return w.writeAll(some.name); | |
| 479 | } | |
| 480 | const struct_obj = t.castTag(.@"struct").?.data; // Handle 0 bit types elsewhere. | |
| 481 | const fqn = try struct_obj.getFullyQualifiedName(dg.typedefs.allocator); | |
| 482 | defer dg.typedefs.allocator.free(fqn); | |
| 483 | ||
| 484 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | |
| 485 | defer buffer.deinit(); | |
| 486 | ||
| 487 | try buffer.appendSlice("typedef struct {\n"); | |
| 488 | for (struct_obj.fields.entries.items) |entry| { | |
| 489 | try buffer.append(' '); | |
| 490 | try dg.renderType(buffer.writer(), entry.value.ty); | |
| 491 | try buffer.writer().print(" {s};\n", .{fmtIdent(entry.key)}); | |
| 492 | } | |
| 493 | try buffer.appendSlice("} "); | |
| 494 | ||
| 495 | const name_start = buffer.items.len; | |
| 496 | try buffer.writer().print("zig_S_{s};\n", .{fmtIdent(fqn)}); | |
| 497 | ||
| 498 | const rendered = buffer.toOwnedSlice(); | |
| 499 | errdefer dg.typedefs.allocator.free(rendered); | |
| 500 | const name = rendered[name_start .. rendered.len - 2]; | |
| 501 | ||
| 502 | try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); | |
| 503 | try w.writeAll(name); | |
| 504 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | |
| 505 | }, | |
| 506 | .Enum => { | |
| 507 | // For enums, we simply use the integer tag type. | |
| 508 | var int_tag_ty_buffer: Type.Payload.Bits = undefined; | |
| 509 | const int_tag_ty = t.intTagType(&int_tag_ty_buffer); | |
| 510 | ||
| 511 | try dg.renderType(w, int_tag_ty); | |
| 512 | }, | |
| 513 | .Union => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Union", .{}), | |
| 514 | .Fn => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Fn", .{}), | |
| 515 | .Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}), | |
| 516 | .Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}), | |
| 517 | .AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}), | |
| 518 | .Vector => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Vector", .{}), | |
| 519 | ||
| 520 | .Null, | |
| 521 | .Undefined, | |
| 522 | .EnumLiteral, | |
| 523 | .ComptimeFloat, | |
| 524 | .ComptimeInt, | |
| 525 | .Type, | |
| 526 | => unreachable, // must be const or comptime | |
| 527 | ||
| 528 | .BoundFn => unreachable, // this type will be deleted from the language | |
| 437 | 529 | } |
| 438 | 530 | } |
| 439 | 531 | |
| ... | ... | @@ -525,8 +617,26 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi |
| 525 | 617 | |
| 526 | 618 | for (body.instructions) |inst| { |
| 527 | 619 | const result_value = switch (inst.tag) { |
| 528 | .constant => unreachable, // excluded from function bodies | |
| 620 | // TODO use a different strategy for add that communicates to the optimizer | |
| 621 | // that wrapping is UB. | |
| 529 | 622 | .add => try genBinOp(o, inst.castTag(.add).?, " + "), |
| 623 | // TODO make this do wrapping arithmetic for signed ints | |
| 624 | .addwrap => try genBinOp(o, inst.castTag(.add).?, " + "), | |
| 625 | // TODO use a different strategy for sub that communicates to the optimizer | |
| 626 | // that wrapping is UB. | |
| 627 | .sub => try genBinOp(o, inst.castTag(.sub).?, " - "), | |
| 628 | // TODO make this do wrapping arithmetic for signed ints | |
| 629 | .subwrap => try genBinOp(o, inst.castTag(.sub).?, " - "), | |
| 630 | // TODO use a different strategy for mul that communicates to the optimizer | |
| 631 | // that wrapping is UB. | |
| 632 | .mul => try genBinOp(o, inst.castTag(.sub).?, " * "), | |
| 633 | // TODO make this do wrapping multiplication for signed ints | |
| 634 | .mulwrap => try genBinOp(o, inst.castTag(.sub).?, " * "), | |
| 635 | // TODO use a different strategy for div that communicates to the optimizer | |
| 636 | // that wrapping is UB. | |
| 637 | .div => try genBinOp(o, inst.castTag(.div).?, " / "), | |
| 638 | ||
| 639 | .constant => unreachable, // excluded from function bodies | |
| 530 | 640 | .alloc => try genAlloc(o, inst.castTag(.alloc).?), |
| 531 | 641 | .arg => genArg(o), |
| 532 | 642 | .assembly => try genAsm(o, inst.castTag(.assembly).?), |
| ... | ... | @@ -546,7 +656,6 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi |
| 546 | 656 | .ret => try genRet(o, inst.castTag(.ret).?), |
| 547 | 657 | .retvoid => try genRetVoid(o), |
| 548 | 658 | .store => try genStore(o, inst.castTag(.store).?), |
| 549 | .sub => try genBinOp(o, inst.castTag(.sub).?, " - "), | |
| 550 | 659 | .unreach => try genUnreach(o, inst.castTag(.unreach).?), |
| 551 | 660 | .loop => try genLoop(o, inst.castTag(.loop).?), |
| 552 | 661 | .condbr => try genCondBr(o, inst.castTag(.condbr).?), |
| ... | ... | @@ -567,17 +676,24 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi |
| 567 | 676 | .wrap_optional => try genWrapOptional(o, inst.castTag(.wrap_optional).?), |
| 568 | 677 | .optional_payload => try genOptionalPayload(o, inst.castTag(.optional_payload).?), |
| 569 | 678 | .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?), |
| 679 | .ref => try genRef(o, inst.castTag(.ref).?), | |
| 680 | .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?), | |
| 681 | ||
| 570 | 682 | .is_err => try genIsErr(o, inst.castTag(.is_err).?), |
| 571 | 683 | .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?), |
| 572 | 684 | .error_to_int => try genErrorToInt(o, inst.castTag(.error_to_int).?), |
| 573 | 685 | .int_to_error => try genIntToError(o, inst.castTag(.int_to_error).?), |
| 686 | ||
| 574 | 687 | .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), |
| 575 | 688 | .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), |
| 576 | 689 | .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?), |
| 577 | 690 | .unwrap_errunion_err_ptr => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err_ptr).?), |
| 578 | 691 | .wrap_errunion_payload => try genWrapErrUnionPay(o, inst.castTag(.wrap_errunion_payload).?), |
| 579 | 692 | .wrap_errunion_err => try genWrapErrUnionErr(o, inst.castTag(.wrap_errunion_err).?), |
| 580 | else => |e| return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for {}", .{e}), | |
| 693 | .br_block_flat => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for br_block_flat", .{}), | |
| 694 | .ptrtoint => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for ptrtoint", .{}), | |
| 695 | .varptr => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for varptr", .{}), | |
| 696 | .floatcast => return o.dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement codegen for floatcast", .{}), | |
| 581 | 697 | }; |
| 582 | 698 | switch (result_value) { |
| 583 | 699 | .none => {}, |
| ... | ... | @@ -996,6 +1112,37 @@ fn genOptionalPayload(o: *Object, inst: *Inst.UnOp) !CValue { |
| 996 | 1112 | return local; |
| 997 | 1113 | } |
| 998 | 1114 | |
| 1115 | fn genRef(o: *Object, inst: *Inst.UnOp) !CValue { | |
| 1116 | const writer = o.writer(); | |
| 1117 | const operand = try o.resolveInst(inst.operand); | |
| 1118 | ||
| 1119 | const local = try o.allocLocal(inst.base.ty, .Const); | |
| 1120 | try writer.writeAll(" = "); | |
| 1121 | try o.writeCValue(writer, operand); | |
| 1122 | try writer.writeAll(";\n"); | |
| 1123 | return local; | |
| 1124 | } | |
| 1125 | ||
| 1126 | fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue { | |
| 1127 | const writer = o.writer(); | |
| 1128 | const struct_ptr = try o.resolveInst(inst.struct_ptr); | |
| 1129 | const struct_obj = inst.struct_ptr.ty.elemType().castTag(.@"struct").?.data; | |
| 1130 | const field_name = struct_obj.fields.entries.items[inst.field_index].key; | |
| 1131 | ||
| 1132 | const local = try o.allocLocal(inst.base.ty, .Const); | |
| 1133 | switch (struct_ptr) { | |
| 1134 | .local_ref => |i| { | |
| 1135 | try writer.print(" = &t{d}.{};\n", .{ i, fmtIdent(field_name) }); | |
| 1136 | }, | |
| 1137 | else => { | |
| 1138 | try writer.writeAll(" = &"); | |
| 1139 | try o.writeCValue(writer, struct_ptr); | |
| 1140 | try writer.print("->{};\n", .{fmtIdent(field_name)}); | |
| 1141 | }, | |
| 1142 | } | |
| 1143 | return local; | |
| 1144 | } | |
| 1145 | ||
| 999 | 1146 | // *(E!T) -> E NOT *E |
| 1000 | 1147 | fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue { |
| 1001 | 1148 | const writer = o.writer(); |
| ... | ... | @@ -1088,7 +1235,7 @@ fn IndentWriter(comptime UnderlyingWriter: type) type { |
| 1088 | 1235 | pub const Error = UnderlyingWriter.Error; |
| 1089 | 1236 | pub const Writer = std.io.Writer(*Self, Error, write); |
| 1090 | 1237 | |
| 1091 | pub const indent_delta = 4; | |
| 1238 | pub const indent_delta = 1; | |
| 1092 | 1239 | |
| 1093 | 1240 | underlying_writer: UnderlyingWriter, |
| 1094 | 1241 | indent_count: usize = 0, |
src/codegen/llvm.zig-1| ... | ... | @@ -76,7 +76,6 @@ pub fn targetTriple(allocator: *Allocator, target: std.Target) ![:0]u8 { |
| 76 | 76 | .spirv32 => return error.LLVMBackendDoesNotSupportSPIRV, |
| 77 | 77 | .spirv64 => return error.LLVMBackendDoesNotSupportSPIRV, |
| 78 | 78 | }; |
| 79 | // TODO Add a sub-arch for some architectures depending on CPU features. | |
| 80 | 79 | |
| 81 | 80 | const llvm_os = switch (target.os.tag) { |
| 82 | 81 | .freestanding => "unknown", |
src/codegen/wasm.zig+623-139| ... | ... | @@ -2,6 +2,7 @@ const std = @import("std"); |
| 2 | 2 | const Allocator = std.mem.Allocator; |
| 3 | 3 | const ArrayList = std.ArrayList; |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | const testing = std.testing; | |
| 5 | 6 | const leb = std.leb; |
| 6 | 7 | const mem = std.mem; |
| 7 | 8 | const wasm = std.wasm; |
| ... | ... | @@ -15,9 +16,12 @@ const Value = @import("../value.zig").Value; |
| 15 | 16 | const Compilation = @import("../Compilation.zig"); |
| 16 | 17 | const AnyMCValue = @import("../codegen.zig").AnyMCValue; |
| 17 | 18 | const LazySrcLoc = Module.LazySrcLoc; |
| 19 | const link = @import("../link.zig"); | |
| 20 | const TypedValue = @import("../TypedValue.zig"); | |
| 18 | 21 | |
| 19 | 22 | /// Wasm Value, created when generating an instruction |
| 20 | 23 | const WValue = union(enum) { |
| 24 | /// May be referenced but is unused | |
| 21 | 25 | none: void, |
| 22 | 26 | /// Index of the local variable |
| 23 | 27 | local: u32, |
| ... | ... | @@ -29,6 +33,450 @@ const WValue = union(enum) { |
| 29 | 33 | block_idx: u32, |
| 30 | 34 | }; |
| 31 | 35 | |
| 36 | /// Wasm ops, but without input/output/signedness information | |
| 37 | /// Used for `buildOpcode` | |
| 38 | const Op = enum { | |
| 39 | @"unreachable", | |
| 40 | nop, | |
| 41 | block, | |
| 42 | loop, | |
| 43 | @"if", | |
| 44 | @"else", | |
| 45 | end, | |
| 46 | br, | |
| 47 | br_if, | |
| 48 | br_table, | |
| 49 | @"return", | |
| 50 | call, | |
| 51 | call_indirect, | |
| 52 | drop, | |
| 53 | select, | |
| 54 | local_get, | |
| 55 | local_set, | |
| 56 | local_tee, | |
| 57 | global_get, | |
| 58 | global_set, | |
| 59 | load, | |
| 60 | store, | |
| 61 | memory_size, | |
| 62 | memory_grow, | |
| 63 | @"const", | |
| 64 | eqz, | |
| 65 | eq, | |
| 66 | ne, | |
| 67 | lt, | |
| 68 | gt, | |
| 69 | le, | |
| 70 | ge, | |
| 71 | clz, | |
| 72 | ctz, | |
| 73 | popcnt, | |
| 74 | add, | |
| 75 | sub, | |
| 76 | mul, | |
| 77 | div, | |
| 78 | rem, | |
| 79 | @"and", | |
| 80 | @"or", | |
| 81 | xor, | |
| 82 | shl, | |
| 83 | shr, | |
| 84 | rotl, | |
| 85 | rotr, | |
| 86 | abs, | |
| 87 | neg, | |
| 88 | ceil, | |
| 89 | floor, | |
| 90 | trunc, | |
| 91 | nearest, | |
| 92 | sqrt, | |
| 93 | min, | |
| 94 | max, | |
| 95 | copysign, | |
| 96 | wrap, | |
| 97 | convert, | |
| 98 | demote, | |
| 99 | promote, | |
| 100 | reinterpret, | |
| 101 | extend, | |
| 102 | }; | |
| 103 | ||
| 104 | /// Contains the settings needed to create an `Opcode` using `buildOpcode`. | |
| 105 | /// | |
| 106 | /// The fields correspond to the opcode name. Here is an example | |
| 107 | /// i32_trunc_f32_s | |
| 108 | /// ^ ^ ^ ^ | |
| 109 | /// | | | | | |
| 110 | /// valtype1 | | | | |
| 111 | /// = .i32 | | | | |
| 112 | /// | | | | |
| 113 | /// op | | | |
| 114 | /// = .trunc | | | |
| 115 | /// | | | |
| 116 | /// valtype2 | | |
| 117 | /// = .f32 | | |
| 118 | /// | | |
| 119 | /// width | | |
| 120 | /// = null | | |
| 121 | /// | | |
| 122 | /// signed | |
| 123 | /// = true | |
| 124 | /// | |
| 125 | /// There can be missing fields, here are some more examples: | |
| 126 | /// i64_load8_u | |
| 127 | /// --> .{ .valtype1 = .i64, .op = .load, .width = 8, signed = false } | |
| 128 | /// i32_mul | |
| 129 | /// --> .{ .valtype1 = .i32, .op = .trunc } | |
| 130 | /// nop | |
| 131 | /// --> .{ .op = .nop } | |
| 132 | const OpcodeBuildArguments = struct { | |
| 133 | /// First valtype in the opcode (usually represents the type of the output) | |
| 134 | valtype1: ?wasm.Valtype = null, | |
| 135 | /// The operation (e.g. call, unreachable, div, min, sqrt, etc.) | |
| 136 | op: Op, | |
| 137 | /// Width of the operation (e.g. 8 for i32_load8_s, 16 for i64_extend16_i32_s) | |
| 138 | width: ?u8 = null, | |
| 139 | /// Second valtype in the opcode name (usually represents the type of the input) | |
| 140 | valtype2: ?wasm.Valtype = null, | |
| 141 | /// Signedness of the op | |
| 142 | signedness: ?std.builtin.Signedness = null, | |
| 143 | }; | |
| 144 | ||
| 145 | /// Helper function that builds an Opcode given the arguments needed | |
| 146 | fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | |
| 147 | switch (args.op) { | |
| 148 | .@"unreachable" => return .@"unreachable", | |
| 149 | .nop => return .nop, | |
| 150 | .block => return .block, | |
| 151 | .loop => return .loop, | |
| 152 | .@"if" => return .@"if", | |
| 153 | .@"else" => return .@"else", | |
| 154 | .end => return .end, | |
| 155 | .br => return .br, | |
| 156 | .br_if => return .br_if, | |
| 157 | .br_table => return .br_table, | |
| 158 | .@"return" => return .@"return", | |
| 159 | .call => return .call, | |
| 160 | .call_indirect => return .call_indirect, | |
| 161 | .drop => return .drop, | |
| 162 | .select => return .select, | |
| 163 | .local_get => return .local_get, | |
| 164 | .local_set => return .local_set, | |
| 165 | .local_tee => return .local_tee, | |
| 166 | .global_get => return .global_get, | |
| 167 | .global_set => return .global_set, | |
| 168 | ||
| 169 | .load => if (args.width) |width| switch (width) { | |
| 170 | 8 => switch (args.valtype1.?) { | |
| 171 | .i32 => if (args.signedness.? == .signed) return .i32_load8_s else return .i32_load8_u, | |
| 172 | .i64 => if (args.signedness.? == .signed) return .i64_load8_s else return .i64_load8_u, | |
| 173 | .f32, .f64 => unreachable, | |
| 174 | }, | |
| 175 | 16 => switch (args.valtype1.?) { | |
| 176 | .i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u, | |
| 177 | .i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u, | |
| 178 | .f32, .f64 => unreachable, | |
| 179 | }, | |
| 180 | 32 => switch (args.valtype1.?) { | |
| 181 | .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u, | |
| 182 | .i32, .f32, .f64 => unreachable, | |
| 183 | }, | |
| 184 | else => unreachable, | |
| 185 | } else switch (args.valtype1.?) { | |
| 186 | .i32 => return .i32_load, | |
| 187 | .i64 => return .i64_load, | |
| 188 | .f32 => return .f32_load, | |
| 189 | .f64 => return .f64_load, | |
| 190 | }, | |
| 191 | .store => if (args.width) |width| { | |
| 192 | switch (width) { | |
| 193 | 8 => switch (args.valtype1.?) { | |
| 194 | .i32 => return .i32_store8, | |
| 195 | .i64 => return .i64_store8, | |
| 196 | .f32, .f64 => unreachable, | |
| 197 | }, | |
| 198 | 16 => switch (args.valtype1.?) { | |
| 199 | .i32 => return .i32_store16, | |
| 200 | .i64 => return .i64_store16, | |
| 201 | .f32, .f64 => unreachable, | |
| 202 | }, | |
| 203 | 32 => switch (args.valtype1.?) { | |
| 204 | .i64 => return .i64_store32, | |
| 205 | .i32, .f32, .f64 => unreachable, | |
| 206 | }, | |
| 207 | else => unreachable, | |
| 208 | } | |
| 209 | } else { | |
| 210 | switch (args.valtype1.?) { | |
| 211 | .i32 => return .i32_store, | |
| 212 | .i64 => return .i64_store, | |
| 213 | .f32 => return .f32_store, | |
| 214 | .f64 => return .f64_store, | |
| 215 | } | |
| 216 | }, | |
| 217 | ||
| 218 | .memory_size => return .memory_size, | |
| 219 | .memory_grow => return .memory_grow, | |
| 220 | ||
| 221 | .@"const" => switch (args.valtype1.?) { | |
| 222 | .i32 => return .i32_const, | |
| 223 | .i64 => return .i64_const, | |
| 224 | .f32 => return .f32_const, | |
| 225 | .f64 => return .f64_const, | |
| 226 | }, | |
| 227 | ||
| 228 | .eqz => switch (args.valtype1.?) { | |
| 229 | .i32 => return .i32_eqz, | |
| 230 | .i64 => return .i64_eqz, | |
| 231 | .f32, .f64 => unreachable, | |
| 232 | }, | |
| 233 | .eq => switch (args.valtype1.?) { | |
| 234 | .i32 => return .i32_eq, | |
| 235 | .i64 => return .i64_eq, | |
| 236 | .f32 => return .f32_eq, | |
| 237 | .f64 => return .f64_eq, | |
| 238 | }, | |
| 239 | .ne => switch (args.valtype1.?) { | |
| 240 | .i32 => return .i32_ne, | |
| 241 | .i64 => return .i64_ne, | |
| 242 | .f32 => return .f32_ne, | |
| 243 | .f64 => return .f64_ne, | |
| 244 | }, | |
| 245 | ||
| 246 | .lt => switch (args.valtype1.?) { | |
| 247 | .i32 => if (args.signedness.? == .signed) return .i32_lt_s else return .i32_lt_u, | |
| 248 | .i64 => if (args.signedness.? == .signed) return .i64_lt_s else return .i64_lt_u, | |
| 249 | .f32 => return .f32_lt, | |
| 250 | .f64 => return .f64_lt, | |
| 251 | }, | |
| 252 | .gt => switch (args.valtype1.?) { | |
| 253 | .i32 => if (args.signedness.? == .signed) return .i32_gt_s else return .i32_gt_u, | |
| 254 | .i64 => if (args.signedness.? == .signed) return .i64_gt_s else return .i64_gt_u, | |
| 255 | .f32 => return .f32_gt, | |
| 256 | .f64 => return .f64_gt, | |
| 257 | }, | |
| 258 | .le => switch (args.valtype1.?) { | |
| 259 | .i32 => if (args.signedness.? == .signed) return .i32_le_s else return .i32_le_u, | |
| 260 | .i64 => if (args.signedness.? == .signed) return .i64_le_s else return .i64_le_u, | |
| 261 | .f32 => return .f32_le, | |
| 262 | .f64 => return .f64_le, | |
| 263 | }, | |
| 264 | .ge => switch (args.valtype1.?) { | |
| 265 | .i32 => if (args.signedness.? == .signed) return .i32_ge_s else return .i32_ge_u, | |
| 266 | .i64 => if (args.signedness.? == .signed) return .i64_ge_s else return .i64_ge_u, | |
| 267 | .f32 => return .f32_ge, | |
| 268 | .f64 => return .f64_ge, | |
| 269 | }, | |
| 270 | ||
| 271 | .clz => switch (args.valtype1.?) { | |
| 272 | .i32 => return .i32_clz, | |
| 273 | .i64 => return .i64_clz, | |
| 274 | .f32, .f64 => unreachable, | |
| 275 | }, | |
| 276 | .ctz => switch (args.valtype1.?) { | |
| 277 | .i32 => return .i32_ctz, | |
| 278 | .i64 => return .i64_ctz, | |
| 279 | .f32, .f64 => unreachable, | |
| 280 | }, | |
| 281 | .popcnt => switch (args.valtype1.?) { | |
| 282 | .i32 => return .i32_popcnt, | |
| 283 | .i64 => return .i64_popcnt, | |
| 284 | .f32, .f64 => unreachable, | |
| 285 | }, | |
| 286 | ||
| 287 | .add => switch (args.valtype1.?) { | |
| 288 | .i32 => return .i32_add, | |
| 289 | .i64 => return .i64_add, | |
| 290 | .f32 => return .f32_add, | |
| 291 | .f64 => return .f64_add, | |
| 292 | }, | |
| 293 | .sub => switch (args.valtype1.?) { | |
| 294 | .i32 => return .i32_sub, | |
| 295 | .i64 => return .i64_sub, | |
| 296 | .f32 => return .f32_sub, | |
| 297 | .f64 => return .f64_sub, | |
| 298 | }, | |
| 299 | .mul => switch (args.valtype1.?) { | |
| 300 | .i32 => return .i32_mul, | |
| 301 | .i64 => return .i64_mul, | |
| 302 | .f32 => return .f32_mul, | |
| 303 | .f64 => return .f64_mul, | |
| 304 | }, | |
| 305 | ||
| 306 | .div => switch (args.valtype1.?) { | |
| 307 | .i32 => if (args.signedness.? == .signed) return .i32_div_s else return .i32_div_u, | |
| 308 | .i64 => if (args.signedness.? == .signed) return .i64_div_s else return .i64_div_u, | |
| 309 | .f32 => return .f32_div, | |
| 310 | .f64 => return .f64_div, | |
| 311 | }, | |
| 312 | .rem => switch (args.valtype1.?) { | |
| 313 | .i32 => if (args.signedness.? == .signed) return .i32_rem_s else return .i32_rem_u, | |
| 314 | .i64 => if (args.signedness.? == .signed) return .i64_rem_s else return .i64_rem_u, | |
| 315 | .f32, .f64 => unreachable, | |
| 316 | }, | |
| 317 | ||
| 318 | .@"and" => switch (args.valtype1.?) { | |
| 319 | .i32 => return .i32_and, | |
| 320 | .i64 => return .i64_and, | |
| 321 | .f32, .f64 => unreachable, | |
| 322 | }, | |
| 323 | .@"or" => switch (args.valtype1.?) { | |
| 324 | .i32 => return .i32_or, | |
| 325 | .i64 => return .i64_or, | |
| 326 | .f32, .f64 => unreachable, | |
| 327 | }, | |
| 328 | .xor => switch (args.valtype1.?) { | |
| 329 | .i32 => return .i32_xor, | |
| 330 | .i64 => return .i64_xor, | |
| 331 | .f32, .f64 => unreachable, | |
| 332 | }, | |
| 333 | ||
| 334 | .shl => switch (args.valtype1.?) { | |
| 335 | .i32 => return .i32_shl, | |
| 336 | .i64 => return .i64_shl, | |
| 337 | .f32, .f64 => unreachable, | |
| 338 | }, | |
| 339 | .shr => switch (args.valtype1.?) { | |
| 340 | .i32 => if (args.signedness.? == .signed) return .i32_shr_s else return .i32_shr_u, | |
| 341 | .i64 => if (args.signedness.? == .signed) return .i64_shr_s else return .i64_shr_u, | |
| 342 | .f32, .f64 => unreachable, | |
| 343 | }, | |
| 344 | .rotl => switch (args.valtype1.?) { | |
| 345 | .i32 => return .i32_rotl, | |
| 346 | .i64 => return .i64_rotl, | |
| 347 | .f32, .f64 => unreachable, | |
| 348 | }, | |
| 349 | .rotr => switch (args.valtype1.?) { | |
| 350 | .i32 => return .i32_rotr, | |
| 351 | .i64 => return .i64_rotr, | |
| 352 | .f32, .f64 => unreachable, | |
| 353 | }, | |
| 354 | ||
| 355 | .abs => switch (args.valtype1.?) { | |
| 356 | .i32, .i64 => unreachable, | |
| 357 | .f32 => return .f32_abs, | |
| 358 | .f64 => return .f64_abs, | |
| 359 | }, | |
| 360 | .neg => switch (args.valtype1.?) { | |
| 361 | .i32, .i64 => unreachable, | |
| 362 | .f32 => return .f32_neg, | |
| 363 | .f64 => return .f64_neg, | |
| 364 | }, | |
| 365 | .ceil => switch (args.valtype1.?) { | |
| 366 | .i32, .i64 => unreachable, | |
| 367 | .f32 => return .f32_ceil, | |
| 368 | .f64 => return .f64_ceil, | |
| 369 | }, | |
| 370 | .floor => switch (args.valtype1.?) { | |
| 371 | .i32, .i64 => unreachable, | |
| 372 | .f32 => return .f32_floor, | |
| 373 | .f64 => return .f64_floor, | |
| 374 | }, | |
| 375 | .trunc => switch (args.valtype1.?) { | |
| 376 | .i32 => switch (args.valtype2.?) { | |
| 377 | .i32 => unreachable, | |
| 378 | .i64 => unreachable, | |
| 379 | .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u, | |
| 380 | .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u, | |
| 381 | }, | |
| 382 | .i64 => unreachable, | |
| 383 | .f32 => return .f32_trunc, | |
| 384 | .f64 => return .f64_trunc, | |
| 385 | }, | |
| 386 | .nearest => switch (args.valtype1.?) { | |
| 387 | .i32, .i64 => unreachable, | |
| 388 | .f32 => return .f32_nearest, | |
| 389 | .f64 => return .f64_nearest, | |
| 390 | }, | |
| 391 | .sqrt => switch (args.valtype1.?) { | |
| 392 | .i32, .i64 => unreachable, | |
| 393 | .f32 => return .f32_sqrt, | |
| 394 | .f64 => return .f64_sqrt, | |
| 395 | }, | |
| 396 | .min => switch (args.valtype1.?) { | |
| 397 | .i32, .i64 => unreachable, | |
| 398 | .f32 => return .f32_min, | |
| 399 | .f64 => return .f64_min, | |
| 400 | }, | |
| 401 | .max => switch (args.valtype1.?) { | |
| 402 | .i32, .i64 => unreachable, | |
| 403 | .f32 => return .f32_max, | |
| 404 | .f64 => return .f64_max, | |
| 405 | }, | |
| 406 | .copysign => switch (args.valtype1.?) { | |
| 407 | .i32, .i64 => unreachable, | |
| 408 | .f32 => return .f32_copysign, | |
| 409 | .f64 => return .f64_copysign, | |
| 410 | }, | |
| 411 | ||
| 412 | .wrap => switch (args.valtype1.?) { | |
| 413 | .i32 => switch (args.valtype2.?) { | |
| 414 | .i32 => unreachable, | |
| 415 | .i64 => return .i32_wrap_i64, | |
| 416 | .f32, .f64 => unreachable, | |
| 417 | }, | |
| 418 | .i64, .f32, .f64 => unreachable, | |
| 419 | }, | |
| 420 | .convert => switch (args.valtype1.?) { | |
| 421 | .i32, .i64 => unreachable, | |
| 422 | .f32 => switch (args.valtype2.?) { | |
| 423 | .i32 => if (args.signedness.? == .signed) return .f32_convert_i32_s else return .f32_convert_i32_u, | |
| 424 | .i64 => if (args.signedness.? == .signed) return .f32_convert_i64_s else return .f32_convert_i64_u, | |
| 425 | .f32, .f64 => unreachable, | |
| 426 | }, | |
| 427 | .f64 => switch (args.valtype2.?) { | |
| 428 | .i32 => if (args.signedness.? == .signed) return .f64_convert_i32_s else return .f64_convert_i32_u, | |
| 429 | .i64 => if (args.signedness.? == .signed) return .f64_convert_i64_s else return .f64_convert_i64_u, | |
| 430 | .f32, .f64 => unreachable, | |
| 431 | }, | |
| 432 | }, | |
| 433 | .demote => if (args.valtype1.? == .f32 and args.valtype2.? == .f64) return .f32_demote_f64 else unreachable, | |
| 434 | .promote => if (args.valtype1.? == .f64 and args.valtype2.? == .f32) return .f64_promote_f32 else unreachable, | |
| 435 | .reinterpret => switch (args.valtype1.?) { | |
| 436 | .i32 => if (args.valtype2.? == .f32) return .i32_reinterpret_f32 else unreachable, | |
| 437 | .i64 => if (args.valtype2.? == .f64) return .i64_reinterpret_f64 else unreachable, | |
| 438 | .f32 => if (args.valtype2.? == .i32) return .f32_reinterpret_i32 else unreachable, | |
| 439 | .f64 => if (args.valtype2.? == .i64) return .f64_reinterpret_i64 else unreachable, | |
| 440 | }, | |
| 441 | .extend => switch (args.valtype1.?) { | |
| 442 | .i32 => switch (args.width.?) { | |
| 443 | 8 => if (args.signedness.? == .signed) return .i32_extend8_s else unreachable, | |
| 444 | 16 => if (args.signedness.? == .signed) return .i32_extend16_s else unreachable, | |
| 445 | else => unreachable, | |
| 446 | }, | |
| 447 | .i64 => switch (args.width.?) { | |
| 448 | 8 => if (args.signedness.? == .signed) return .i64_extend8_s else unreachable, | |
| 449 | 16 => if (args.signedness.? == .signed) return .i64_extend16_s else unreachable, | |
| 450 | 32 => if (args.signedness.? == .signed) return .i64_extend32_s else unreachable, | |
| 451 | else => unreachable, | |
| 452 | }, | |
| 453 | .f32, .f64 => unreachable, | |
| 454 | }, | |
| 455 | } | |
| 456 | } | |
| 457 | ||
| 458 | test "Wasm - buildOpcode" { | |
| 459 | // Make sure buildOpcode is referenced, and test some examples | |
| 460 | const i32_const = buildOpcode(.{ .op = .@"const", .valtype1 = .i32 }); | |
| 461 | const end = buildOpcode(.{ .op = .end }); | |
| 462 | const local_get = buildOpcode(.{ .op = .local_get }); | |
| 463 | const i64_extend32_s = buildOpcode(.{ .op = .extend, .valtype1 = .i64, .width = 32, .signedness = .signed }); | |
| 464 | const f64_reinterpret_i64 = buildOpcode(.{ .op = .reinterpret, .valtype1 = .f64, .valtype2 = .i64 }); | |
| 465 | ||
| 466 | testing.expectEqual(@as(wasm.Opcode, .i32_const), i32_const); | |
| 467 | testing.expectEqual(@as(wasm.Opcode, .end), end); | |
| 468 | testing.expectEqual(@as(wasm.Opcode, .local_get), local_get); | |
| 469 | testing.expectEqual(@as(wasm.Opcode, .i64_extend32_s), i64_extend32_s); | |
| 470 | testing.expectEqual(@as(wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64); | |
| 471 | } | |
| 472 | ||
| 473 | pub const Result = union(enum) { | |
| 474 | /// The codegen bytes have been appended to `Context.code` | |
| 475 | appended: void, | |
| 476 | /// The data is managed externally and are part of the `Result` | |
| 477 | externally_managed: []const u8, | |
| 478 | }; | |
| 479 | ||
| 32 | 480 | /// Hashmap to store generated `WValue` for each `Inst` |
| 33 | 481 | pub const ValueTable = std.AutoHashMapUnmanaged(*Inst, WValue); |
| 34 | 482 | |
| ... | ... | @@ -58,10 +506,14 @@ pub const Context = struct { |
| 58 | 506 | /// List of all locals' types generated throughout this declaration |
| 59 | 507 | /// used to emit locals count at start of 'code' section. |
| 60 | 508 | locals: std.ArrayListUnmanaged(u8), |
| 509 | /// The Target we're emitting (used to call intInfo) | |
| 510 | target: std.Target, | |
| 61 | 511 | |
| 62 | 512 | const InnerError = error{ |
| 63 | 513 | OutOfMemory, |
| 64 | 514 | CodegenFail, |
| 515 | /// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed | |
| 516 | AnalysisFail, | |
| 65 | 517 | }; |
| 66 | 518 | |
| 67 | 519 | pub fn deinit(self: *Context) void { |
| ... | ... | @@ -89,17 +541,31 @@ pub const Context = struct { |
| 89 | 541 | return self.values.get(inst).?; // Instruction does not dominate all uses! |
| 90 | 542 | } |
| 91 | 543 | |
| 92 | /// Using a given `Type`, returns the corresponding wasm value type | |
| 93 | fn genValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 { | |
| 94 | return switch (ty.tag()) { | |
| 95 | .f32 => wasm.valtype(.f32), | |
| 96 | .f64 => wasm.valtype(.f64), | |
| 97 | .u32, .i32, .bool => wasm.valtype(.i32), | |
| 98 | .u64, .i64 => wasm.valtype(.i64), | |
| 99 | else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}), | |
| 544 | /// Using a given `Type`, returns the corresponding wasm Valtype | |
| 545 | fn typeToValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!wasm.Valtype { | |
| 546 | return switch (ty.zigTypeTag()) { | |
| 547 | .Float => blk: { | |
| 548 | const bits = ty.floatBits(self.target); | |
| 549 | if (bits == 16 or bits == 32) break :blk wasm.Valtype.f32; | |
| 550 | if (bits == 64) break :blk wasm.Valtype.f64; | |
| 551 | return self.fail(src, "Float bit size not supported by wasm: '{d}'", .{bits}); | |
| 552 | }, | |
| 553 | .Int => blk: { | |
| 554 | const info = ty.intInfo(self.target); | |
| 555 | if (info.bits <= 32) break :blk wasm.Valtype.i32; | |
| 556 | if (info.bits > 32 and info.bits <= 64) break :blk wasm.Valtype.i64; | |
| 557 | return self.fail(src, "Integer bit size not supported by wasm: '{d}'", .{info.bits}); | |
| 558 | }, | |
| 559 | .Bool, .Pointer => wasm.Valtype.i32, | |
| 560 | else => self.fail(src, "TODO - Wasm valtype for type '{s}'", .{ty.tag()}), | |
| 100 | 561 | }; |
| 101 | 562 | } |
| 102 | 563 | |
| 564 | /// Using a given `Type`, returns the byte representation of its wasm value type | |
| 565 | fn genValtype(self: *Context, src: LazySrcLoc, ty: Type) InnerError!u8 { | |
| 566 | return wasm.valtype(try self.typeToValtype(src, ty)); | |
| 567 | } | |
| 568 | ||
| 103 | 569 | /// Using a given `Type`, returns the corresponding wasm value type |
| 104 | 570 | /// Differently from `genValtype` this also allows `void` to create a block |
| 105 | 571 | /// with no return type |
| ... | ... | @@ -157,59 +623,97 @@ pub const Context = struct { |
| 157 | 623 | } |
| 158 | 624 | |
| 159 | 625 | /// Generates the wasm bytecode for the function declaration belonging to `Context` |
| 160 | pub fn gen(self: *Context) InnerError!void { | |
| 161 | assert(self.code.items.len == 0); | |
| 162 | try self.genFunctype(); | |
| 163 | ||
| 164 | // Write instructions | |
| 165 | // TODO: check for and handle death of instructions | |
| 166 | const tv = self.decl.typed_value.most_recent.typed_value; | |
| 167 | const mod_fn = blk: { | |
| 168 | if (tv.val.castTag(.function)) |func| break :blk func.data; | |
| 169 | if (tv.val.castTag(.extern_fn)) |ext_fn| return; // don't need codegen for extern functions | |
| 170 | return self.fail(.{ .node_offset = 0 }, "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()}); | |
| 171 | }; | |
| 172 | ||
| 173 | // Reserve space to write the size after generating the code as well as space for locals count | |
| 174 | try self.code.resize(10); | |
| 175 | ||
| 176 | try self.genBody(mod_fn.body); | |
| 177 | ||
| 178 | // finally, write our local types at the 'offset' position | |
| 179 | { | |
| 180 | leb.writeUnsignedFixed(5, self.code.items[5..10], @intCast(u32, self.locals.items.len)); | |
| 181 | ||
| 182 | // offset into 'code' section where we will put our locals types | |
| 183 | var local_offset: usize = 10; | |
| 184 | ||
| 185 | // emit the actual locals amount | |
| 186 | for (self.locals.items) |local| { | |
| 187 | var buf: [6]u8 = undefined; | |
| 188 | leb.writeUnsignedFixed(5, buf[0..5], @as(u32, 1)); | |
| 189 | buf[5] = local; | |
| 190 | try self.code.insertSlice(local_offset, &buf); | |
| 191 | local_offset += 6; | |
| 192 | } | |
| 626 | pub fn gen(self: *Context, typed_value: TypedValue) InnerError!Result { | |
| 627 | switch (typed_value.ty.zigTypeTag()) { | |
| 628 | .Fn => { | |
| 629 | try self.genFunctype(); | |
| 630 | ||
| 631 | // Write instructions | |
| 632 | // TODO: check for and handle death of instructions | |
| 633 | const mod_fn = blk: { | |
| 634 | if (typed_value.val.castTag(.function)) |func| break :blk func.data; | |
| 635 | if (typed_value.val.castTag(.extern_fn)) |ext_fn| return Result.appended; // don't need code body for extern functions | |
| 636 | unreachable; | |
| 637 | }; | |
| 638 | ||
| 639 | // Reserve space to write the size after generating the code as well as space for locals count | |
| 640 | try self.code.resize(10); | |
| 641 | ||
| 642 | try self.genBody(mod_fn.body); | |
| 643 | ||
| 644 | // finally, write our local types at the 'offset' position | |
| 645 | { | |
| 646 | leb.writeUnsignedFixed(5, self.code.items[5..10], @intCast(u32, self.locals.items.len)); | |
| 647 | ||
| 648 | // offset into 'code' section where we will put our locals types | |
| 649 | var local_offset: usize = 10; | |
| 650 | ||
| 651 | // emit the actual locals amount | |
| 652 | for (self.locals.items) |local| { | |
| 653 | var buf: [6]u8 = undefined; | |
| 654 | leb.writeUnsignedFixed(5, buf[0..5], @as(u32, 1)); | |
| 655 | buf[5] = local; | |
| 656 | try self.code.insertSlice(local_offset, &buf); | |
| 657 | local_offset += 6; | |
| 658 | } | |
| 659 | } | |
| 660 | ||
| 661 | const writer = self.code.writer(); | |
| 662 | try writer.writeByte(wasm.opcode(.end)); | |
| 663 | ||
| 664 | // Fill in the size of the generated code to the reserved space at the | |
| 665 | // beginning of the buffer. | |
| 666 | const size = self.code.items.len - 5 + self.decl.fn_link.wasm.idx_refs.items.len * 5; | |
| 667 | leb.writeUnsignedFixed(5, self.code.items[0..5], @intCast(u32, size)); | |
| 668 | ||
| 669 | // codegen data has been appended to `code` | |
| 670 | return Result.appended; | |
| 671 | }, | |
| 672 | .Array => { | |
| 673 | if (typed_value.val.castTag(.bytes)) |payload| { | |
| 674 | if (typed_value.ty.sentinel()) |sentinel| { | |
| 675 | try self.code.appendSlice(payload.data); | |
| 676 | ||
| 677 | switch (try self.gen(.{ | |
| 678 | .ty = typed_value.ty.elemType(), | |
| 679 | .val = sentinel, | |
| 680 | })) { | |
| 681 | .appended => return Result.appended, | |
| 682 | .externally_managed => |data| { | |
| 683 | try self.code.appendSlice(data); | |
| 684 | return Result.appended; | |
| 685 | }, | |
| 686 | } | |
| 687 | } | |
| 688 | return Result{ .externally_managed = payload.data }; | |
| 689 | } else return self.fail(.{ .node_offset = 0 }, "TODO implement gen for more kinds of arrays", .{}); | |
| 690 | }, | |
| 691 | .Int => { | |
| 692 | const info = typed_value.ty.intInfo(self.target); | |
| 693 | if (info.bits == 8 and info.signedness == .unsigned) { | |
| 694 | const int_byte = typed_value.val.toUnsignedInt(); | |
| 695 | try self.code.append(@intCast(u8, int_byte)); | |
| 696 | return Result.appended; | |
| 697 | } | |
| 698 | return self.fail(.{ .node_offset = 0 }, "TODO: Implement codegen for int type: '{}'", .{typed_value.ty}); | |
| 699 | }, | |
| 700 | else => |tag| return self.fail(.{ .node_offset = 0 }, "TODO: Implement zig type codegen for type: '{s}'", .{tag}), | |
| 193 | 701 | } |
| 194 | ||
| 195 | const writer = self.code.writer(); | |
| 196 | try writer.writeByte(wasm.opcode(.end)); | |
| 197 | ||
| 198 | // Fill in the size of the generated code to the reserved space at the | |
| 199 | // beginning of the buffer. | |
| 200 | const size = self.code.items.len - 5 + self.decl.fn_link.wasm.?.idx_refs.items.len * 5; | |
| 201 | leb.writeUnsignedFixed(5, self.code.items[0..5], @intCast(u32, size)); | |
| 202 | 702 | } |
| 203 | 703 | |
| 204 | 704 | fn genInst(self: *Context, inst: *Inst) InnerError!WValue { |
| 205 | 705 | return switch (inst.tag) { |
| 206 | .add => self.genAdd(inst.castTag(.add).?), | |
| 706 | .add => self.genBinOp(inst.castTag(.add).?, .add), | |
| 207 | 707 | .alloc => self.genAlloc(inst.castTag(.alloc).?), |
| 208 | 708 | .arg => self.genArg(inst.castTag(.arg).?), |
| 209 | 709 | .block => self.genBlock(inst.castTag(.block).?), |
| 210 | 710 | .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?), |
| 211 | 711 | .br => self.genBr(inst.castTag(.br).?), |
| 212 | 712 | .call => self.genCall(inst.castTag(.call).?), |
| 713 | .bit_or => self.genBinOp(inst.castTag(.bit_or).?, .@"or"), | |
| 714 | .bit_and => self.genBinOp(inst.castTag(.bit_and).?, .@"and"), | |
| 715 | .bool_or => self.genBinOp(inst.castTag(.bool_or).?, .@"or"), | |
| 716 | .bool_and => self.genBinOp(inst.castTag(.bool_and).?, .@"and"), | |
| 213 | 717 | .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq), |
| 214 | 718 | .cmp_gte => self.genCmp(inst.castTag(.cmp_gte).?, .gte), |
| 215 | 719 | .cmp_gt => self.genCmp(inst.castTag(.cmp_gt).?, .gt), |
| ... | ... | @@ -221,10 +725,14 @@ pub const Context = struct { |
| 221 | 725 | .dbg_stmt => WValue.none, |
| 222 | 726 | .load => self.genLoad(inst.castTag(.load).?), |
| 223 | 727 | .loop => self.genLoop(inst.castTag(.loop).?), |
| 728 | .mul => self.genBinOp(inst.castTag(.mul).?, .mul), | |
| 729 | .div => self.genBinOp(inst.castTag(.div).?, .div), | |
| 730 | .xor => self.genBinOp(inst.castTag(.xor).?, .xor), | |
| 224 | 731 | .not => self.genNot(inst.castTag(.not).?), |
| 225 | 732 | .ret => self.genRet(inst.castTag(.ret).?), |
| 226 | 733 | .retvoid => WValue.none, |
| 227 | 734 | .store => self.genStore(inst.castTag(.store).?), |
| 735 | .sub => self.genBinOp(inst.castTag(.sub).?, .sub), | |
| 228 | 736 | .unreach => self.genUnreachable(inst.castTag(.unreach).?), |
| 229 | 737 | else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}), |
| 230 | 738 | }; |
| ... | ... | @@ -266,7 +774,7 @@ pub const Context = struct { |
| 266 | 774 | |
| 267 | 775 | // The function index immediate argument will be filled in using this data |
| 268 | 776 | // in link.Wasm.flush(). |
| 269 | try self.decl.fn_link.wasm.?.idx_refs.append(self.gpa, .{ | |
| 777 | try self.decl.fn_link.wasm.idx_refs.append(self.gpa, .{ | |
| 270 | 778 | .offset = @intCast(u32, self.code.items.len), |
| 271 | 779 | .decl = target, |
| 272 | 780 | }); |
| ... | ... | @@ -305,56 +813,76 @@ pub const Context = struct { |
| 305 | 813 | return WValue{ .local = self.local_index }; |
| 306 | 814 | } |
| 307 | 815 | |
| 308 | fn genAdd(self: *Context, inst: *Inst.BinOp) InnerError!WValue { | |
| 816 | fn genBinOp(self: *Context, inst: *Inst.BinOp, op: Op) InnerError!WValue { | |
| 309 | 817 | const lhs = self.resolveInst(inst.lhs); |
| 310 | 818 | const rhs = self.resolveInst(inst.rhs); |
| 311 | 819 | |
| 312 | 820 | try self.emitWValue(lhs); |
| 313 | 821 | try self.emitWValue(rhs); |
| 314 | 822 | |
| 315 | const opcode: wasm.Opcode = switch (inst.base.ty.tag()) { | |
| 316 | .u32, .i32 => .i32_add, | |
| 317 | .u64, .i64 => .i64_add, | |
| 318 | .f32 => .f32_add, | |
| 319 | .f64 => .f64_add, | |
| 320 | else => return self.fail(inst.base.src, "TODO - Implement wasm genAdd for type '{s}'", .{inst.base.ty.tag()}), | |
| 321 | }; | |
| 322 | ||
| 823 | const opcode: wasm.Opcode = buildOpcode(.{ | |
| 824 | .op = op, | |
| 825 | .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty), | |
| 826 | .signedness = if (inst.base.ty.isSignedInt()) .signed else .unsigned, | |
| 827 | }); | |
| 323 | 828 | try self.code.append(wasm.opcode(opcode)); |
| 324 | 829 | return .none; |
| 325 | 830 | } |
| 326 | 831 | |
| 327 | 832 | fn emitConstant(self: *Context, inst: *Inst.Constant) InnerError!void { |
| 328 | 833 | const writer = self.code.writer(); |
| 329 | switch (inst.base.ty.tag()) { | |
| 330 | .u32 => { | |
| 331 | try writer.writeByte(wasm.opcode(.i32_const)); | |
| 332 | try leb.writeILEB128(writer, inst.val.toUnsignedInt()); | |
| 834 | switch (inst.base.ty.zigTypeTag()) { | |
| 835 | .Int => { | |
| 836 | // write opcode | |
| 837 | const opcode: wasm.Opcode = buildOpcode(.{ | |
| 838 | .op = .@"const", | |
| 839 | .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty), | |
| 840 | }); | |
| 841 | try writer.writeByte(wasm.opcode(opcode)); | |
| 842 | // write constant | |
| 843 | switch (inst.base.ty.intInfo(self.target).signedness) { | |
| 844 | .signed => try leb.writeILEB128(writer, inst.val.toSignedInt()), | |
| 845 | .unsigned => try leb.writeILEB128(writer, inst.val.toUnsignedInt()), | |
| 846 | } | |
| 333 | 847 | }, |
| 334 | .i32, .bool => { | |
| 848 | .Bool => { | |
| 849 | // write opcode | |
| 335 | 850 | try writer.writeByte(wasm.opcode(.i32_const)); |
| 851 | // write constant | |
| 336 | 852 | try leb.writeILEB128(writer, inst.val.toSignedInt()); |
| 337 | 853 | }, |
| 338 | .u64 => { | |
| 339 | try writer.writeByte(wasm.opcode(.i64_const)); | |
| 340 | try leb.writeILEB128(writer, inst.val.toUnsignedInt()); | |
| 341 | }, | |
| 342 | .i64 => { | |
| 343 | try writer.writeByte(wasm.opcode(.i64_const)); | |
| 344 | try leb.writeILEB128(writer, inst.val.toSignedInt()); | |
| 854 | .Float => { | |
| 855 | // write opcode | |
| 856 | const opcode: wasm.Opcode = buildOpcode(.{ | |
| 857 | .op = .@"const", | |
| 858 | .valtype1 = try self.typeToValtype(inst.base.src, inst.base.ty), | |
| 859 | }); | |
| 860 | try writer.writeByte(wasm.opcode(opcode)); | |
| 861 | // write constant | |
| 862 | switch (inst.base.ty.floatBits(self.target)) { | |
| 863 | 0...32 => try writer.writeIntLittle(u32, @bitCast(u32, inst.val.toFloat(f32))), | |
| 864 | 64 => try writer.writeIntLittle(u64, @bitCast(u64, inst.val.toFloat(f64))), | |
| 865 | else => |bits| return self.fail(inst.base.src, "Wasm TODO: emitConstant for float with {d} bits", .{bits}), | |
| 866 | } | |
| 345 | 867 | }, |
| 346 | .f32 => { | |
| 347 | try writer.writeByte(wasm.opcode(.f32_const)); | |
| 348 | // TODO: enforce LE byte order | |
| 349 | try writer.writeAll(mem.asBytes(&inst.val.toFloat(f32))); | |
| 868 | .Pointer => { | |
| 869 | if (inst.val.castTag(.decl_ref)) |payload| { | |
| 870 | const decl = payload.data; | |
| 871 | ||
| 872 | // offset into the offset table within the 'data' section | |
| 873 | const ptr_width = self.target.cpu.arch.ptrBitWidth() / 8; | |
| 874 | try writer.writeByte(wasm.opcode(.i32_const)); | |
| 875 | try leb.writeULEB128(writer, decl.link.wasm.offset_index * ptr_width); | |
| 876 | ||
| 877 | // memory instruction followed by their memarg immediate | |
| 878 | // memarg ::== x:u32, y:u32 => {align x, offset y} | |
| 879 | try writer.writeByte(wasm.opcode(.i32_load)); | |
| 880 | try leb.writeULEB128(writer, @as(u32, 0)); | |
| 881 | try leb.writeULEB128(writer, @as(u32, 0)); | |
| 882 | } else return self.fail(inst.base.src, "Wasm TODO: emitConstant for other const pointer tag {s}", .{inst.val.tag()}); | |
| 350 | 883 | }, |
| 351 | .f64 => { | |
| 352 | try writer.writeByte(wasm.opcode(.f64_const)); | |
| 353 | // TODO: enforce LE byte order | |
| 354 | try writer.writeAll(mem.asBytes(&inst.val.toFloat(f64))); | |
| 355 | }, | |
| 356 | .void => {}, | |
| 357 | else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for type {s}", .{ty}), | |
| 884 | .Void => {}, | |
| 885 | else => |ty| return self.fail(inst.base.src, "Wasm TODO: emitConstant for zigTypeTag {s}", .{ty}), | |
| 358 | 886 | } |
| 359 | 887 | } |
| 360 | 888 | |
| ... | ... | @@ -455,62 +983,18 @@ pub const Context = struct { |
| 455 | 983 | try self.emitWValue(lhs); |
| 456 | 984 | try self.emitWValue(rhs); |
| 457 | 985 | |
| 458 | const opcode_maybe: ?wasm.Opcode = switch (op) { | |
| 459 | .lt => @as(?wasm.Opcode, switch (ty) { | |
| 460 | .i32 => .i32_lt_s, | |
| 461 | .u32 => .i32_lt_u, | |
| 462 | .i64 => .i64_lt_s, | |
| 463 | .u64 => .i64_lt_u, | |
| 464 | .f32 => .f32_lt, | |
| 465 | .f64 => .f64_lt, | |
| 466 | else => null, | |
| 467 | }), | |
| 468 | .lte => @as(?wasm.Opcode, switch (ty) { | |
| 469 | .i32 => .i32_le_s, | |
| 470 | .u32 => .i32_le_u, | |
| 471 | .i64 => .i64_le_s, | |
| 472 | .u64 => .i64_le_u, | |
| 473 | .f32 => .f32_le, | |
| 474 | .f64 => .f64_le, | |
| 475 | else => null, | |
| 476 | }), | |
| 477 | .eq => @as(?wasm.Opcode, switch (ty) { | |
| 478 | .i32, .u32 => .i32_eq, | |
| 479 | .i64, .u64 => .i64_eq, | |
| 480 | .f32 => .f32_eq, | |
| 481 | .f64 => .f64_eq, | |
| 482 | else => null, | |
| 483 | }), | |
| 484 | .gte => @as(?wasm.Opcode, switch (ty) { | |
| 485 | .i32 => .i32_ge_s, | |
| 486 | .u32 => .i32_ge_u, | |
| 487 | .i64 => .i64_ge_s, | |
| 488 | .u64 => .i64_ge_u, | |
| 489 | .f32 => .f32_ge, | |
| 490 | .f64 => .f64_ge, | |
| 491 | else => null, | |
| 492 | }), | |
| 493 | .gt => @as(?wasm.Opcode, switch (ty) { | |
| 494 | .i32 => .i32_gt_s, | |
| 495 | .u32 => .i32_gt_u, | |
| 496 | .i64 => .i64_gt_s, | |
| 497 | .u64 => .i64_gt_u, | |
| 498 | .f32 => .f32_gt, | |
| 499 | .f64 => .f64_gt, | |
| 500 | else => null, | |
| 501 | }), | |
| 502 | .neq => @as(?wasm.Opcode, switch (ty) { | |
| 503 | .i32, .u32 => .i32_ne, | |
| 504 | .i64, .u64 => .i64_ne, | |
| 505 | .f32 => .f32_ne, | |
| 506 | .f64 => .f64_ne, | |
| 507 | else => null, | |
| 508 | }), | |
| 509 | }; | |
| 510 | ||
| 511 | const opcode = opcode_maybe orelse | |
| 512 | return self.fail(inst.base.src, "TODO - Wasm genCmp for type '{s}' and operator '{s}'", .{ ty, @tagName(op) }); | |
| 513 | ||
| 986 | const opcode: wasm.Opcode = buildOpcode(.{ | |
| 987 | .valtype1 = try self.typeToValtype(inst.base.src, inst.lhs.ty), | |
| 988 | .op = switch (op) { | |
| 989 | .lt => .lt, | |
| 990 | .lte => .le, | |
| 991 | .eq => .eq, | |
| 992 | .neq => .ne, | |
| 993 | .gte => .ge, | |
| 994 | .gt => .gt, | |
| 995 | }, | |
| 996 | .signedness = inst.lhs.ty.intInfo(self.target).signedness, | |
| 997 | }); | |
| 514 | 998 | try self.code.append(wasm.opcode(opcode)); |
| 515 | 999 | return WValue{ .code_offset = offset }; |
| 516 | 1000 | } |
src/ir.zig+4| ... | ... | @@ -115,6 +115,7 @@ pub const Inst = struct { |
| 115 | 115 | unreach, |
| 116 | 116 | mul, |
| 117 | 117 | mulwrap, |
| 118 | div, | |
| 118 | 119 | not, |
| 119 | 120 | floatcast, |
| 120 | 121 | intcast, |
| ... | ... | @@ -181,6 +182,7 @@ pub const Inst = struct { |
| 181 | 182 | .subwrap, |
| 182 | 183 | .mul, |
| 183 | 184 | .mulwrap, |
| 185 | .div, | |
| 184 | 186 | .cmp_lt, |
| 185 | 187 | .cmp_lte, |
| 186 | 188 | .cmp_eq, |
| ... | ... | @@ -752,6 +754,7 @@ const DumpTzir = struct { |
| 752 | 754 | .subwrap, |
| 753 | 755 | .mul, |
| 754 | 756 | .mulwrap, |
| 757 | .div, | |
| 755 | 758 | .cmp_lt, |
| 756 | 759 | .cmp_lte, |
| 757 | 760 | .cmp_eq, |
| ... | ... | @@ -891,6 +894,7 @@ const DumpTzir = struct { |
| 891 | 894 | .subwrap, |
| 892 | 895 | .mul, |
| 893 | 896 | .mulwrap, |
| 897 | .div, | |
| 894 | 898 | .cmp_lt, |
| 895 | 899 | .cmp_lte, |
| 896 | 900 | .cmp_eq, |
src/link.zig+4-3| ... | ... | @@ -138,7 +138,7 @@ pub const File = struct { |
| 138 | 138 | coff: Coff.TextBlock, |
| 139 | 139 | macho: MachO.TextBlock, |
| 140 | 140 | c: C.DeclBlock, |
| 141 | wasm: void, | |
| 141 | wasm: Wasm.DeclBlock, | |
| 142 | 142 | spirv: void, |
| 143 | 143 | }; |
| 144 | 144 | |
| ... | ... | @@ -147,7 +147,7 @@ pub const File = struct { |
| 147 | 147 | coff: Coff.SrcFn, |
| 148 | 148 | macho: MachO.SrcFn, |
| 149 | 149 | c: C.FnBlock, |
| 150 | wasm: ?Wasm.FnData, | |
| 150 | wasm: Wasm.FnData, | |
| 151 | 151 | spirv: SpirV.FnData, |
| 152 | 152 | }; |
| 153 | 153 | |
| ... | ... | @@ -328,7 +328,8 @@ pub const File = struct { |
| 328 | 328 | .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), |
| 329 | 329 | .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), |
| 330 | 330 | .c => return @fieldParentPtr(C, "base", base).allocateDeclIndexes(decl), |
| 331 | .wasm, .spirv => {}, | |
| 331 | .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl), | |
| 332 | .spirv => {}, | |
| 332 | 333 | } |
| 333 | 334 | } |
| 334 | 335 |
src/link/Elf.zig+2-4| ... | ... | @@ -2228,10 +2228,9 @@ pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void { |
| 2228 | 2228 | const node_datas = tree.nodes.items(.data); |
| 2229 | 2229 | const token_starts = tree.tokens.items(.start); |
| 2230 | 2230 | |
| 2231 | const file_ast_decls = tree.rootDecls(); | |
| 2232 | 2231 | // TODO Look into improving the performance here by adding a token-index-to-line |
| 2233 | 2232 | // lookup table. Currently this involves scanning over the source code for newlines. |
| 2234 | const fn_decl = file_ast_decls[decl.src_index]; | |
| 2233 | const fn_decl = decl.src_node; | |
| 2235 | 2234 | assert(node_tags[fn_decl] == .fn_decl); |
| 2236 | 2235 | const block = node_datas[fn_decl].rhs; |
| 2237 | 2236 | const lbrace = tree.firstToken(block); |
| ... | ... | @@ -2755,10 +2754,9 @@ pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Dec |
| 2755 | 2754 | const node_datas = tree.nodes.items(.data); |
| 2756 | 2755 | const token_starts = tree.tokens.items(.start); |
| 2757 | 2756 | |
| 2758 | const file_ast_decls = tree.rootDecls(); | |
| 2759 | 2757 | // TODO Look into improving the performance here by adding a token-index-to-line |
| 2760 | 2758 | // lookup table. Currently this involves scanning over the source code for newlines. |
| 2761 | const fn_decl = file_ast_decls[decl.src_index]; | |
| 2759 | const fn_decl = decl.src_node; | |
| 2762 | 2760 | assert(node_tags[fn_decl] == .fn_decl); |
| 2763 | 2761 | const block = node_datas[fn_decl].rhs; |
| 2764 | 2762 | const lbrace = tree.firstToken(block); |
src/link/MachO.zig+2-2| ... | ... | @@ -1256,7 +1256,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1256 | 1256 | const inst = code_buffer.items[fixup.offset..][0..4]; |
| 1257 | 1257 | var parsed = mem.bytesAsValue(meta.TagPayload( |
| 1258 | 1258 | aarch64.Instruction, |
| 1259 | aarch64.Instruction.PCRelativeAddress, | |
| 1259 | aarch64.Instruction.pc_relative_address, | |
| 1260 | 1260 | ), inst); |
| 1261 | 1261 | const this_page = @intCast(i32, this_addr >> 12); |
| 1262 | 1262 | const target_page = @intCast(i32, target_addr >> 12); |
| ... | ... | @@ -1268,7 +1268,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { |
| 1268 | 1268 | const inst = code_buffer.items[fixup.offset + 4 ..][0..4]; |
| 1269 | 1269 | var parsed = mem.bytesAsValue(meta.TagPayload( |
| 1270 | 1270 | aarch64.Instruction, |
| 1271 | aarch64.Instruction.LoadStoreRegister, | |
| 1271 | aarch64.Instruction.load_store_register, | |
| 1272 | 1272 | ), inst); |
| 1273 | 1273 | const narrowed = @truncate(u12, target_addr); |
| 1274 | 1274 | const offset = try math.divExact(u12, narrowed, 8); |
src/link/MachO/DebugSymbols.zig+2-4| ... | ... | @@ -909,10 +909,9 @@ pub fn updateDeclLineNumber(self: *DebugSymbols, module: *Module, decl: *const M |
| 909 | 909 | const node_datas = tree.nodes.items(.data); |
| 910 | 910 | const token_starts = tree.tokens.items(.start); |
| 911 | 911 | |
| 912 | const file_ast_decls = tree.rootDecls(); | |
| 913 | 912 | // TODO Look into improving the performance here by adding a token-index-to-line |
| 914 | 913 | // lookup table. Currently this involves scanning over the source code for newlines. |
| 915 | const fn_decl = file_ast_decls[decl.src_index]; | |
| 914 | const fn_decl = decl.src_node; | |
| 916 | 915 | assert(node_tags[fn_decl] == .fn_decl); |
| 917 | 916 | const block = node_datas[fn_decl].rhs; |
| 918 | 917 | const lbrace = tree.firstToken(block); |
| ... | ... | @@ -959,10 +958,9 @@ pub fn initDeclDebugBuffers( |
| 959 | 958 | const node_datas = tree.nodes.items(.data); |
| 960 | 959 | const token_starts = tree.tokens.items(.start); |
| 961 | 960 | |
| 962 | const file_ast_decls = tree.rootDecls(); | |
| 963 | 961 | // TODO Look into improving the performance here by adding a token-index-to-line |
| 964 | 962 | // lookup table. Currently this involves scanning over the source code for newlines. |
| 965 | const fn_decl = file_ast_decls[decl.src_index]; | |
| 963 | const fn_decl = decl.src_node; | |
| 966 | 964 | assert(node_tags[fn_decl] == .fn_decl); |
| 967 | 965 | const block = node_datas[fn_decl].rhs; |
| 968 | 966 | const lbrace = tree.firstToken(block); |
src/link/MachO/Zld.zig+6-6| ... | ... | @@ -1668,7 +1668,7 @@ fn doRelocs(self: *Zld) !void { |
| 1668 | 1668 | var parsed = mem.bytesAsValue( |
| 1669 | 1669 | meta.TagPayload( |
| 1670 | 1670 | aarch64.Instruction, |
| 1671 | aarch64.Instruction.UnconditionalBranchImmediate, | |
| 1671 | aarch64.Instruction.unconditional_branch_immediate, | |
| 1672 | 1672 | ), |
| 1673 | 1673 | inst, |
| 1674 | 1674 | ); |
| ... | ... | @@ -1688,7 +1688,7 @@ fn doRelocs(self: *Zld) !void { |
| 1688 | 1688 | var parsed = mem.bytesAsValue( |
| 1689 | 1689 | meta.TagPayload( |
| 1690 | 1690 | aarch64.Instruction, |
| 1691 | aarch64.Instruction.PCRelativeAddress, | |
| 1691 | aarch64.Instruction.pc_relative_address, | |
| 1692 | 1692 | ), |
| 1693 | 1693 | inst, |
| 1694 | 1694 | ); |
| ... | ... | @@ -1706,7 +1706,7 @@ fn doRelocs(self: *Zld) !void { |
| 1706 | 1706 | var parsed = mem.bytesAsValue( |
| 1707 | 1707 | meta.TagPayload( |
| 1708 | 1708 | aarch64.Instruction, |
| 1709 | aarch64.Instruction.AddSubtractImmediate, | |
| 1709 | aarch64.Instruction.add_subtract_immediate, | |
| 1710 | 1710 | ), |
| 1711 | 1711 | inst, |
| 1712 | 1712 | ); |
| ... | ... | @@ -1719,7 +1719,7 @@ fn doRelocs(self: *Zld) !void { |
| 1719 | 1719 | var parsed = mem.bytesAsValue( |
| 1720 | 1720 | meta.TagPayload( |
| 1721 | 1721 | aarch64.Instruction, |
| 1722 | aarch64.Instruction.LoadStoreRegister, | |
| 1722 | aarch64.Instruction.load_store_register, | |
| 1723 | 1723 | ), |
| 1724 | 1724 | inst, |
| 1725 | 1725 | ); |
| ... | ... | @@ -1774,7 +1774,7 @@ fn doRelocs(self: *Zld) !void { |
| 1774 | 1774 | const curr = mem.bytesAsValue( |
| 1775 | 1775 | meta.TagPayload( |
| 1776 | 1776 | aarch64.Instruction, |
| 1777 | aarch64.Instruction.AddSubtractImmediate, | |
| 1777 | aarch64.Instruction.add_subtract_immediate, | |
| 1778 | 1778 | ), |
| 1779 | 1779 | inst, |
| 1780 | 1780 | ); |
| ... | ... | @@ -1783,7 +1783,7 @@ fn doRelocs(self: *Zld) !void { |
| 1783 | 1783 | const curr = mem.bytesAsValue( |
| 1784 | 1784 | meta.TagPayload( |
| 1785 | 1785 | aarch64.Instruction, |
| 1786 | aarch64.Instruction.LoadStoreRegister, | |
| 1786 | aarch64.Instruction.load_store_register, | |
| 1787 | 1787 | ), |
| 1788 | 1788 | inst, |
| 1789 | 1789 | ); |
src/link/Wasm.zig+279-62| ... | ... | @@ -16,21 +16,11 @@ const link = @import("../link.zig"); |
| 16 | 16 | const trace = @import("../tracy.zig").trace; |
| 17 | 17 | const build_options = @import("build_options"); |
| 18 | 18 | const Cache = @import("../Cache.zig"); |
| 19 | const TypedValue = @import("../TypedValue.zig"); | |
| 19 | 20 | |
| 20 | 21 | pub const base_tag = link.File.Tag.wasm; |
| 21 | 22 | |
| 22 | pub const FnData = struct { | |
| 23 | /// Generated code for the type of the function | |
| 24 | functype: std.ArrayListUnmanaged(u8) = .{}, | |
| 25 | /// Generated code for the body of the function | |
| 26 | code: std.ArrayListUnmanaged(u8) = .{}, | |
| 27 | /// Locations in the generated code where function indexes must be filled in. | |
| 28 | /// This must be kept ordered by offset. | |
| 29 | idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }) = .{}, | |
| 30 | }; | |
| 31 | ||
| 32 | 23 | base: link.File, |
| 33 | ||
| 34 | 24 | /// List of all function Decls to be written to the output file. The index of |
| 35 | 25 | /// each Decl in this list at the time of writing the binary is used as the |
| 36 | 26 | /// function index. In the event where ext_funcs' size is not 0, the index of |
| ... | ... | @@ -45,6 +35,77 @@ ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, |
| 45 | 35 | /// to support existing code. |
| 46 | 36 | /// TODO: Allow setting this through a flag? |
| 47 | 37 | host_name: []const u8 = "env", |
| 38 | /// The last `DeclBlock` that was initialized will be saved here. | |
| 39 | last_block: ?*DeclBlock = null, | |
| 40 | /// Table with offsets, each element represents an offset with the value being | |
| 41 | /// the offset into the 'data' section where the data lives | |
| 42 | offset_table: std.ArrayListUnmanaged(u32) = .{}, | |
| 43 | /// List of offset indexes which are free to be used for new decl's. | |
| 44 | /// Each element's value points to an index into the offset_table. | |
| 45 | offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, | |
| 46 | /// List of all `Decl` that are currently alive. | |
| 47 | /// This is ment for bookkeeping so we can safely cleanup all codegen memory | |
| 48 | /// when calling `deinit` | |
| 49 | symbols: std.ArrayListUnmanaged(*Module.Decl) = .{}, | |
| 50 | ||
| 51 | pub const FnData = struct { | |
| 52 | /// Generated code for the type of the function | |
| 53 | functype: std.ArrayListUnmanaged(u8), | |
| 54 | /// Generated code for the body of the function | |
| 55 | code: std.ArrayListUnmanaged(u8), | |
| 56 | /// Locations in the generated code where function indexes must be filled in. | |
| 57 | /// This must be kept ordered by offset. | |
| 58 | idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }), | |
| 59 | ||
| 60 | pub const empty: FnData = .{ | |
| 61 | .functype = .{}, | |
| 62 | .code = .{}, | |
| 63 | .idx_refs = .{}, | |
| 64 | }; | |
| 65 | }; | |
| 66 | ||
| 67 | pub const DeclBlock = struct { | |
| 68 | /// Determines whether the `DeclBlock` has been initialized for codegen. | |
| 69 | init: bool, | |
| 70 | /// Index into the `symbols` list. | |
| 71 | symbol_index: u32, | |
| 72 | /// Index into the offset table | |
| 73 | offset_index: u32, | |
| 74 | /// The size of the block and how large part of the data section it occupies. | |
| 75 | /// Will be 0 when the Decl will not live inside the data section and `data` will be undefined. | |
| 76 | size: u32, | |
| 77 | /// Points to the previous and next blocks. | |
| 78 | /// Can be used to find the total size, and used to calculate the `offset` based on the previous block. | |
| 79 | prev: ?*DeclBlock, | |
| 80 | next: ?*DeclBlock, | |
| 81 | /// Pointer to data that will be written to the 'data' section. | |
| 82 | /// This data either lives in `FnData.code` or is externally managed. | |
| 83 | /// For data that does not live inside the 'data' section, this field will be undefined. (size == 0). | |
| 84 | data: [*]const u8, | |
| 85 | ||
| 86 | pub const empty: DeclBlock = .{ | |
| 87 | .init = false, | |
| 88 | .symbol_index = 0, | |
| 89 | .offset_index = 0, | |
| 90 | .size = 0, | |
| 91 | .prev = null, | |
| 92 | .next = null, | |
| 93 | .data = undefined, | |
| 94 | }; | |
| 95 | ||
| 96 | /// Unplugs the `DeclBlock` from the chain | |
| 97 | fn unplug(self: *DeclBlock) void { | |
| 98 | if (self.prev) |prev| { | |
| 99 | prev.next = self.next; | |
| 100 | } | |
| 101 | ||
| 102 | if (self.next) |next| { | |
| 103 | next.prev = self.prev; | |
| 104 | } | |
| 105 | self.next = null; | |
| 106 | self.prev = null; | |
| 107 | } | |
| 108 | }; | |
| 48 | 109 | |
| 49 | 110 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm { |
| 50 | 111 | assert(options.object_format == .wasm); |
| ... | ... | @@ -52,7 +113,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 52 | 113 | if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO |
| 53 | 114 | if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO |
| 54 | 115 | |
| 55 | // TODO: read the file and keep vaild parts instead of truncating | |
| 116 | // TODO: read the file and keep valid parts instead of truncating | |
| 56 | 117 | const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); |
| 57 | 118 | errdefer file.close(); |
| 58 | 119 | |
| ... | ... | @@ -80,58 +141,76 @@ pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm { |
| 80 | 141 | } |
| 81 | 142 | |
| 82 | 143 | pub fn deinit(self: *Wasm) void { |
| 83 | for (self.funcs.items) |decl| { | |
| 84 | decl.fn_link.wasm.?.functype.deinit(self.base.allocator); | |
| 85 | decl.fn_link.wasm.?.code.deinit(self.base.allocator); | |
| 86 | decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); | |
| 87 | } | |
| 88 | for (self.ext_funcs.items) |decl| { | |
| 89 | decl.fn_link.wasm.?.functype.deinit(self.base.allocator); | |
| 90 | decl.fn_link.wasm.?.code.deinit(self.base.allocator); | |
| 91 | decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); | |
| 144 | for (self.symbols.items) |decl| { | |
| 145 | decl.fn_link.wasm.functype.deinit(self.base.allocator); | |
| 146 | decl.fn_link.wasm.code.deinit(self.base.allocator); | |
| 147 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); | |
| 92 | 148 | } |
| 149 | ||
| 93 | 150 | self.funcs.deinit(self.base.allocator); |
| 94 | 151 | self.ext_funcs.deinit(self.base.allocator); |
| 152 | self.offset_table.deinit(self.base.allocator); | |
| 153 | self.offset_table_free_list.deinit(self.base.allocator); | |
| 154 | self.symbols.deinit(self.base.allocator); | |
| 95 | 155 | } |
| 96 | 156 | |
| 97 | // Generate code for the Decl, storing it in memory to be later written to | |
| 98 | // the file on flush(). | |
| 99 | pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { | |
| 100 | const typed_value = decl.typed_value.most_recent.typed_value; | |
| 101 | if (typed_value.ty.zigTypeTag() != .Fn) | |
| 102 | return error.TODOImplementNonFnDeclsForWasm; | |
| 157 | pub fn allocateDeclIndexes(self: *Wasm, decl: *Module.Decl) !void { | |
| 158 | if (decl.link.wasm.init) return; | |
| 159 | ||
| 160 | try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); | |
| 161 | try self.symbols.ensureCapacity(self.base.allocator, self.symbols.items.len + 1); | |
| 103 | 162 | |
| 104 | if (decl.fn_link.wasm) |*fn_data| { | |
| 105 | fn_data.functype.items.len = 0; | |
| 106 | fn_data.code.items.len = 0; | |
| 107 | fn_data.idx_refs.items.len = 0; | |
| 163 | const block = &decl.link.wasm; | |
| 164 | block.init = true; | |
| 165 | ||
| 166 | block.symbol_index = @intCast(u32, self.symbols.items.len); | |
| 167 | self.symbols.appendAssumeCapacity(decl); | |
| 168 | ||
| 169 | if (self.offset_table_free_list.popOrNull()) |index| { | |
| 170 | block.offset_index = index; | |
| 108 | 171 | } else { |
| 109 | decl.fn_link.wasm = .{}; | |
| 110 | // dependent on function type, appends it to the correct list | |
| 111 | switch (decl.typed_value.most_recent.typed_value.val.tag()) { | |
| 172 | block.offset_index = @intCast(u32, self.offset_table.items.len); | |
| 173 | _ = self.offset_table.addOneAssumeCapacity(); | |
| 174 | } | |
| 175 | ||
| 176 | self.offset_table.items[block.offset_index] = 0; | |
| 177 | ||
| 178 | const typed_value = decl.typed_value.most_recent.typed_value; | |
| 179 | if (typed_value.ty.zigTypeTag() == .Fn) { | |
| 180 | switch (typed_value.val.tag()) { | |
| 181 | // dependent on function type, appends it to the correct list | |
| 112 | 182 | .function => try self.funcs.append(self.base.allocator, decl), |
| 113 | 183 | .extern_fn => try self.ext_funcs.append(self.base.allocator, decl), |
| 114 | else => return error.TODOImplementNonFnDeclsForWasm, | |
| 184 | else => unreachable, | |
| 115 | 185 | } |
| 116 | 186 | } |
| 117 | const fn_data = &decl.fn_link.wasm.?; | |
| 187 | } | |
| 118 | 188 | |
| 119 | var managed_functype = fn_data.functype.toManaged(self.base.allocator); | |
| 120 | var managed_code = fn_data.code.toManaged(self.base.allocator); | |
| 189 | // Generate code for the Decl, storing it in memory to be later written to | |
| 190 | // the file on flush(). | |
| 191 | pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { | |
| 192 | std.debug.assert(decl.link.wasm.init); // Must call allocateDeclIndexes() | |
| 193 | ||
| 194 | const typed_value = decl.typed_value.most_recent.typed_value; | |
| 195 | const fn_data = &decl.fn_link.wasm; | |
| 196 | fn_data.functype.items.len = 0; | |
| 197 | fn_data.code.items.len = 0; | |
| 198 | fn_data.idx_refs.items.len = 0; | |
| 121 | 199 | |
| 122 | 200 | var context = codegen.Context{ |
| 123 | 201 | .gpa = self.base.allocator, |
| 124 | 202 | .values = .{}, |
| 125 | .code = managed_code, | |
| 126 | .func_type_data = managed_functype, | |
| 203 | .code = fn_data.code.toManaged(self.base.allocator), | |
| 204 | .func_type_data = fn_data.functype.toManaged(self.base.allocator), | |
| 127 | 205 | .decl = decl, |
| 128 | 206 | .err_msg = undefined, |
| 129 | 207 | .locals = .{}, |
| 208 | .target = self.base.options.target, | |
| 130 | 209 | }; |
| 131 | 210 | defer context.deinit(); |
| 132 | 211 | |
| 133 | 212 | // generate the 'code' section for the function declaration |
| 134 | context.gen() catch |err| switch (err) { | |
| 213 | const result = context.gen(typed_value) catch |err| switch (err) { | |
| 135 | 214 | error.CodegenFail => { |
| 136 | 215 | decl.analysis = .codegen_failure; |
| 137 | 216 | try module.failed_decls.put(module.gpa, decl, context.err_msg); |
| ... | ... | @@ -140,15 +219,38 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { |
| 140 | 219 | else => |e| return err, |
| 141 | 220 | }; |
| 142 | 221 | |
| 143 | // as locals are patched afterwards, the offsets of funcidx's are off, | |
| 144 | // here we update them to correct them | |
| 145 | for (decl.fn_link.wasm.?.idx_refs.items) |*func| { | |
| 146 | // For each local, add 6 bytes (count + type) | |
| 147 | func.offset += @intCast(u32, context.locals.items.len * 6); | |
| 148 | } | |
| 222 | const code: []const u8 = switch (result) { | |
| 223 | .appended => @as([]const u8, context.code.items), | |
| 224 | .externally_managed => |payload| payload, | |
| 225 | }; | |
| 149 | 226 | |
| 150 | fn_data.functype = context.func_type_data.toUnmanaged(); | |
| 151 | 227 | fn_data.code = context.code.toUnmanaged(); |
| 228 | fn_data.functype = context.func_type_data.toUnmanaged(); | |
| 229 | ||
| 230 | const block = &decl.link.wasm; | |
| 231 | if (typed_value.ty.zigTypeTag() == .Fn) { | |
| 232 | // as locals are patched afterwards, the offsets of funcidx's are off, | |
| 233 | // here we update them to correct them | |
| 234 | for (fn_data.idx_refs.items) |*func| { | |
| 235 | // For each local, add 6 bytes (count + type) | |
| 236 | func.offset += @intCast(u32, context.locals.items.len * 6); | |
| 237 | } | |
| 238 | } else { | |
| 239 | block.size = @intCast(u32, code.len); | |
| 240 | block.data = code.ptr; | |
| 241 | } | |
| 242 | ||
| 243 | // If we're updating an existing decl, unplug it first | |
| 244 | // to avoid infinite loops due to earlier links | |
| 245 | block.unplug(); | |
| 246 | ||
| 247 | if (self.last_block) |last| { | |
| 248 | if (last != block) { | |
| 249 | last.next = block; | |
| 250 | block.prev = last; | |
| 251 | } | |
| 252 | } | |
| 253 | self.last_block = block; | |
| 152 | 254 | } |
| 153 | 255 | |
| 154 | 256 | pub fn updateDeclExports( |
| ... | ... | @@ -159,18 +261,34 @@ pub fn updateDeclExports( |
| 159 | 261 | ) !void {} |
| 160 | 262 | |
| 161 | 263 | pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { |
| 162 | // TODO: remove this assert when non-function Decls are implemented | |
| 163 | assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn); | |
| 164 | const func_idx = self.getFuncidx(decl).?; | |
| 165 | switch (decl.typed_value.most_recent.typed_value.val.tag()) { | |
| 166 | .function => _ = self.funcs.swapRemove(func_idx), | |
| 167 | .extern_fn => _ = self.ext_funcs.swapRemove(func_idx), | |
| 168 | else => unreachable, | |
| 264 | if (self.getFuncidx(decl)) |func_idx| { | |
| 265 | switch (decl.typed_value.most_recent.typed_value.val.tag()) { | |
| 266 | .function => _ = self.funcs.swapRemove(func_idx), | |
| 267 | .extern_fn => _ = self.ext_funcs.swapRemove(func_idx), | |
| 268 | else => unreachable, | |
| 269 | } | |
| 270 | } | |
| 271 | const block = &decl.link.wasm; | |
| 272 | ||
| 273 | if (self.last_block == block) { | |
| 274 | self.last_block = block.prev; | |
| 169 | 275 | } |
| 170 | decl.fn_link.wasm.?.functype.deinit(self.base.allocator); | |
| 171 | decl.fn_link.wasm.?.code.deinit(self.base.allocator); | |
| 172 | decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); | |
| 173 | decl.fn_link.wasm = null; | |
| 276 | ||
| 277 | block.unplug(); | |
| 278 | ||
| 279 | self.offset_table_free_list.append(self.base.allocator, decl.link.wasm.offset_index) catch {}; | |
| 280 | _ = self.symbols.swapRemove(block.symbol_index); | |
| 281 | ||
| 282 | // update symbol_index as we swap removed the last symbol into the removed's position | |
| 283 | if (block.symbol_index < self.symbols.items.len) | |
| 284 | self.symbols.items[block.symbol_index].link.wasm.symbol_index = block.symbol_index; | |
| 285 | ||
| 286 | block.init = false; | |
| 287 | ||
| 288 | decl.fn_link.wasm.functype.deinit(self.base.allocator); | |
| 289 | decl.fn_link.wasm.code.deinit(self.base.allocator); | |
| 290 | decl.fn_link.wasm.idx_refs.deinit(self.base.allocator); | |
| 291 | decl.fn_link.wasm = undefined; | |
| 174 | 292 | } |
| 175 | 293 | |
| 176 | 294 | pub fn flush(self: *Wasm, comp: *Compilation) !void { |
| ... | ... | @@ -187,6 +305,25 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 187 | 305 | |
| 188 | 306 | const file = self.base.file.?; |
| 189 | 307 | const header_size = 5 + 1; |
| 308 | // ptr_width in bytes | |
| 309 | const ptr_width = self.base.options.target.cpu.arch.ptrBitWidth() / 8; | |
| 310 | // The size of the offset table in bytes | |
| 311 | // The table contains all decl's with its corresponding offset into | |
| 312 | // the 'data' section | |
| 313 | const offset_table_size = @intCast(u32, self.offset_table.items.len * ptr_width); | |
| 314 | ||
| 315 | // The size of the data, this together with `offset_table_size` amounts to the | |
| 316 | // total size of the 'data' section | |
| 317 | var first_decl: ?*DeclBlock = null; | |
| 318 | const data_size: u32 = if (self.last_block) |last| blk: { | |
| 319 | var size = last.size; | |
| 320 | var cur = last; | |
| 321 | while (cur.prev) |prev| : (cur = prev) { | |
| 322 | size += prev.size; | |
| 323 | } | |
| 324 | first_decl = cur; | |
| 325 | break :blk size; | |
| 326 | } else 0; | |
| 190 | 327 | |
| 191 | 328 | // No need to rewrite the magic/version header |
| 192 | 329 | try file.setEndPos(@sizeOf(@TypeOf(wasm.magic ++ wasm.version))); |
| ... | ... | @@ -198,8 +335,8 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 198 | 335 | |
| 199 | 336 | // extern functions are defined in the wasm binary first through the `import` |
| 200 | 337 | // section, so define their func types first |
| 201 | for (self.ext_funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items); | |
| 202 | for (self.funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items); | |
| 338 | for (self.ext_funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.functype.items); | |
| 339 | for (self.funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.functype.items); | |
| 203 | 340 | |
| 204 | 341 | try writeVecSectionHeader( |
| 205 | 342 | file, |
| ... | ... | @@ -256,6 +393,31 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 256 | 393 | ); |
| 257 | 394 | } |
| 258 | 395 | |
| 396 | // Memory section | |
| 397 | if (data_size != 0) { | |
| 398 | const header_offset = try reserveVecSectionHeader(file); | |
| 399 | const writer = file.writer(); | |
| 400 | ||
| 401 | try leb.writeULEB128(writer, @as(u32, 0)); | |
| 402 | // Calculate the amount of memory pages are required and write them. | |
| 403 | // Wasm uses 64kB page sizes. Round up to ensure the data segments fit into the memory | |
| 404 | try leb.writeULEB128( | |
| 405 | writer, | |
| 406 | try std.math.divCeil( | |
| 407 | u32, | |
| 408 | offset_table_size + data_size, | |
| 409 | std.wasm.page_size, | |
| 410 | ), | |
| 411 | ); | |
| 412 | try writeVecSectionHeader( | |
| 413 | file, | |
| 414 | header_offset, | |
| 415 | .memory, | |
| 416 | @intCast(u32, (try file.getPos()) - header_offset - header_size), | |
| 417 | @as(u32, 1), // wasm currently only supports 1 linear memory segment | |
| 418 | ); | |
| 419 | } | |
| 420 | ||
| 259 | 421 | // Export section |
| 260 | 422 | if (self.base.options.module) |module| { |
| 261 | 423 | const header_offset = try reserveVecSectionHeader(file); |
| ... | ... | @@ -280,6 +442,16 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 280 | 442 | count += 1; |
| 281 | 443 | } |
| 282 | 444 | } |
| 445 | ||
| 446 | // export memory if size is not 0 | |
| 447 | if (data_size != 0) { | |
| 448 | try leb.writeULEB128(writer, @intCast(u32, "memory".len)); | |
| 449 | try writer.writeAll("memory"); | |
| 450 | try writer.writeByte(wasm.externalKind(.memory)); | |
| 451 | try leb.writeULEB128(writer, @as(u32, 0)); // only 1 memory 'object' can exist | |
| 452 | count += 1; | |
| 453 | } | |
| 454 | ||
| 283 | 455 | try writeVecSectionHeader( |
| 284 | 456 | file, |
| 285 | 457 | header_offset, |
| ... | ... | @@ -294,7 +466,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 294 | 466 | const header_offset = try reserveVecSectionHeader(file); |
| 295 | 467 | const writer = file.writer(); |
| 296 | 468 | for (self.funcs.items) |decl| { |
| 297 | const fn_data = &decl.fn_link.wasm.?; | |
| 469 | const fn_data = &decl.fn_link.wasm; | |
| 298 | 470 | |
| 299 | 471 | // Write the already generated code to the file, inserting |
| 300 | 472 | // function indexes where required. |
| ... | ... | @@ -319,6 +491,51 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { |
| 319 | 491 | @intCast(u32, self.funcs.items.len), |
| 320 | 492 | ); |
| 321 | 493 | } |
| 494 | ||
| 495 | // Data section | |
| 496 | if (data_size != 0) { | |
| 497 | const header_offset = try reserveVecSectionHeader(file); | |
| 498 | const writer = file.writer(); | |
| 499 | var len: u32 = 0; | |
| 500 | // index to memory section (currently, there can only be 1 memory section in wasm) | |
| 501 | try leb.writeULEB128(writer, @as(u32, 0)); | |
| 502 | ||
| 503 | // offset into data section | |
| 504 | try writer.writeByte(wasm.opcode(.i32_const)); | |
| 505 | try leb.writeILEB128(writer, @as(i32, 0)); | |
| 506 | try writer.writeByte(wasm.opcode(.end)); | |
| 507 | ||
| 508 | const total_size = offset_table_size + data_size; | |
| 509 | ||
| 510 | // offset table + data size | |
| 511 | try leb.writeULEB128(writer, total_size); | |
| 512 | ||
| 513 | // fill in the offset table and the data segments | |
| 514 | const file_offset = try file.getPos(); | |
| 515 | var cur = first_decl; | |
| 516 | var data_offset = offset_table_size; | |
| 517 | while (cur) |cur_block| : (cur = cur_block.next) { | |
| 518 | if (cur_block.size == 0) continue; | |
| 519 | std.debug.assert(cur_block.init); | |
| 520 | ||
| 521 | const offset = (cur_block.offset_index) * ptr_width; | |
| 522 | var buf: [4]u8 = undefined; | |
| 523 | std.mem.writeIntLittle(u32, &buf, data_offset); | |
| 524 | ||
| 525 | try file.pwriteAll(&buf, file_offset + offset); | |
| 526 | try file.pwriteAll(cur_block.data[0..cur_block.size], file_offset + data_offset); | |
| 527 | data_offset += cur_block.size; | |
| 528 | } | |
| 529 | ||
| 530 | try file.seekTo(file_offset + data_offset); | |
| 531 | try writeVecSectionHeader( | |
| 532 | file, | |
| 533 | header_offset, | |
| 534 | .data, | |
| 535 | @intCast(u32, (file_offset + data_offset) - header_offset - header_size), | |
| 536 | @intCast(u32, 1), // only 1 data section | |
| 537 | ); | |
| 538 | } | |
| 322 | 539 | } |
| 323 | 540 | |
| 324 | 541 | fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { |
src/main.zig+34-25| ... | ... | @@ -505,7 +505,6 @@ fn buildOutputType( |
| 505 | 505 | var emit_bin: EmitBin = .yes_default_path; |
| 506 | 506 | var emit_asm: Emit = .no; |
| 507 | 507 | var emit_llvm_ir: Emit = .no; |
| 508 | var emit_zir: Emit = .no; | |
| 509 | 508 | var emit_docs: Emit = .no; |
| 510 | 509 | var emit_analysis: Emit = .no; |
| 511 | 510 | var target_arch_os_abi: []const u8 = "native"; |
| ... | ... | @@ -599,15 +598,15 @@ fn buildOutputType( |
| 599 | 598 | var test_exec_args = std.ArrayList(?[]const u8).init(gpa); |
| 600 | 599 | defer test_exec_args.deinit(); |
| 601 | 600 | |
| 602 | const pkg_tree_root = try gpa.create(Package); | |
| 603 | 601 | // This package only exists to clean up the code parsing --pkg-begin and |
| 604 | 602 | // --pkg-end flags. Use dummy values that are safe for the destroy call. |
| 605 | pkg_tree_root.* = .{ | |
| 603 | var pkg_tree_root: Package = .{ | |
| 606 | 604 | .root_src_directory = .{ .path = null, .handle = fs.cwd() }, |
| 607 | 605 | .root_src_path = &[0]u8{}, |
| 606 | .namespace_hash = Package.root_namespace_hash, | |
| 608 | 607 | }; |
| 609 | defer pkg_tree_root.destroy(gpa); | |
| 610 | var cur_pkg: *Package = pkg_tree_root; | |
| 608 | defer freePkgTree(gpa, &pkg_tree_root, false); | |
| 609 | var cur_pkg: *Package = &pkg_tree_root; | |
| 611 | 610 | |
| 612 | 611 | switch (arg_mode) { |
| 613 | 612 | .build, .translate_c, .zig_test, .run => { |
| ... | ... | @@ -658,8 +657,7 @@ fn buildOutputType( |
| 658 | 657 | ) catch |err| { |
| 659 | 658 | fatal("Failed to add package at path {s}: {s}", .{ pkg_path, @errorName(err) }); |
| 660 | 659 | }; |
| 661 | new_cur_pkg.parent = cur_pkg; | |
| 662 | try cur_pkg.add(gpa, pkg_name, new_cur_pkg); | |
| 660 | try cur_pkg.addAndAdopt(gpa, pkg_name, new_cur_pkg); | |
| 663 | 661 | cur_pkg = new_cur_pkg; |
| 664 | 662 | } else if (mem.eql(u8, arg, "--pkg-end")) { |
| 665 | 663 | cur_pkg = cur_pkg.parent orelse |
| ... | ... | @@ -924,12 +922,6 @@ fn buildOutputType( |
| 924 | 922 | emit_bin = .{ .yes = arg["-femit-bin=".len..] }; |
| 925 | 923 | } else if (mem.eql(u8, arg, "-fno-emit-bin")) { |
| 926 | 924 | emit_bin = .no; |
| 927 | } else if (mem.eql(u8, arg, "-femit-zir")) { | |
| 928 | emit_zir = .yes_default_path; | |
| 929 | } else if (mem.startsWith(u8, arg, "-femit-zir=")) { | |
| 930 | emit_zir = .{ .yes = arg["-femit-zir=".len..] }; | |
| 931 | } else if (mem.eql(u8, arg, "-fno-emit-zir")) { | |
| 932 | emit_zir = .no; | |
| 933 | 925 | } else if (mem.eql(u8, arg, "-femit-h")) { |
| 934 | 926 | emit_h = .yes_default_path; |
| 935 | 927 | } else if (mem.startsWith(u8, arg, "-femit-h=")) { |
| ... | ... | @@ -1026,7 +1018,7 @@ fn buildOutputType( |
| 1026 | 1018 | .extra_flags = try arena.dupe([]const u8, extra_cflags.items), |
| 1027 | 1019 | }); |
| 1028 | 1020 | }, |
| 1029 | .zig, .zir => { | |
| 1021 | .zig => { | |
| 1030 | 1022 | if (root_src_file) |other| { |
| 1031 | 1023 | fatal("found another zig file '{s}' after root source file '{s}'", .{ arg, other }); |
| 1032 | 1024 | } else { |
| ... | ... | @@ -1087,7 +1079,7 @@ fn buildOutputType( |
| 1087 | 1079 | .unknown, .shared_library, .object, .static_library => { |
| 1088 | 1080 | try link_objects.append(it.only_arg); |
| 1089 | 1081 | }, |
| 1090 | .zig, .zir => { | |
| 1082 | .zig => { | |
| 1091 | 1083 | if (root_src_file) |other| { |
| 1092 | 1084 | fatal("found another zig file '{s}' after root source file '{s}'", .{ it.only_arg, other }); |
| 1093 | 1085 | } else { |
| ... | ... | @@ -1725,13 +1717,6 @@ fn buildOutputType( |
| 1725 | 1717 | var emit_docs_resolved = try emit_docs.resolve("docs"); |
| 1726 | 1718 | defer emit_docs_resolved.deinit(); |
| 1727 | 1719 | |
| 1728 | switch (emit_zir) { | |
| 1729 | .no => {}, | |
| 1730 | .yes_default_path, .yes => { | |
| 1731 | fatal("The -femit-zir implementation has been intentionally deleted so that it can be rewritten as a proper backend.", .{}); | |
| 1732 | }, | |
| 1733 | } | |
| 1734 | ||
| 1735 | 1720 | const root_pkg: ?*Package = if (root_src_file) |src_path| blk: { |
| 1736 | 1721 | if (main_pkg_path) |p| { |
| 1737 | 1722 | const rel_src_path = try fs.path.relative(gpa, p, src_path); |
| ... | ... | @@ -1747,6 +1732,7 @@ fn buildOutputType( |
| 1747 | 1732 | if (root_pkg) |pkg| { |
| 1748 | 1733 | pkg.table = pkg_tree_root.table; |
| 1749 | 1734 | pkg_tree_root.table = .{}; |
| 1735 | pkg.namespace_hash = pkg_tree_root.namespace_hash; | |
| 1750 | 1736 | } |
| 1751 | 1737 | |
| 1752 | 1738 | const self_exe_path = try fs.selfExePathAlloc(arena); |
| ... | ... | @@ -1815,6 +1801,11 @@ fn buildOutputType( |
| 1815 | 1801 | @import("codegen/llvm/bindings.zig").ParseCommandLineOptions(argv.len, &argv); |
| 1816 | 1802 | } |
| 1817 | 1803 | |
| 1804 | const clang_passthrough_mode = switch (arg_mode) { | |
| 1805 | .cc, .cpp, .translate_c => true, | |
| 1806 | else => false, | |
| 1807 | }; | |
| 1808 | ||
| 1818 | 1809 | gimmeMoreOfThoseSweetSweetFileDescriptors(); |
| 1819 | 1810 | |
| 1820 | 1811 | const comp = Compilation.create(gpa, .{ |
| ... | ... | @@ -1886,7 +1877,7 @@ fn buildOutputType( |
| 1886 | 1877 | .function_sections = function_sections, |
| 1887 | 1878 | .self_exe_path = self_exe_path, |
| 1888 | 1879 | .thread_pool = &thread_pool, |
| 1889 | .clang_passthrough_mode = arg_mode != .build, | |
| 1880 | .clang_passthrough_mode = clang_passthrough_mode, | |
| 1890 | 1881 | .clang_preprocessor_mode = clang_preprocessor_mode, |
| 1891 | 1882 | .version = optional_version, |
| 1892 | 1883 | .libc_installation = if (libc_installation) |*lci| lci else null, |
| ... | ... | @@ -2101,8 +2092,12 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi |
| 2101 | 2092 | defer errors.deinit(comp.gpa); |
| 2102 | 2093 | |
| 2103 | 2094 | if (errors.list.len != 0) { |
| 2095 | const ttyconf: std.debug.TTY.Config = switch (comp.color) { | |
| 2096 | .auto, .on => std.debug.detectTTYConfig(), | |
| 2097 | .off => .no_color, | |
| 2098 | }; | |
| 2104 | 2099 | for (errors.list) |full_err_msg| { |
| 2105 | full_err_msg.renderToStdErr(); | |
| 2100 | full_err_msg.renderToStdErr(ttyconf); | |
| 2106 | 2101 | } |
| 2107 | 2102 | const log_text = comp.getCompileLogOutput(); |
| 2108 | 2103 | if (log_text.len != 0) { |
| ... | ... | @@ -2146,6 +2141,18 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi |
| 2146 | 2141 | } |
| 2147 | 2142 | } |
| 2148 | 2143 | |
| 2144 | fn freePkgTree(gpa: *Allocator, pkg: *Package, free_parent: bool) void { | |
| 2145 | { | |
| 2146 | var it = pkg.table.iterator(); | |
| 2147 | while (it.next()) |kv| { | |
| 2148 | freePkgTree(gpa, kv.value, true); | |
| 2149 | } | |
| 2150 | } | |
| 2151 | if (free_parent) { | |
| 2152 | pkg.destroy(gpa); | |
| 2153 | } | |
| 2154 | } | |
| 2155 | ||
| 2149 | 2156 | fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !void { |
| 2150 | 2157 | if (!build_options.have_llvm) |
| 2151 | 2158 | fatal("cannot translate-c: compiler built without LLVM extensions", .{}); |
| ... | ... | @@ -2500,6 +2507,7 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v |
| 2500 | 2507 | .handle = try zig_lib_directory.handle.openDir(std_special, .{}), |
| 2501 | 2508 | }, |
| 2502 | 2509 | .root_src_path = "build_runner.zig", |
| 2510 | .namespace_hash = Package.root_namespace_hash, | |
| 2503 | 2511 | }; |
| 2504 | 2512 | defer root_pkg.root_src_directory.handle.close(); |
| 2505 | 2513 | |
| ... | ... | @@ -2545,8 +2553,9 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v |
| 2545 | 2553 | var build_pkg: Package = .{ |
| 2546 | 2554 | .root_src_directory = build_directory, |
| 2547 | 2555 | .root_src_path = build_zig_basename, |
| 2556 | .namespace_hash = undefined, | |
| 2548 | 2557 | }; |
| 2549 | try root_pkg.table.put(arena, "@build", &build_pkg); | |
| 2558 | try root_pkg.addAndAdopt(arena, "@build", &build_pkg); | |
| 2550 | 2559 | |
| 2551 | 2560 | var global_cache_directory: Compilation.Directory = l: { |
| 2552 | 2561 | const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); |
src/register_manager.zig+89-2| ... | ... | @@ -35,6 +35,10 @@ pub fn RegisterManager( |
| 35 | 35 | self.registers.deinit(allocator); |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | fn isTracked(reg: Register) bool { | |
| 39 | return std.mem.indexOfScalar(Register, callee_preserved_regs, reg) != null; | |
| 40 | } | |
| 41 | ||
| 38 | 42 | fn markRegUsed(self: *Self, reg: Register) void { |
| 39 | 43 | if (FreeRegInt == u0) return; |
| 40 | 44 | const index = reg.allocIndex() orelse return; |
| ... | ... | @@ -51,6 +55,13 @@ pub fn RegisterManager( |
| 51 | 55 | self.free_registers |= @as(FreeRegInt, 1) << shift; |
| 52 | 56 | } |
| 53 | 57 | |
| 58 | pub fn isRegFree(self: Self, reg: Register) bool { | |
| 59 | if (FreeRegInt == u0) return true; | |
| 60 | const index = reg.allocIndex() orelse return true; | |
| 61 | const shift = @intCast(ShiftInt, index); | |
| 62 | return self.free_registers & @as(FreeRegInt, 1) << shift != 0; | |
| 63 | } | |
| 64 | ||
| 54 | 65 | /// Returns whether this register was allocated in the course |
| 55 | 66 | /// of this function |
| 56 | 67 | pub fn isRegAllocated(self: Self, reg: Register) bool { |
| ... | ... | @@ -117,17 +128,61 @@ pub fn RegisterManager( |
| 117 | 128 | const regs_entry = self.registers.remove(reg).?; |
| 118 | 129 | const spilled_inst = regs_entry.value; |
| 119 | 130 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); |
| 131 | self.markRegFree(reg); | |
| 120 | 132 | |
| 121 | 133 | break :b reg; |
| 122 | 134 | }; |
| 123 | 135 | } |
| 124 | 136 | |
| 125 | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) !void { | |
| 126 | try self.registers.putNoClobber(self.getFunction().gpa, reg, inst); | |
| 137 | /// Allocates the specified register with the specified | |
| 138 | /// instruction. Spills the register if it is currently | |
| 139 | /// allocated. | |
| 140 | /// Before calling, must ensureCapacity + 1 on self.registers. | |
| 141 | pub fn getReg(self: *Self, reg: Register, inst: *ir.Inst) !void { | |
| 142 | if (!isTracked(reg)) return; | |
| 143 | ||
| 144 | if (!self.isRegFree(reg)) { | |
| 145 | // Move the instruction that was previously there to a | |
| 146 | // stack allocation. | |
| 147 | const regs_entry = self.registers.getEntry(reg).?; | |
| 148 | const spilled_inst = regs_entry.value; | |
| 149 | regs_entry.value = inst; | |
| 150 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | |
| 151 | } else { | |
| 152 | self.getRegAssumeFree(reg, inst); | |
| 153 | } | |
| 154 | } | |
| 155 | ||
| 156 | /// Spills the register if it is currently allocated. | |
| 157 | /// Does not track the register. | |
| 158 | pub fn getRegWithoutTracking(self: *Self, reg: Register) !void { | |
| 159 | if (!isTracked(reg)) return; | |
| 160 | ||
| 161 | if (!self.isRegFree(reg)) { | |
| 162 | // Move the instruction that was previously there to a | |
| 163 | // stack allocation. | |
| 164 | const regs_entry = self.registers.remove(reg).?; | |
| 165 | const spilled_inst = regs_entry.value; | |
| 166 | try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); | |
| 167 | self.markRegFree(reg); | |
| 168 | } | |
| 169 | } | |
| 170 | ||
| 171 | /// Allocates the specified register with the specified | |
| 172 | /// instruction. Assumes that the register is free and no | |
| 173 | /// spilling is necessary. | |
| 174 | /// Before calling, must ensureCapacity + 1 on self.registers. | |
| 175 | pub fn getRegAssumeFree(self: *Self, reg: Register, inst: *ir.Inst) void { | |
| 176 | if (!isTracked(reg)) return; | |
| 177 | ||
| 178 | self.registers.putAssumeCapacityNoClobber(reg, inst); | |
| 127 | 179 | self.markRegUsed(reg); |
| 128 | 180 | } |
| 129 | 181 | |
| 182 | /// Marks the specified register as free | |
| 130 | 183 | pub fn freeReg(self: *Self, reg: Register) void { |
| 184 | if (!isTracked(reg)) return; | |
| 185 | ||
| 131 | 186 | _ = self.registers.remove(reg); |
| 132 | 187 | self.markRegFree(reg); |
| 133 | 188 | } |
| ... | ... | @@ -226,3 +281,35 @@ test "allocReg: spilling" { |
| 226 | 281 | std.testing.expectEqual(@as(?MockRegister, .r3), try function.register_manager.allocReg(&mock_instruction)); |
| 227 | 282 | std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r2}, function.spilled.items); |
| 228 | 283 | } |
| 284 | ||
| 285 | test "getReg" { | |
| 286 | const allocator = std.testing.allocator; | |
| 287 | ||
| 288 | var function = MockFunction{ | |
| 289 | .allocator = allocator, | |
| 290 | }; | |
| 291 | defer function.deinit(); | |
| 292 | ||
| 293 | var mock_instruction = ir.Inst{ | |
| 294 | .tag = .breakpoint, | |
| 295 | .ty = Type.initTag(.void), | |
| 296 | .src = .unneeded, | |
| 297 | }; | |
| 298 | ||
| 299 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | |
| 300 | std.testing.expect(!function.register_manager.isRegAllocated(.r3)); | |
| 301 | ||
| 302 | try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); | |
| 303 | try function.register_manager.getReg(.r3, &mock_instruction); | |
| 304 | ||
| 305 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | |
| 306 | std.testing.expect(function.register_manager.isRegAllocated(.r3)); | |
| 307 | ||
| 308 | // Spill r3 | |
| 309 | try function.register_manager.registers.ensureCapacity(allocator, function.register_manager.registers.count() + 2); | |
| 310 | try function.register_manager.getReg(.r3, &mock_instruction); | |
| 311 | ||
| 312 | std.testing.expect(!function.register_manager.isRegAllocated(.r2)); | |
| 313 | std.testing.expect(function.register_manager.isRegAllocated(.r3)); | |
| 314 | std.testing.expectEqualSlices(MockRegister, &[_]MockRegister{.r3}, function.spilled.items); | |
| 315 | } |
src/stage1/codegen.cpp+1| ... | ... | @@ -9110,6 +9110,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { |
| 9110 | 9110 | buf_appendf(contents, "pub const position_independent_executable = %s;\n", bool_to_str(g->have_pie)); |
| 9111 | 9111 | buf_appendf(contents, "pub const strip_debug_info = %s;\n", bool_to_str(g->strip_debug_symbols)); |
| 9112 | 9112 | buf_appendf(contents, "pub const code_model = CodeModel.default;\n"); |
| 9113 | buf_appendf(contents, "pub const zig_is_stage2 = false;\n"); | |
| 9113 | 9114 | |
| 9114 | 9115 | { |
| 9115 | 9116 | TargetSubsystem detected_subsystem = detect_subsystem(g); |
src/test.zig+21-42| ... | ... | @@ -122,11 +122,6 @@ pub const TestContext = struct { |
| 122 | 122 | path: []const u8, |
| 123 | 123 | }; |
| 124 | 124 | |
| 125 | pub const Extension = enum { | |
| 126 | Zig, | |
| 127 | ZIR, | |
| 128 | }; | |
| 129 | ||
| 130 | 125 | /// A `Case` consists of a list of `Update`. The same `Compilation` is used for each |
| 131 | 126 | /// update, so each update's source is treated as a single file being |
| 132 | 127 | /// updated by the test harness and incrementally compiled. |
| ... | ... | @@ -141,7 +136,6 @@ pub const TestContext = struct { |
| 141 | 136 | /// to Executable. |
| 142 | 137 | output_mode: std.builtin.OutputMode, |
| 143 | 138 | updates: std.ArrayList(Update), |
| 144 | extension: Extension, | |
| 145 | 139 | object_format: ?std.builtin.ObjectFormat = null, |
| 146 | 140 | emit_h: bool = false, |
| 147 | 141 | llvm_backend: bool = false, |
| ... | ... | @@ -238,14 +232,12 @@ pub const TestContext = struct { |
| 238 | 232 | ctx: *TestContext, |
| 239 | 233 | name: []const u8, |
| 240 | 234 | target: CrossTarget, |
| 241 | extension: Extension, | |
| 242 | 235 | ) *Case { |
| 243 | 236 | ctx.cases.append(Case{ |
| 244 | 237 | .name = name, |
| 245 | 238 | .target = target, |
| 246 | 239 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 247 | 240 | .output_mode = .Exe, |
| 248 | .extension = extension, | |
| 249 | 241 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 250 | 242 | }) catch @panic("out of memory"); |
| 251 | 243 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| ... | ... | @@ -253,7 +245,7 @@ pub const TestContext = struct { |
| 253 | 245 | |
| 254 | 246 | /// Adds a test case for Zig input, producing an executable |
| 255 | 247 | pub fn exe(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { |
| 256 | return ctx.addExe(name, target, .Zig); | |
| 248 | return ctx.addExe(name, target); | |
| 257 | 249 | } |
| 258 | 250 | |
| 259 | 251 | /// Adds a test case for ZIR input, producing an executable |
| ... | ... | @@ -269,7 +261,6 @@ pub const TestContext = struct { |
| 269 | 261 | .target = target, |
| 270 | 262 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 271 | 263 | .output_mode = .Exe, |
| 272 | .extension = .Zig, | |
| 273 | 264 | .object_format = .c, |
| 274 | 265 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 275 | 266 | }) catch @panic("out of memory"); |
| ... | ... | @@ -284,7 +275,6 @@ pub const TestContext = struct { |
| 284 | 275 | .target = target, |
| 285 | 276 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 286 | 277 | .output_mode = .Exe, |
| 287 | .extension = .Zig, | |
| 288 | 278 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 289 | 279 | .llvm_backend = true, |
| 290 | 280 | }) catch @panic("out of memory"); |
| ... | ... | @@ -295,14 +285,12 @@ pub const TestContext = struct { |
| 295 | 285 | ctx: *TestContext, |
| 296 | 286 | name: []const u8, |
| 297 | 287 | target: CrossTarget, |
| 298 | extension: Extension, | |
| 299 | 288 | ) *Case { |
| 300 | 289 | ctx.cases.append(Case{ |
| 301 | 290 | .name = name, |
| 302 | 291 | .target = target, |
| 303 | 292 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 304 | 293 | .output_mode = .Obj, |
| 305 | .extension = extension, | |
| 306 | 294 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 307 | 295 | }) catch @panic("out of memory"); |
| 308 | 296 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| ... | ... | @@ -310,7 +298,7 @@ pub const TestContext = struct { |
| 310 | 298 | |
| 311 | 299 | /// Adds a test case for Zig input, producing an object file. |
| 312 | 300 | pub fn obj(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { |
| 313 | return ctx.addObj(name, target, .Zig); | |
| 301 | return ctx.addObj(name, target); | |
| 314 | 302 | } |
| 315 | 303 | |
| 316 | 304 | /// Adds a test case for ZIR input, producing an object file. |
| ... | ... | @@ -319,13 +307,12 @@ pub const TestContext = struct { |
| 319 | 307 | } |
| 320 | 308 | |
| 321 | 309 | /// Adds a test case for Zig or ZIR input, producing C code. |
| 322 | pub fn addC(ctx: *TestContext, name: []const u8, target: CrossTarget, ext: Extension) *Case { | |
| 310 | pub fn addC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case { | |
| 323 | 311 | ctx.cases.append(Case{ |
| 324 | 312 | .name = name, |
| 325 | 313 | .target = target, |
| 326 | 314 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 327 | 315 | .output_mode = .Obj, |
| 328 | .extension = ext, | |
| 329 | 316 | .object_format = .c, |
| 330 | 317 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 331 | 318 | }) catch @panic("out of memory"); |
| ... | ... | @@ -333,21 +320,20 @@ pub const TestContext = struct { |
| 333 | 320 | } |
| 334 | 321 | |
| 335 | 322 | pub fn c(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void { |
| 336 | ctx.addC(name, target, .Zig).addCompareObjectFile(src, zig_h ++ out); | |
| 323 | ctx.addC(name, target).addCompareObjectFile(src, zig_h ++ out); | |
| 337 | 324 | } |
| 338 | 325 | |
| 339 | 326 | pub fn h(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void { |
| 340 | ctx.addC(name, target, .Zig).addHeader(src, zig_h ++ out); | |
| 327 | ctx.addC(name, target).addHeader(src, zig_h ++ out); | |
| 341 | 328 | } |
| 342 | 329 | |
| 343 | 330 | pub fn addCompareOutput( |
| 344 | 331 | ctx: *TestContext, |
| 345 | 332 | name: []const u8, |
| 346 | extension: Extension, | |
| 347 | 333 | src: [:0]const u8, |
| 348 | 334 | expected_stdout: []const u8, |
| 349 | 335 | ) void { |
| 350 | ctx.addExe(name, .{}, extension).addCompareOutput(src, expected_stdout); | |
| 336 | ctx.addExe(name, .{}).addCompareOutput(src, expected_stdout); | |
| 351 | 337 | } |
| 352 | 338 | |
| 353 | 339 | /// Adds a test case that compiles the Zig source given in `src`, executes |
| ... | ... | @@ -358,7 +344,7 @@ pub const TestContext = struct { |
| 358 | 344 | src: [:0]const u8, |
| 359 | 345 | expected_stdout: []const u8, |
| 360 | 346 | ) void { |
| 361 | return ctx.addCompareOutput(name, .Zig, src, expected_stdout); | |
| 347 | return ctx.addCompareOutput(name, src, expected_stdout); | |
| 362 | 348 | } |
| 363 | 349 | |
| 364 | 350 | /// Adds a test case that compiles the ZIR source given in `src`, executes |
| ... | ... | @@ -376,11 +362,10 @@ pub const TestContext = struct { |
| 376 | 362 | ctx: *TestContext, |
| 377 | 363 | name: []const u8, |
| 378 | 364 | target: CrossTarget, |
| 379 | extension: Extension, | |
| 380 | 365 | src: [:0]const u8, |
| 381 | 366 | result: [:0]const u8, |
| 382 | 367 | ) void { |
| 383 | ctx.addObj(name, target, extension).addTransform(src, result); | |
| 368 | ctx.addObj(name, target).addTransform(src, result); | |
| 384 | 369 | } |
| 385 | 370 | |
| 386 | 371 | /// Adds a test case that compiles the Zig given in `src` to ZIR and tests |
| ... | ... | @@ -392,7 +377,7 @@ pub const TestContext = struct { |
| 392 | 377 | src: [:0]const u8, |
| 393 | 378 | result: [:0]const u8, |
| 394 | 379 | ) void { |
| 395 | ctx.addTransform(name, target, .Zig, src, result); | |
| 380 | ctx.addTransform(name, target, src, result); | |
| 396 | 381 | } |
| 397 | 382 | |
| 398 | 383 | /// Adds a test case that cleans up the ZIR source given in `src`, and |
| ... | ... | @@ -411,11 +396,10 @@ pub const TestContext = struct { |
| 411 | 396 | ctx: *TestContext, |
| 412 | 397 | name: []const u8, |
| 413 | 398 | target: CrossTarget, |
| 414 | extension: Extension, | |
| 415 | 399 | src: [:0]const u8, |
| 416 | 400 | expected_errors: []const []const u8, |
| 417 | 401 | ) void { |
| 418 | ctx.addObj(name, target, extension).addError(src, expected_errors); | |
| 402 | ctx.addObj(name, target).addError(src, expected_errors); | |
| 419 | 403 | } |
| 420 | 404 | |
| 421 | 405 | /// Adds a test case that ensures that the Zig given in `src` fails to |
| ... | ... | @@ -428,7 +412,7 @@ pub const TestContext = struct { |
| 428 | 412 | src: [:0]const u8, |
| 429 | 413 | expected_errors: []const []const u8, |
| 430 | 414 | ) void { |
| 431 | ctx.addError(name, target, .Zig, src, expected_errors); | |
| 415 | ctx.addError(name, target, src, expected_errors); | |
| 432 | 416 | } |
| 433 | 417 | |
| 434 | 418 | /// Adds a test case that ensures that the ZIR given in `src` fails to |
| ... | ... | @@ -448,10 +432,9 @@ pub const TestContext = struct { |
| 448 | 432 | ctx: *TestContext, |
| 449 | 433 | name: []const u8, |
| 450 | 434 | target: CrossTarget, |
| 451 | extension: Extension, | |
| 452 | 435 | src: [:0]const u8, |
| 453 | 436 | ) void { |
| 454 | ctx.addObj(name, target, extension).compiles(src); | |
| 437 | ctx.addObj(name, target).compiles(src); | |
| 455 | 438 | } |
| 456 | 439 | |
| 457 | 440 | /// Adds a test case that asserts that the Zig given in `src` compiles |
| ... | ... | @@ -462,7 +445,7 @@ pub const TestContext = struct { |
| 462 | 445 | target: CrossTarget, |
| 463 | 446 | src: [:0]const u8, |
| 464 | 447 | ) void { |
| 465 | ctx.addCompiles(name, target, .Zig, src); | |
| 448 | ctx.addCompiles(name, target, src); | |
| 466 | 449 | } |
| 467 | 450 | |
| 468 | 451 | /// Adds a test case that asserts that the ZIR given in `src` compiles |
| ... | ... | @@ -489,7 +472,7 @@ pub const TestContext = struct { |
| 489 | 472 | expected_errors: []const []const u8, |
| 490 | 473 | fixed_src: [:0]const u8, |
| 491 | 474 | ) void { |
| 492 | var case = ctx.addObj(name, target, .Zig); | |
| 475 | var case = ctx.addObj(name, target); | |
| 493 | 476 | case.addError(src, expected_errors); |
| 494 | 477 | case.compiles(fixed_src); |
| 495 | 478 | } |
| ... | ... | @@ -614,15 +597,14 @@ pub const TestContext = struct { |
| 614 | 597 | .path = try std.fs.path.join(arena, &[_][]const u8{ tmp_dir_path, "zig-cache" }), |
| 615 | 598 | }; |
| 616 | 599 | |
| 617 | const tmp_src_path = switch (case.extension) { | |
| 618 | .Zig => "test_case.zig", | |
| 619 | .ZIR => "test_case.zir", | |
| 620 | }; | |
| 600 | const tmp_src_path = "test_case.zig"; | |
| 621 | 601 | |
| 622 | 602 | var root_pkg: Package = .{ |
| 623 | 603 | .root_src_directory = .{ .path = tmp_dir_path, .handle = tmp.dir }, |
| 624 | 604 | .root_src_path = tmp_src_path, |
| 605 | .namespace_hash = Package.root_namespace_hash, | |
| 625 | 606 | }; |
| 607 | defer root_pkg.table.deinit(allocator); | |
| 626 | 608 | |
| 627 | 609 | const bin_name = try std.zig.binNameAlloc(arena, .{ |
| 628 | 610 | .root_name = "test_case", |
| ... | ... | @@ -639,13 +621,10 @@ pub const TestContext = struct { |
| 639 | 621 | .directory = emit_directory, |
| 640 | 622 | .basename = bin_name, |
| 641 | 623 | }; |
| 642 | const emit_h: ?Compilation.EmitLoc = if (case.emit_h) | |
| 643 | .{ | |
| 644 | .directory = emit_directory, | |
| 645 | .basename = "test_case.h", | |
| 646 | } | |
| 647 | else | |
| 648 | null; | |
| 624 | const emit_h: ?Compilation.EmitLoc = if (case.emit_h) .{ | |
| 625 | .directory = emit_directory, | |
| 626 | .basename = "test_case.h", | |
| 627 | } else null; | |
| 649 | 628 | const comp = try Compilation.create(allocator, .{ |
| 650 | 629 | .local_cache_directory = zig_cache_directory, |
| 651 | 630 | .global_cache_directory = global_cache_directory, |
src/translate_c.zig+233-13| ... | ... | @@ -1123,6 +1123,16 @@ fn transStmt( |
| 1123 | 1123 | const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt); |
| 1124 | 1124 | return transExpr(c, scope, gen_sel.getResultExpr(), result_used); |
| 1125 | 1125 | }, |
| 1126 | .ConvertVectorExprClass => { | |
| 1127 | const conv_vec = @ptrCast(*const clang.ConvertVectorExpr, stmt); | |
| 1128 | const conv_vec_node = try transConvertVectorExpr(c, scope, stmt.getBeginLoc(), conv_vec); | |
| 1129 | return maybeSuppressResult(c, scope, result_used, conv_vec_node); | |
| 1130 | }, | |
| 1131 | .ShuffleVectorExprClass => { | |
| 1132 | const shuffle_vec_expr = @ptrCast(*const clang.ShuffleVectorExpr, stmt); | |
| 1133 | const shuffle_vec_node = try transShuffleVectorExpr(c, scope, shuffle_vec_expr); | |
| 1134 | return maybeSuppressResult(c, scope, result_used, shuffle_vec_node); | |
| 1135 | }, | |
| 1126 | 1136 | // When adding new cases here, see comment for maybeBlockify() |
| 1127 | 1137 | else => { |
| 1128 | 1138 | return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)}); |
| ... | ... | @@ -1130,6 +1140,128 @@ fn transStmt( |
| 1130 | 1140 | } |
| 1131 | 1141 | } |
| 1132 | 1142 | |
| 1143 | /// See https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-convertvector | |
| 1144 | fn transConvertVectorExpr( | |
| 1145 | c: *Context, | |
| 1146 | scope: *Scope, | |
| 1147 | source_loc: clang.SourceLocation, | |
| 1148 | expr: *const clang.ConvertVectorExpr, | |
| 1149 | ) TransError!Node { | |
| 1150 | const base_stmt = @ptrCast(*const clang.Stmt, expr); | |
| 1151 | ||
| 1152 | var block_scope = try Scope.Block.init(c, scope, true); | |
| 1153 | defer block_scope.deinit(); | |
| 1154 | ||
| 1155 | const src_expr = expr.getSrcExpr(); | |
| 1156 | const src_type = qualTypeCanon(src_expr.getType()); | |
| 1157 | const src_vector_ty = @ptrCast(*const clang.VectorType, src_type); | |
| 1158 | const src_element_qt = src_vector_ty.getElementType(); | |
| 1159 | const src_element_type_node = try transQualType(c, &block_scope.base, src_element_qt, base_stmt.getBeginLoc()); | |
| 1160 | ||
| 1161 | const src_expr_node = try transExpr(c, &block_scope.base, src_expr, .used); | |
| 1162 | ||
| 1163 | const dst_qt = expr.getTypeSourceInfo_getType(); | |
| 1164 | const dst_type_node = try transQualType(c, &block_scope.base, dst_qt, base_stmt.getBeginLoc()); | |
| 1165 | const dst_vector_ty = @ptrCast(*const clang.VectorType, qualTypeCanon(dst_qt)); | |
| 1166 | const num_elements = dst_vector_ty.getNumElements(); | |
| 1167 | const dst_element_qt = dst_vector_ty.getElementType(); | |
| 1168 | ||
| 1169 | // workaround for https://github.com/ziglang/zig/issues/8322 | |
| 1170 | // we store the casted results into temp variables and use those | |
| 1171 | // to initialize the vector. Eventually we can just directly | |
| 1172 | // construct the init_list from casted source members | |
| 1173 | var i: usize = 0; | |
| 1174 | while (i < num_elements) : (i += 1) { | |
| 1175 | const mangled_name = try block_scope.makeMangledName(c, "tmp"); | |
| 1176 | const value = try Tag.array_access.create(c.arena, .{ | |
| 1177 | .lhs = src_expr_node, | |
| 1178 | .rhs = try transCreateNodeNumber(c, i, .int), | |
| 1179 | }); | |
| 1180 | const tmp_decl_node = try Tag.var_simple.create(c.arena, .{ | |
| 1181 | .name = mangled_name, | |
| 1182 | .init = try transCCast(c, &block_scope.base, base_stmt.getBeginLoc(), dst_element_qt, src_element_qt, value), | |
| 1183 | }); | |
| 1184 | try block_scope.statements.append(tmp_decl_node); | |
| 1185 | } | |
| 1186 | ||
| 1187 | const init_list = try c.arena.alloc(Node, num_elements); | |
| 1188 | for (init_list) |*init, init_index| { | |
| 1189 | const tmp_decl = block_scope.statements.items[init_index]; | |
| 1190 | const name = tmp_decl.castTag(.var_simple).?.data.name; | |
| 1191 | init.* = try Tag.identifier.create(c.arena, name); | |
| 1192 | } | |
| 1193 | ||
| 1194 | const vec_init = try Tag.array_init.create(c.arena, .{ | |
| 1195 | .cond = dst_type_node, | |
| 1196 | .cases = init_list, | |
| 1197 | }); | |
| 1198 | ||
| 1199 | const break_node = try Tag.break_val.create(c.arena, .{ | |
| 1200 | .label = block_scope.label, | |
| 1201 | .val = vec_init, | |
| 1202 | }); | |
| 1203 | try block_scope.statements.append(break_node); | |
| 1204 | return block_scope.complete(c); | |
| 1205 | } | |
| 1206 | ||
| 1207 | fn makeShuffleMask(c: *Context, scope: *Scope, expr: *const clang.ShuffleVectorExpr, vector_len: Node) TransError!Node { | |
| 1208 | const num_subexprs = expr.getNumSubExprs(); | |
| 1209 | assert(num_subexprs >= 3); // two source vectors + at least 1 index expression | |
| 1210 | const mask_len = num_subexprs - 2; | |
| 1211 | ||
| 1212 | const mask_type = try Tag.std_meta_vector.create(c.arena, .{ | |
| 1213 | .lhs = try transCreateNodeNumber(c, mask_len, .int), | |
| 1214 | .rhs = try Tag.type.create(c.arena, "i32"), | |
| 1215 | }); | |
| 1216 | ||
| 1217 | const init_list = try c.arena.alloc(Node, mask_len); | |
| 1218 | ||
| 1219 | for (init_list) |*init, i| { | |
| 1220 | const index_expr = try transExprCoercing(c, scope, expr.getExpr(@intCast(c_uint, i + 2)), .used); | |
| 1221 | const converted_index = try Tag.std_meta_shuffle_vector_index.create(c.arena, .{ .lhs = index_expr, .rhs = vector_len }); | |
| 1222 | init.* = converted_index; | |
| 1223 | } | |
| 1224 | ||
| 1225 | const mask_init = try Tag.array_init.create(c.arena, .{ | |
| 1226 | .cond = mask_type, | |
| 1227 | .cases = init_list, | |
| 1228 | }); | |
| 1229 | return Tag.@"comptime".create(c.arena, mask_init); | |
| 1230 | } | |
| 1231 | ||
| 1232 | /// @typeInfo(@TypeOf(vec_node)).Vector.<field> | |
| 1233 | fn vectorTypeInfo(arena: *mem.Allocator, vec_node: Node, field: []const u8) TransError!Node { | |
| 1234 | const typeof_call = try Tag.typeof.create(arena, vec_node); | |
| 1235 | const typeinfo_call = try Tag.typeinfo.create(arena, typeof_call); | |
| 1236 | const vector_type_info = try Tag.field_access.create(arena, .{ .lhs = typeinfo_call, .field_name = "Vector" }); | |
| 1237 | return Tag.field_access.create(arena, .{ .lhs = vector_type_info, .field_name = field }); | |
| 1238 | } | |
| 1239 | ||
| 1240 | fn transShuffleVectorExpr( | |
| 1241 | c: *Context, | |
| 1242 | scope: *Scope, | |
| 1243 | expr: *const clang.ShuffleVectorExpr, | |
| 1244 | ) TransError!Node { | |
| 1245 | const base_expr = @ptrCast(*const clang.Expr, expr); | |
| 1246 | const num_subexprs = expr.getNumSubExprs(); | |
| 1247 | if (num_subexprs < 3) return fail(c, error.UnsupportedTranslation, base_expr.getBeginLoc(), "ShuffleVector needs at least 1 index", .{}); | |
| 1248 | ||
| 1249 | const a = try transExpr(c, scope, expr.getExpr(0), .used); | |
| 1250 | const b = try transExpr(c, scope, expr.getExpr(1), .used); | |
| 1251 | ||
| 1252 | // clang requires first two arguments to __builtin_shufflevector to be same type | |
| 1253 | const vector_child_type = try vectorTypeInfo(c.arena, a, "child"); | |
| 1254 | const vector_len = try vectorTypeInfo(c.arena, a, "len"); | |
| 1255 | const shuffle_mask = try makeShuffleMask(c, scope, expr, vector_len); | |
| 1256 | ||
| 1257 | return Tag.shuffle.create(c.arena, .{ | |
| 1258 | .element_type = vector_child_type, | |
| 1259 | .a = a, | |
| 1260 | .b = b, | |
| 1261 | .mask_vector = shuffle_mask, | |
| 1262 | }); | |
| 1263 | } | |
| 1264 | ||
| 1133 | 1265 | /// Translate a "simple" offsetof expression containing exactly one component, |
| 1134 | 1266 | /// when that component is of kind .Field - e.g. offsetof(mytype, myfield) |
| 1135 | 1267 | fn transSimpleOffsetOfExpr( |
| ... | ... | @@ -1935,6 +2067,10 @@ fn cIsEnum(qt: clang.QualType) bool { |
| 1935 | 2067 | return qt.getCanonicalType().getTypeClass() == .Enum; |
| 1936 | 2068 | } |
| 1937 | 2069 | |
| 2070 | fn cIsVector(qt: clang.QualType) bool { | |
| 2071 | return qt.getCanonicalType().getTypeClass() == .Vector; | |
| 2072 | } | |
| 2073 | ||
| 1938 | 2074 | /// Get the underlying int type of an enum. The C compiler chooses a signed int |
| 1939 | 2075 | /// type that is large enough to hold all of the enum's values. It is not required |
| 1940 | 2076 | /// to be the smallest possible type that can hold all the values. |
| ... | ... | @@ -1991,6 +2127,11 @@ fn transCCast( |
| 1991 | 2127 | // @bitCast(dest_type, intermediate_value) |
| 1992 | 2128 | return Tag.bit_cast.create(c.arena, .{ .lhs = dst_node, .rhs = src_int_expr }); |
| 1993 | 2129 | } |
| 2130 | if (cIsVector(src_type) or cIsVector(dst_type)) { | |
| 2131 | // C cast where at least 1 operand is a vector requires them to be same size | |
| 2132 | // @bitCast(dest_type, val) | |
| 2133 | return Tag.bit_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); | |
| 2134 | } | |
| 1994 | 2135 | if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) { |
| 1995 | 2136 | // @intCast(dest_type, @ptrToInt(val)) |
| 1996 | 2137 | const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr); |
| ... | ... | @@ -2209,6 +2350,63 @@ fn transInitListExprArray( |
| 2209 | 2350 | } |
| 2210 | 2351 | } |
| 2211 | 2352 | |
| 2353 | fn transInitListExprVector( | |
| 2354 | c: *Context, | |
| 2355 | scope: *Scope, | |
| 2356 | loc: clang.SourceLocation, | |
| 2357 | expr: *const clang.InitListExpr, | |
| 2358 | ty: *const clang.Type, | |
| 2359 | ) TransError!Node { | |
| 2360 | ||
| 2361 | const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr)); | |
| 2362 | const vector_type = try transQualType(c, scope, qt, loc); | |
| 2363 | const init_count = expr.getNumInits(); | |
| 2364 | ||
| 2365 | if (init_count == 0) { | |
| 2366 | return Tag.container_init.create(c.arena, .{ | |
| 2367 | .lhs = vector_type, | |
| 2368 | .inits = try c.arena.alloc(ast.Payload.ContainerInit.Initializer, 0), | |
| 2369 | }); | |
| 2370 | } | |
| 2371 | ||
| 2372 | var block_scope = try Scope.Block.init(c, scope, true); | |
| 2373 | defer block_scope.deinit(); | |
| 2374 | ||
| 2375 | // workaround for https://github.com/ziglang/zig/issues/8322 | |
| 2376 | // we store the initializers in temp variables and use those | |
| 2377 | // to initialize the vector. Eventually we can just directly | |
| 2378 | // construct the init_list from casted source members | |
| 2379 | var i: usize = 0; | |
| 2380 | while (i < init_count) : (i += 1) { | |
| 2381 | const mangled_name = try block_scope.makeMangledName(c, "tmp"); | |
| 2382 | const init_expr = expr.getInit(@intCast(c_uint, i)); | |
| 2383 | const tmp_decl_node = try Tag.var_simple.create(c.arena, .{ | |
| 2384 | .name = mangled_name, | |
| 2385 | .init = try transExpr(c, &block_scope.base, init_expr, .used), | |
| 2386 | }); | |
| 2387 | try block_scope.statements.append(tmp_decl_node); | |
| 2388 | } | |
| 2389 | ||
| 2390 | const init_list = try c.arena.alloc(Node, init_count); | |
| 2391 | for (init_list) |*init, init_index| { | |
| 2392 | const tmp_decl = block_scope.statements.items[init_index]; | |
| 2393 | const name = tmp_decl.castTag(.var_simple).?.data.name; | |
| 2394 | init.* = try Tag.identifier.create(c.arena, name); | |
| 2395 | } | |
| 2396 | ||
| 2397 | const array_init = try Tag.array_init.create(c.arena, .{ | |
| 2398 | .cond = vector_type, | |
| 2399 | .cases = init_list, | |
| 2400 | }); | |
| 2401 | const break_node = try Tag.break_val.create(c.arena, .{ | |
| 2402 | .label = block_scope.label, | |
| 2403 | .val = array_init, | |
| 2404 | }); | |
| 2405 | try block_scope.statements.append(break_node); | |
| 2406 | ||
| 2407 | return block_scope.complete(c); | |
| 2408 | } | |
| 2409 | ||
| 2212 | 2410 | fn transInitListExpr( |
| 2213 | 2411 | c: *Context, |
| 2214 | 2412 | scope: *Scope, |
| ... | ... | @@ -2235,6 +2433,14 @@ fn transInitListExpr( |
| 2235 | 2433 | expr, |
| 2236 | 2434 | qual_type, |
| 2237 | 2435 | )); |
| 2436 | } else if (qual_type.isVectorType()) { | |
| 2437 | return maybeSuppressResult(c, scope, used, try transInitListExprVector( | |
| 2438 | c, | |
| 2439 | scope, | |
| 2440 | source_loc, | |
| 2441 | expr, | |
| 2442 | qual_type, | |
| 2443 | )); | |
| 2238 | 2444 | } else { |
| 2239 | 2445 | const type_name = c.str(qual_type.getTypeClassName()); |
| 2240 | 2446 | return fail(c, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name}); |
| ... | ... | @@ -4085,6 +4291,15 @@ fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clan |
| 4085 | 4291 | }; |
| 4086 | 4292 | return Tag.typeof.create(c.arena, underlying_expr); |
| 4087 | 4293 | }, |
| 4294 | .Vector => { | |
| 4295 | const vector_ty = @ptrCast(*const clang.VectorType, ty); | |
| 4296 | const num_elements = vector_ty.getNumElements(); | |
| 4297 | const element_qt = vector_ty.getElementType(); | |
| 4298 | return Tag.std_meta_vector.create(c.arena, .{ | |
| 4299 | .lhs = try transCreateNodeNumber(c, num_elements, .int), | |
| 4300 | .rhs = try transQualType(c, scope, element_qt, source_loc), | |
| 4301 | }); | |
| 4302 | }, | |
| 4088 | 4303 | else => { |
| 4089 | 4304 | const type_name = c.str(ty.getTypeClassName()); |
| 4090 | 4305 | return fail(c, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name}); |
| ... | ... | @@ -5211,21 +5426,26 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { |
| 5211 | 5426 | } |
| 5212 | 5427 | }, |
| 5213 | 5428 | .LParen => { |
| 5214 | var args = std.ArrayList(Node).init(c.gpa); | |
| 5215 | defer args.deinit(); | |
| 5216 | while (true) { | |
| 5217 | const arg = try parseCCondExpr(c, m, scope); | |
| 5218 | try args.append(arg); | |
| 5219 | switch (m.next().?) { | |
| 5220 | .Comma => {}, | |
| 5221 | .RParen => break, | |
| 5222 | else => { | |
| 5223 | try m.fail(c, "unable to translate C expr: expected ',' or ')'", .{}); | |
| 5224 | return error.ParseError; | |
| 5225 | }, | |
| 5429 | if (m.peek().? == .RParen) { | |
| 5430 | m.i += 1; | |
| 5431 | node = try Tag.call.create(c.arena, .{ .lhs = node, .args = &[0]Node{} }); | |
| 5432 | } else { | |
| 5433 | var args = std.ArrayList(Node).init(c.gpa); | |
| 5434 | defer args.deinit(); | |
| 5435 | while (true) { | |
| 5436 | const arg = try parseCCondExpr(c, m, scope); | |
| 5437 | try args.append(arg); | |
| 5438 | switch (m.next().?) { | |
| 5439 | .Comma => {}, | |
| 5440 | .RParen => break, | |
| 5441 | else => { | |
| 5442 | try m.fail(c, "unable to translate C expr: expected ',' or ')'", .{}); | |
| 5443 | return error.ParseError; | |
| 5444 | }, | |
| 5445 | } | |
| 5226 | 5446 | } |
| 5447 | node = try Tag.call.create(c.arena, .{ .lhs = node, .args = try c.arena.dupe(Node, args.items) }); | |
| 5227 | 5448 | } |
| 5228 | node = try Tag.call.create(c.arena, .{ .lhs = node, .args = try c.arena.dupe(Node, args.items) }); | |
| 5229 | 5449 | }, |
| 5230 | 5450 | .LBrace => { |
| 5231 | 5451 | var init_vals = std.ArrayList(Node).init(c.gpa); |
src/translate_c/ast.zig+95-8| ... | ... | @@ -66,6 +66,7 @@ pub const Node = extern union { |
| 66 | 66 | @"enum", |
| 67 | 67 | @"struct", |
| 68 | 68 | @"union", |
| 69 | @"comptime", | |
| 69 | 70 | array_init, |
| 70 | 71 | tuple, |
| 71 | 72 | container_init, |
| ... | ... | @@ -154,6 +155,8 @@ pub const Node = extern union { |
| 154 | 155 | div_exact, |
| 155 | 156 | /// @byteOffsetOf(lhs, rhs) |
| 156 | 157 | byte_offset_of, |
| 158 | /// @shuffle(type, a, b, mask) | |
| 159 | shuffle, | |
| 157 | 160 | |
| 158 | 161 | negate, |
| 159 | 162 | negate_wrap, |
| ... | ... | @@ -172,6 +175,7 @@ pub const Node = extern union { |
| 172 | 175 | sizeof, |
| 173 | 176 | alignof, |
| 174 | 177 | typeof, |
| 178 | typeinfo, | |
| 175 | 179 | type, |
| 176 | 180 | |
| 177 | 181 | optional_type, |
| ... | ... | @@ -182,6 +186,10 @@ pub const Node = extern union { |
| 182 | 186 | |
| 183 | 187 | /// @import("std").meta.sizeof(operand) |
| 184 | 188 | std_meta_sizeof, |
| 189 | /// @import("std").meta.shuffleVectorIndex(lhs, rhs) | |
| 190 | std_meta_shuffle_vector_index, | |
| 191 | /// @import("std").meta.Vector(lhs, rhs) | |
| 192 | std_meta_vector, | |
| 185 | 193 | /// @import("std").mem.zeroes(operand) |
| 186 | 194 | std_mem_zeroes, |
| 187 | 195 | /// @import("std").mem.zeroInit(lhs, rhs) |
| ... | ... | @@ -233,6 +241,7 @@ pub const Node = extern union { |
| 233 | 241 | |
| 234 | 242 | .std_mem_zeroes, |
| 235 | 243 | .@"return", |
| 244 | .@"comptime", | |
| 236 | 245 | .discard, |
| 237 | 246 | .std_math_Log2Int, |
| 238 | 247 | .negate, |
| ... | ... | @@ -255,6 +264,7 @@ pub const Node = extern union { |
| 255 | 264 | .sizeof, |
| 256 | 265 | .alignof, |
| 257 | 266 | .typeof, |
| 267 | .typeinfo, | |
| 258 | 268 | => Payload.UnOp, |
| 259 | 269 | |
| 260 | 270 | .add, |
| ... | ... | @@ -308,6 +318,8 @@ pub const Node = extern union { |
| 308 | 318 | .align_cast, |
| 309 | 319 | .array_access, |
| 310 | 320 | .std_mem_zeroinit, |
| 321 | .std_meta_shuffle_vector_index, | |
| 322 | .std_meta_vector, | |
| 311 | 323 | .ptr_cast, |
| 312 | 324 | .div_exact, |
| 313 | 325 | .byte_offset_of, |
| ... | ... | @@ -346,6 +358,7 @@ pub const Node = extern union { |
| 346 | 358 | .pub_inline_fn => Payload.PubInlineFn, |
| 347 | 359 | .field_access => Payload.FieldAccess, |
| 348 | 360 | .string_slice => Payload.StringSlice, |
| 361 | .shuffle => Payload.Shuffle, | |
| 349 | 362 | }; |
| 350 | 363 | } |
| 351 | 364 | |
| ... | ... | @@ -678,6 +691,16 @@ pub const Payload = struct { |
| 678 | 691 | end: usize, |
| 679 | 692 | }, |
| 680 | 693 | }; |
| 694 | ||
| 695 | pub const Shuffle = struct { | |
| 696 | base: Payload, | |
| 697 | data: struct { | |
| 698 | element_type: Node, | |
| 699 | a: Node, | |
| 700 | b: Node, | |
| 701 | mask_vector: Node, | |
| 702 | }, | |
| 703 | }; | |
| 681 | 704 | }; |
| 682 | 705 | |
| 683 | 706 | /// Converts the nodes into a Zig ast. |
| ... | ... | @@ -868,6 +891,16 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 868 | 891 | const import_node = try renderStdImport(c, "mem", "zeroInit"); |
| 869 | 892 | return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); |
| 870 | 893 | }, |
| 894 | .std_meta_shuffle_vector_index => { | |
| 895 | const payload = node.castTag(.std_meta_shuffle_vector_index).?.data; | |
| 896 | const import_node = try renderStdImport(c, "meta", "shuffleVectorIndex"); | |
| 897 | return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); | |
| 898 | }, | |
| 899 | .std_meta_vector => { | |
| 900 | const payload = node.castTag(.std_meta_vector).?.data; | |
| 901 | const import_node = try renderStdImport(c, "meta", "Vector"); | |
| 902 | return renderCall(c, import_node, &.{ payload.lhs, payload.rhs }); | |
| 903 | }, | |
| 871 | 904 | .call => { |
| 872 | 905 | const payload = node.castTag(.call).?.data; |
| 873 | 906 | const lhs = try renderNode(c, payload.lhs); |
| ... | ... | @@ -964,6 +997,17 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 964 | 997 | }, |
| 965 | 998 | }); |
| 966 | 999 | }, |
| 1000 | .@"comptime" => { | |
| 1001 | const payload = node.castTag(.@"comptime").?.data; | |
| 1002 | return c.addNode(.{ | |
| 1003 | .tag = .@"comptime", | |
| 1004 | .main_token = try c.addToken(.keyword_comptime, "comptime"), | |
| 1005 | .data = .{ | |
| 1006 | .lhs = try renderNode(c, payload), | |
| 1007 | .rhs = undefined, | |
| 1008 | }, | |
| 1009 | }); | |
| 1010 | }, | |
| 967 | 1011 | .type => { |
| 968 | 1012 | const payload = node.castTag(.type).?.data; |
| 969 | 1013 | return c.addNode(.{ |
| ... | ... | @@ -1217,6 +1261,15 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1217 | 1261 | const payload = node.castTag(.sizeof).?.data; |
| 1218 | 1262 | return renderBuiltinCall(c, "@sizeOf", &.{payload}); |
| 1219 | 1263 | }, |
| 1264 | .shuffle => { | |
| 1265 | const payload = node.castTag(.shuffle).?.data; | |
| 1266 | return renderBuiltinCall(c, "@shuffle", &.{ | |
| 1267 | payload.element_type, | |
| 1268 | payload.a, | |
| 1269 | payload.b, | |
| 1270 | payload.mask_vector, | |
| 1271 | }); | |
| 1272 | }, | |
| 1220 | 1273 | .alignof => { |
| 1221 | 1274 | const payload = node.castTag(.alignof).?.data; |
| 1222 | 1275 | return renderBuiltinCall(c, "@alignOf", &.{payload}); |
| ... | ... | @@ -1225,6 +1278,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1225 | 1278 | const payload = node.castTag(.typeof).?.data; |
| 1226 | 1279 | return renderBuiltinCall(c, "@TypeOf", &.{payload}); |
| 1227 | 1280 | }, |
| 1281 | .typeinfo => { | |
| 1282 | const payload = node.castTag(.typeinfo).?.data; | |
| 1283 | return renderBuiltinCall(c, "@typeInfo", &.{payload}); | |
| 1284 | }, | |
| 1228 | 1285 | .negate => return renderPrefixOp(c, node, .negation, .minus, "-"), |
| 1229 | 1286 | .negate_wrap => return renderPrefixOp(c, node, .negation_wrap, .minus_percent, "-%"), |
| 1230 | 1287 | .bit_not => return renderPrefixOp(c, node, .bit_not, .tilde, "~"), |
| ... | ... | @@ -2085,9 +2142,12 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 2085 | 2142 | .sizeof, |
| 2086 | 2143 | .alignof, |
| 2087 | 2144 | .typeof, |
| 2145 | .typeinfo, | |
| 2088 | 2146 | .std_meta_sizeof, |
| 2089 | 2147 | .std_meta_cast, |
| 2090 | 2148 | .std_meta_promoteIntLiteral, |
| 2149 | .std_meta_vector, | |
| 2150 | .std_meta_shuffle_vector_index, | |
| 2091 | 2151 | .std_mem_zeroinit, |
| 2092 | 2152 | .integer_literal, |
| 2093 | 2153 | .float_literal, |
| ... | ... | @@ -2118,6 +2178,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 2118 | 2178 | .bool_to_int, |
| 2119 | 2179 | .div_exact, |
| 2120 | 2180 | .byte_offset_of, |
| 2181 | .shuffle, | |
| 2121 | 2182 | => { |
| 2122 | 2183 | // no grouping needed |
| 2123 | 2184 | return renderNode(c, node); |
| ... | ... | @@ -2185,6 +2246,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 2185 | 2246 | .discard, |
| 2186 | 2247 | .@"continue", |
| 2187 | 2248 | .@"return", |
| 2249 | .@"comptime", | |
| 2188 | 2250 | .usingnamespace_builtins, |
| 2189 | 2251 | .while_true, |
| 2190 | 2252 | .if_not_break, |
| ... | ... | @@ -2327,6 +2389,8 @@ fn renderBuiltinCall(c: *Context, builtin: []const u8, args: []const Node) !Node |
| 2327 | 2389 | _ = try c.addToken(.l_paren, "("); |
| 2328 | 2390 | var arg_1: NodeIndex = 0; |
| 2329 | 2391 | var arg_2: NodeIndex = 0; |
| 2392 | var arg_3: NodeIndex = 0; | |
| 2393 | var arg_4: NodeIndex = 0; | |
| 2330 | 2394 | switch (args.len) { |
| 2331 | 2395 | 0 => {}, |
| 2332 | 2396 | 1 => { |
| ... | ... | @@ -2337,18 +2401,41 @@ fn renderBuiltinCall(c: *Context, builtin: []const u8, args: []const Node) !Node |
| 2337 | 2401 | _ = try c.addToken(.comma, ","); |
| 2338 | 2402 | arg_2 = try renderNode(c, args[1]); |
| 2339 | 2403 | }, |
| 2404 | 4 => { | |
| 2405 | arg_1 = try renderNode(c, args[0]); | |
| 2406 | _ = try c.addToken(.comma, ","); | |
| 2407 | arg_2 = try renderNode(c, args[1]); | |
| 2408 | _ = try c.addToken(.comma, ","); | |
| 2409 | arg_3 = try renderNode(c, args[2]); | |
| 2410 | _ = try c.addToken(.comma, ","); | |
| 2411 | arg_4 = try renderNode(c, args[3]); | |
| 2412 | }, | |
| 2340 | 2413 | else => unreachable, // expand this function as needed. |
| 2341 | 2414 | } |
| 2342 | 2415 | |
| 2343 | 2416 | _ = try c.addToken(.r_paren, ")"); |
| 2344 | return c.addNode(.{ | |
| 2345 | .tag = .builtin_call_two, | |
| 2346 | .main_token = builtin_tok, | |
| 2347 | .data = .{ | |
| 2348 | .lhs = arg_1, | |
| 2349 | .rhs = arg_2, | |
| 2350 | }, | |
| 2351 | }); | |
| 2417 | if (args.len <= 2) { | |
| 2418 | return c.addNode(.{ | |
| 2419 | .tag = .builtin_call_two, | |
| 2420 | .main_token = builtin_tok, | |
| 2421 | .data = .{ | |
| 2422 | .lhs = arg_1, | |
| 2423 | .rhs = arg_2, | |
| 2424 | }, | |
| 2425 | }); | |
| 2426 | } else { | |
| 2427 | std.debug.assert(args.len == 4); | |
| 2428 | ||
| 2429 | const params = try c.listToSpan(&.{ arg_1, arg_2, arg_3, arg_4 }); | |
| 2430 | return c.addNode(.{ | |
| 2431 | .tag = .builtin_call, | |
| 2432 | .main_token = builtin_tok, | |
| 2433 | .data = .{ | |
| 2434 | .lhs = params.start, | |
| 2435 | .rhs = params.end, | |
| 2436 | }, | |
| 2437 | }); | |
| 2438 | } | |
| 2352 | 2439 | } |
| 2353 | 2440 | |
| 2354 | 2441 | fn renderVar(c: *Context, node: Node) !NodeIndex { |
src/type.zig+621-1734| ... | ... | @@ -93,14 +93,56 @@ pub const Type = extern union { |
| 93 | 93 | |
| 94 | 94 | .anyerror_void_error_union, .error_union => return .ErrorUnion, |
| 95 | 95 | |
| 96 | .empty_struct => return .Struct, | |
| 97 | .empty_struct_literal => return .Struct, | |
| 98 | .@"struct" => return .Struct, | |
| 96 | .empty_struct, | |
| 97 | .empty_struct_literal, | |
| 98 | .@"struct", | |
| 99 | => return .Struct, | |
| 100 | ||
| 101 | .enum_full, | |
| 102 | .enum_nonexhaustive, | |
| 103 | .enum_simple, | |
| 104 | => return .Enum, | |
| 99 | 105 | |
| 100 | 106 | .var_args_param => unreachable, // can be any type |
| 101 | 107 | } |
| 102 | 108 | } |
| 103 | 109 | |
| 110 | pub fn isSelfComparable(ty: Type, is_equality_cmp: bool) bool { | |
| 111 | return switch (ty.zigTypeTag()) { | |
| 112 | .Int, | |
| 113 | .Float, | |
| 114 | .ComptimeFloat, | |
| 115 | .ComptimeInt, | |
| 116 | .Vector, // TODO some vectors require is_equality_cmp==true | |
| 117 | => true, | |
| 118 | ||
| 119 | .Bool, | |
| 120 | .Type, | |
| 121 | .Void, | |
| 122 | .ErrorSet, | |
| 123 | .Fn, | |
| 124 | .BoundFn, | |
| 125 | .Opaque, | |
| 126 | .AnyFrame, | |
| 127 | .Enum, | |
| 128 | .EnumLiteral, | |
| 129 | => is_equality_cmp, | |
| 130 | ||
| 131 | .NoReturn, | |
| 132 | .Array, | |
| 133 | .Struct, | |
| 134 | .Undefined, | |
| 135 | .Null, | |
| 136 | .ErrorUnion, | |
| 137 | .Union, | |
| 138 | .Frame, | |
| 139 | => false, | |
| 140 | ||
| 141 | .Pointer => is_equality_cmp or ty.isCPtr(), | |
| 142 | .Optional => is_equality_cmp and ty.isPtrLikeOptional(), | |
| 143 | }; | |
| 144 | } | |
| 145 | ||
| 104 | 146 | pub fn initTag(comptime small_tag: Tag) Type { |
| 105 | 147 | comptime assert(@enumToInt(small_tag) < Tag.no_payload_count); |
| 106 | 148 | return .{ .tag_if_small_enough = @enumToInt(small_tag) }; |
| ... | ... | @@ -614,6 +656,8 @@ pub const Type = extern union { |
| 614 | 656 | .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), |
| 615 | 657 | .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope), |
| 616 | 658 | .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct), |
| 659 | .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple), | |
| 660 | .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull), | |
| 617 | 661 | .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque), |
| 618 | 662 | } |
| 619 | 663 | } |
| ... | ... | @@ -626,13 +670,13 @@ pub const Type = extern union { |
| 626 | 670 | } |
| 627 | 671 | |
| 628 | 672 | pub fn format( |
| 629 | self: Type, | |
| 673 | start_type: Type, | |
| 630 | 674 | comptime fmt: []const u8, |
| 631 | 675 | options: std.fmt.FormatOptions, |
| 632 | out_stream: anytype, | |
| 633 | ) @TypeOf(out_stream).Error!void { | |
| 676 | writer: anytype, | |
| 677 | ) @TypeOf(writer).Error!void { | |
| 634 | 678 | comptime assert(fmt.len == 0); |
| 635 | var ty = self; | |
| 679 | var ty = start_type; | |
| 636 | 680 | while (true) { |
| 637 | 681 | const t = ty.tag(); |
| 638 | 682 | switch (t) { |
| ... | ... | @@ -670,132 +714,149 @@ pub const Type = extern union { |
| 670 | 714 | .comptime_float, |
| 671 | 715 | .noreturn, |
| 672 | 716 | .var_args_param, |
| 673 | => return out_stream.writeAll(@tagName(t)), | |
| 674 | ||
| 675 | .enum_literal => return out_stream.writeAll("@Type(.EnumLiteral)"), | |
| 676 | .@"null" => return out_stream.writeAll("@Type(.Null)"), | |
| 677 | .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"), | |
| 678 | ||
| 679 | .empty_struct, .empty_struct_literal => return out_stream.writeAll("struct {}"), | |
| 680 | .@"struct" => return out_stream.writeAll("(struct)"), | |
| 681 | .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"), | |
| 682 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), | |
| 683 | .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), | |
| 684 | .fn_void_no_args => return out_stream.writeAll("fn() void"), | |
| 685 | .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), | |
| 686 | .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"), | |
| 687 | .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"), | |
| 717 | => return writer.writeAll(@tagName(t)), | |
| 718 | ||
| 719 | .enum_literal => return writer.writeAll("@Type(.EnumLiteral)"), | |
| 720 | .@"null" => return writer.writeAll("@Type(.Null)"), | |
| 721 | .@"undefined" => return writer.writeAll("@Type(.Undefined)"), | |
| 722 | ||
| 723 | .empty_struct, .empty_struct_literal => return writer.writeAll("struct {}"), | |
| 724 | ||
| 725 | .@"struct" => { | |
| 726 | const struct_obj = ty.castTag(.@"struct").?.data; | |
| 727 | return struct_obj.owner_decl.renderFullyQualifiedName(writer); | |
| 728 | }, | |
| 729 | .enum_full, .enum_nonexhaustive => { | |
| 730 | const enum_full = ty.cast(Payload.EnumFull).?.data; | |
| 731 | return enum_full.owner_decl.renderFullyQualifiedName(writer); | |
| 732 | }, | |
| 733 | .enum_simple => { | |
| 734 | const enum_simple = ty.castTag(.enum_simple).?.data; | |
| 735 | return enum_simple.owner_decl.renderFullyQualifiedName(writer); | |
| 736 | }, | |
| 737 | .@"opaque" => { | |
| 738 | // TODO use declaration name | |
| 739 | return writer.writeAll("opaque {}"); | |
| 740 | }, | |
| 741 | ||
| 742 | .anyerror_void_error_union => return writer.writeAll("anyerror!void"), | |
| 743 | .const_slice_u8 => return writer.writeAll("[]const u8"), | |
| 744 | .fn_noreturn_no_args => return writer.writeAll("fn() noreturn"), | |
| 745 | .fn_void_no_args => return writer.writeAll("fn() void"), | |
| 746 | .fn_naked_noreturn_no_args => return writer.writeAll("fn() callconv(.Naked) noreturn"), | |
| 747 | .fn_ccc_void_no_args => return writer.writeAll("fn() callconv(.C) void"), | |
| 748 | .single_const_pointer_to_comptime_int => return writer.writeAll("*const comptime_int"), | |
| 688 | 749 | .function => { |
| 689 | 750 | const payload = ty.castTag(.function).?.data; |
| 690 | try out_stream.writeAll("fn("); | |
| 751 | try writer.writeAll("fn("); | |
| 691 | 752 | for (payload.param_types) |param_type, i| { |
| 692 | if (i != 0) try out_stream.writeAll(", "); | |
| 693 | try param_type.format("", .{}, out_stream); | |
| 753 | if (i != 0) try writer.writeAll(", "); | |
| 754 | try param_type.format("", .{}, writer); | |
| 694 | 755 | } |
| 695 | 756 | if (payload.is_var_args) { |
| 696 | 757 | if (payload.param_types.len != 0) { |
| 697 | try out_stream.writeAll(", "); | |
| 758 | try writer.writeAll(", "); | |
| 698 | 759 | } |
| 699 | try out_stream.writeAll("..."); | |
| 760 | try writer.writeAll("..."); | |
| 700 | 761 | } |
| 701 | try out_stream.writeAll(") callconv(."); | |
| 702 | try out_stream.writeAll(@tagName(payload.cc)); | |
| 703 | try out_stream.writeAll(")"); | |
| 762 | try writer.writeAll(") callconv(."); | |
| 763 | try writer.writeAll(@tagName(payload.cc)); | |
| 764 | try writer.writeAll(")"); | |
| 704 | 765 | ty = payload.return_type; |
| 705 | 766 | continue; |
| 706 | 767 | }, |
| 707 | 768 | |
| 708 | 769 | .array_u8 => { |
| 709 | 770 | const len = ty.castTag(.array_u8).?.data; |
| 710 | return out_stream.print("[{d}]u8", .{len}); | |
| 771 | return writer.print("[{d}]u8", .{len}); | |
| 711 | 772 | }, |
| 712 | 773 | .array_u8_sentinel_0 => { |
| 713 | 774 | const len = ty.castTag(.array_u8_sentinel_0).?.data; |
| 714 | return out_stream.print("[{d}:0]u8", .{len}); | |
| 775 | return writer.print("[{d}:0]u8", .{len}); | |
| 715 | 776 | }, |
| 716 | 777 | .array => { |
| 717 | 778 | const payload = ty.castTag(.array).?.data; |
| 718 | try out_stream.print("[{d}]", .{payload.len}); | |
| 779 | try writer.print("[{d}]", .{payload.len}); | |
| 719 | 780 | ty = payload.elem_type; |
| 720 | 781 | continue; |
| 721 | 782 | }, |
| 722 | 783 | .array_sentinel => { |
| 723 | 784 | const payload = ty.castTag(.array_sentinel).?.data; |
| 724 | try out_stream.print("[{d}:{}]", .{ payload.len, payload.sentinel }); | |
| 785 | try writer.print("[{d}:{}]", .{ payload.len, payload.sentinel }); | |
| 725 | 786 | ty = payload.elem_type; |
| 726 | 787 | continue; |
| 727 | 788 | }, |
| 728 | 789 | .single_const_pointer => { |
| 729 | 790 | const pointee_type = ty.castTag(.single_const_pointer).?.data; |
| 730 | try out_stream.writeAll("*const "); | |
| 791 | try writer.writeAll("*const "); | |
| 731 | 792 | ty = pointee_type; |
| 732 | 793 | continue; |
| 733 | 794 | }, |
| 734 | 795 | .single_mut_pointer => { |
| 735 | 796 | const pointee_type = ty.castTag(.single_mut_pointer).?.data; |
| 736 | try out_stream.writeAll("*"); | |
| 797 | try writer.writeAll("*"); | |
| 737 | 798 | ty = pointee_type; |
| 738 | 799 | continue; |
| 739 | 800 | }, |
| 740 | 801 | .many_const_pointer => { |
| 741 | 802 | const pointee_type = ty.castTag(.many_const_pointer).?.data; |
| 742 | try out_stream.writeAll("[*]const "); | |
| 803 | try writer.writeAll("[*]const "); | |
| 743 | 804 | ty = pointee_type; |
| 744 | 805 | continue; |
| 745 | 806 | }, |
| 746 | 807 | .many_mut_pointer => { |
| 747 | 808 | const pointee_type = ty.castTag(.many_mut_pointer).?.data; |
| 748 | try out_stream.writeAll("[*]"); | |
| 809 | try writer.writeAll("[*]"); | |
| 749 | 810 | ty = pointee_type; |
| 750 | 811 | continue; |
| 751 | 812 | }, |
| 752 | 813 | .c_const_pointer => { |
| 753 | 814 | const pointee_type = ty.castTag(.c_const_pointer).?.data; |
| 754 | try out_stream.writeAll("[*c]const "); | |
| 815 | try writer.writeAll("[*c]const "); | |
| 755 | 816 | ty = pointee_type; |
| 756 | 817 | continue; |
| 757 | 818 | }, |
| 758 | 819 | .c_mut_pointer => { |
| 759 | 820 | const pointee_type = ty.castTag(.c_mut_pointer).?.data; |
| 760 | try out_stream.writeAll("[*c]"); | |
| 821 | try writer.writeAll("[*c]"); | |
| 761 | 822 | ty = pointee_type; |
| 762 | 823 | continue; |
| 763 | 824 | }, |
| 764 | 825 | .const_slice => { |
| 765 | 826 | const pointee_type = ty.castTag(.const_slice).?.data; |
| 766 | try out_stream.writeAll("[]const "); | |
| 827 | try writer.writeAll("[]const "); | |
| 767 | 828 | ty = pointee_type; |
| 768 | 829 | continue; |
| 769 | 830 | }, |
| 770 | 831 | .mut_slice => { |
| 771 | 832 | const pointee_type = ty.castTag(.mut_slice).?.data; |
| 772 | try out_stream.writeAll("[]"); | |
| 833 | try writer.writeAll("[]"); | |
| 773 | 834 | ty = pointee_type; |
| 774 | 835 | continue; |
| 775 | 836 | }, |
| 776 | 837 | .int_signed => { |
| 777 | 838 | const bits = ty.castTag(.int_signed).?.data; |
| 778 | return out_stream.print("i{d}", .{bits}); | |
| 839 | return writer.print("i{d}", .{bits}); | |
| 779 | 840 | }, |
| 780 | 841 | .int_unsigned => { |
| 781 | 842 | const bits = ty.castTag(.int_unsigned).?.data; |
| 782 | return out_stream.print("u{d}", .{bits}); | |
| 843 | return writer.print("u{d}", .{bits}); | |
| 783 | 844 | }, |
| 784 | 845 | .optional => { |
| 785 | 846 | const child_type = ty.castTag(.optional).?.data; |
| 786 | try out_stream.writeByte('?'); | |
| 847 | try writer.writeByte('?'); | |
| 787 | 848 | ty = child_type; |
| 788 | 849 | continue; |
| 789 | 850 | }, |
| 790 | 851 | .optional_single_const_pointer => { |
| 791 | 852 | const pointee_type = ty.castTag(.optional_single_const_pointer).?.data; |
| 792 | try out_stream.writeAll("?*const "); | |
| 853 | try writer.writeAll("?*const "); | |
| 793 | 854 | ty = pointee_type; |
| 794 | 855 | continue; |
| 795 | 856 | }, |
| 796 | 857 | .optional_single_mut_pointer => { |
| 797 | 858 | const pointee_type = ty.castTag(.optional_single_mut_pointer).?.data; |
| 798 | try out_stream.writeAll("?*"); | |
| 859 | try writer.writeAll("?*"); | |
| 799 | 860 | ty = pointee_type; |
| 800 | 861 | continue; |
| 801 | 862 | }, |
| ... | ... | @@ -804,48 +865,46 @@ pub const Type = extern union { |
| 804 | 865 | const payload = ty.castTag(.pointer).?.data; |
| 805 | 866 | if (payload.sentinel) |some| switch (payload.size) { |
| 806 | 867 | .One, .C => unreachable, |
| 807 | .Many => try out_stream.print("[*:{}]", .{some}), | |
| 808 | .Slice => try out_stream.print("[:{}]", .{some}), | |
| 868 | .Many => try writer.print("[*:{}]", .{some}), | |
| 869 | .Slice => try writer.print("[:{}]", .{some}), | |
| 809 | 870 | } else switch (payload.size) { |
| 810 | .One => try out_stream.writeAll("*"), | |
| 811 | .Many => try out_stream.writeAll("[*]"), | |
| 812 | .C => try out_stream.writeAll("[*c]"), | |
| 813 | .Slice => try out_stream.writeAll("[]"), | |
| 871 | .One => try writer.writeAll("*"), | |
| 872 | .Many => try writer.writeAll("[*]"), | |
| 873 | .C => try writer.writeAll("[*c]"), | |
| 874 | .Slice => try writer.writeAll("[]"), | |
| 814 | 875 | } |
| 815 | 876 | if (payload.@"align" != 0) { |
| 816 | try out_stream.print("align({d}", .{payload.@"align"}); | |
| 877 | try writer.print("align({d}", .{payload.@"align"}); | |
| 817 | 878 | |
| 818 | 879 | if (payload.bit_offset != 0) { |
| 819 | try out_stream.print(":{d}:{d}", .{ payload.bit_offset, payload.host_size }); | |
| 880 | try writer.print(":{d}:{d}", .{ payload.bit_offset, payload.host_size }); | |
| 820 | 881 | } |
| 821 | try out_stream.writeAll(") "); | |
| 882 | try writer.writeAll(") "); | |
| 822 | 883 | } |
| 823 | if (!payload.mutable) try out_stream.writeAll("const "); | |
| 824 | if (payload.@"volatile") try out_stream.writeAll("volatile "); | |
| 825 | if (payload.@"allowzero") try out_stream.writeAll("allowzero "); | |
| 884 | if (!payload.mutable) try writer.writeAll("const "); | |
| 885 | if (payload.@"volatile") try writer.writeAll("volatile "); | |
| 886 | if (payload.@"allowzero") try writer.writeAll("allowzero "); | |
| 826 | 887 | |
| 827 | 888 | ty = payload.pointee_type; |
| 828 | 889 | continue; |
| 829 | 890 | }, |
| 830 | 891 | .error_union => { |
| 831 | 892 | const payload = ty.castTag(.error_union).?.data; |
| 832 | try payload.error_set.format("", .{}, out_stream); | |
| 833 | try out_stream.writeAll("!"); | |
| 893 | try payload.error_set.format("", .{}, writer); | |
| 894 | try writer.writeAll("!"); | |
| 834 | 895 | ty = payload.payload; |
| 835 | 896 | continue; |
| 836 | 897 | }, |
| 837 | 898 | .error_set => { |
| 838 | 899 | const error_set = ty.castTag(.error_set).?.data; |
| 839 | return out_stream.writeAll(std.mem.spanZ(error_set.owner_decl.name)); | |
| 900 | return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name)); | |
| 840 | 901 | }, |
| 841 | 902 | .error_set_single => { |
| 842 | 903 | const name = ty.castTag(.error_set_single).?.data; |
| 843 | return out_stream.print("error{{{s}}}", .{name}); | |
| 904 | return writer.print("error{{{s}}}", .{name}); | |
| 844 | 905 | }, |
| 845 | .inferred_alloc_const => return out_stream.writeAll("(inferred_alloc_const)"), | |
| 846 | .inferred_alloc_mut => return out_stream.writeAll("(inferred_alloc_mut)"), | |
| 847 | // TODO use declaration name | |
| 848 | .@"opaque" => return out_stream.writeAll("opaque {}"), | |
| 906 | .inferred_alloc_const => return writer.writeAll("(inferred_alloc_const)"), | |
| 907 | .inferred_alloc_mut => return writer.writeAll("(inferred_alloc_mut)"), | |
| 849 | 908 | } |
| 850 | 909 | unreachable; |
| 851 | 910 | } |
| ... | ... | @@ -954,6 +1013,19 @@ pub const Type = extern union { |
| 954 | 1013 | return false; |
| 955 | 1014 | } |
| 956 | 1015 | }, |
| 1016 | .enum_full => { | |
| 1017 | const enum_full = self.castTag(.enum_full).?.data; | |
| 1018 | return enum_full.fields.count() >= 2; | |
| 1019 | }, | |
| 1020 | .enum_simple => { | |
| 1021 | const enum_simple = self.castTag(.enum_simple).?.data; | |
| 1022 | return enum_simple.fields.count() >= 2; | |
| 1023 | }, | |
| 1024 | .enum_nonexhaustive => { | |
| 1025 | var buffer: Payload.Bits = undefined; | |
| 1026 | const int_tag_ty = self.intTagType(&buffer); | |
| 1027 | return int_tag_ty.hasCodeGenBits(); | |
| 1028 | }, | |
| 957 | 1029 | |
| 958 | 1030 | // TODO lazy types |
| 959 | 1031 | .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, |
| ... | ... | @@ -1112,13 +1184,37 @@ pub const Type = extern union { |
| 1112 | 1184 | } else if (!payload.payload.hasCodeGenBits()) { |
| 1113 | 1185 | return payload.error_set.abiAlignment(target); |
| 1114 | 1186 | } |
| 1115 | @panic("TODO abiAlignment error union"); | |
| 1187 | return std.math.max( | |
| 1188 | payload.payload.abiAlignment(target), | |
| 1189 | payload.error_set.abiAlignment(target), | |
| 1190 | ); | |
| 1116 | 1191 | }, |
| 1117 | 1192 | |
| 1118 | 1193 | .@"struct" => { |
| 1119 | @panic("TODO abiAlignment struct"); | |
| 1194 | // TODO take into account field alignment | |
| 1195 | // also make this possible to fail, and lazy | |
| 1196 | // I think we need to move all the functions from type.zig which can | |
| 1197 | // fail into Sema. | |
| 1198 | // Probably will need to introduce multi-stage struct resolution just | |
| 1199 | // like we have in stage1. | |
| 1200 | const struct_obj = self.castTag(.@"struct").?.data; | |
| 1201 | var biggest: u32 = 0; | |
| 1202 | for (struct_obj.fields.entries.items) |entry| { | |
| 1203 | const field_ty = entry.value.ty; | |
| 1204 | if (!field_ty.hasCodeGenBits()) continue; | |
| 1205 | const field_align = field_ty.abiAlignment(target); | |
| 1206 | if (field_align > biggest) { | |
| 1207 | return field_align; | |
| 1208 | } | |
| 1209 | } | |
| 1210 | assert(biggest != 0); | |
| 1211 | return biggest; | |
| 1212 | }, | |
| 1213 | .enum_full, .enum_nonexhaustive, .enum_simple => { | |
| 1214 | var buffer: Payload.Bits = undefined; | |
| 1215 | const int_tag_ty = self.intTagType(&buffer); | |
| 1216 | return int_tag_ty.abiAlignment(target); | |
| 1120 | 1217 | }, |
| 1121 | ||
| 1122 | 1218 | .c_void, |
| 1123 | 1219 | .void, |
| 1124 | 1220 | .type, |
| ... | ... | @@ -1166,6 +1262,11 @@ pub const Type = extern union { |
| 1166 | 1262 | .@"struct" => { |
| 1167 | 1263 | @panic("TODO abiSize struct"); |
| 1168 | 1264 | }, |
| 1265 | .enum_simple, .enum_full, .enum_nonexhaustive => { | |
| 1266 | var buffer: Payload.Bits = undefined; | |
| 1267 | const int_tag_ty = self.intTagType(&buffer); | |
| 1268 | return int_tag_ty.abiSize(target); | |
| 1269 | }, | |
| 1169 | 1270 | |
| 1170 | 1271 | .u8, |
| 1171 | 1272 | .i8, |
| ... | ... | @@ -1276,76 +1377,25 @@ pub const Type = extern union { |
| 1276 | 1377 | }; |
| 1277 | 1378 | } |
| 1278 | 1379 | |
| 1380 | /// Asserts the type is an enum. | |
| 1381 | pub fn intTagType(self: Type, buffer: *Payload.Bits) Type { | |
| 1382 | switch (self.tag()) { | |
| 1383 | .enum_full, .enum_nonexhaustive => return self.cast(Payload.EnumFull).?.data.tag_ty, | |
| 1384 | .enum_simple => { | |
| 1385 | const enum_simple = self.castTag(.enum_simple).?.data; | |
| 1386 | const bits = std.math.log2_int_ceil(usize, enum_simple.fields.count()); | |
| 1387 | buffer.* = .{ | |
| 1388 | .base = .{ .tag = .int_unsigned }, | |
| 1389 | .data = bits, | |
| 1390 | }; | |
| 1391 | return Type.initPayload(&buffer.base); | |
| 1392 | }, | |
| 1393 | else => unreachable, | |
| 1394 | } | |
| 1395 | } | |
| 1396 | ||
| 1279 | 1397 | pub fn isSinglePointer(self: Type) bool { |
| 1280 | 1398 | return switch (self.tag()) { |
| 1281 | .u8, | |
| 1282 | .i8, | |
| 1283 | .u16, | |
| 1284 | .i16, | |
| 1285 | .u32, | |
| 1286 | .i32, | |
| 1287 | .u64, | |
| 1288 | .i64, | |
| 1289 | .u128, | |
| 1290 | .i128, | |
| 1291 | .usize, | |
| 1292 | .isize, | |
| 1293 | .c_short, | |
| 1294 | .c_ushort, | |
| 1295 | .c_int, | |
| 1296 | .c_uint, | |
| 1297 | .c_long, | |
| 1298 | .c_ulong, | |
| 1299 | .c_longlong, | |
| 1300 | .c_ulonglong, | |
| 1301 | .c_longdouble, | |
| 1302 | .f16, | |
| 1303 | .f32, | |
| 1304 | .f64, | |
| 1305 | .f128, | |
| 1306 | .c_void, | |
| 1307 | .bool, | |
| 1308 | .void, | |
| 1309 | .type, | |
| 1310 | .anyerror, | |
| 1311 | .comptime_int, | |
| 1312 | .comptime_float, | |
| 1313 | .noreturn, | |
| 1314 | .@"null", | |
| 1315 | .@"undefined", | |
| 1316 | .array, | |
| 1317 | .array_sentinel, | |
| 1318 | .array_u8, | |
| 1319 | .array_u8_sentinel_0, | |
| 1320 | .const_slice_u8, | |
| 1321 | .fn_noreturn_no_args, | |
| 1322 | .fn_void_no_args, | |
| 1323 | .fn_naked_noreturn_no_args, | |
| 1324 | .fn_ccc_void_no_args, | |
| 1325 | .function, | |
| 1326 | .int_unsigned, | |
| 1327 | .int_signed, | |
| 1328 | .optional, | |
| 1329 | .optional_single_mut_pointer, | |
| 1330 | .optional_single_const_pointer, | |
| 1331 | .enum_literal, | |
| 1332 | .many_const_pointer, | |
| 1333 | .many_mut_pointer, | |
| 1334 | .c_const_pointer, | |
| 1335 | .c_mut_pointer, | |
| 1336 | .const_slice, | |
| 1337 | .mut_slice, | |
| 1338 | .error_union, | |
| 1339 | .anyerror_void_error_union, | |
| 1340 | .error_set, | |
| 1341 | .error_set_single, | |
| 1342 | .@"struct", | |
| 1343 | .empty_struct, | |
| 1344 | .empty_struct_literal, | |
| 1345 | .@"opaque", | |
| 1346 | .var_args_param, | |
| 1347 | => false, | |
| 1348 | ||
| 1349 | 1399 | .single_const_pointer, |
| 1350 | 1400 | .single_mut_pointer, |
| 1351 | 1401 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -1354,73 +1404,14 @@ pub const Type = extern union { |
| 1354 | 1404 | => true, |
| 1355 | 1405 | |
| 1356 | 1406 | .pointer => self.castTag(.pointer).?.data.size == .One, |
| 1407 | ||
| 1408 | else => false, | |
| 1357 | 1409 | }; |
| 1358 | 1410 | } |
| 1359 | 1411 | |
| 1360 | 1412 | /// Asserts the `Type` is a pointer. |
| 1361 | 1413 | pub fn ptrSize(self: Type) std.builtin.TypeInfo.Pointer.Size { |
| 1362 | 1414 | return switch (self.tag()) { |
| 1363 | .u8, | |
| 1364 | .i8, | |
| 1365 | .u16, | |
| 1366 | .i16, | |
| 1367 | .u32, | |
| 1368 | .i32, | |
| 1369 | .u64, | |
| 1370 | .i64, | |
| 1371 | .u128, | |
| 1372 | .i128, | |
| 1373 | .usize, | |
| 1374 | .isize, | |
| 1375 | .c_short, | |
| 1376 | .c_ushort, | |
| 1377 | .c_int, | |
| 1378 | .c_uint, | |
| 1379 | .c_long, | |
| 1380 | .c_ulong, | |
| 1381 | .c_longlong, | |
| 1382 | .c_ulonglong, | |
| 1383 | .c_longdouble, | |
| 1384 | .f16, | |
| 1385 | .f32, | |
| 1386 | .f64, | |
| 1387 | .f128, | |
| 1388 | .c_void, | |
| 1389 | .bool, | |
| 1390 | .void, | |
| 1391 | .type, | |
| 1392 | .anyerror, | |
| 1393 | .comptime_int, | |
| 1394 | .comptime_float, | |
| 1395 | .noreturn, | |
| 1396 | .@"null", | |
| 1397 | .@"undefined", | |
| 1398 | .array, | |
| 1399 | .array_sentinel, | |
| 1400 | .array_u8, | |
| 1401 | .array_u8_sentinel_0, | |
| 1402 | .fn_noreturn_no_args, | |
| 1403 | .fn_void_no_args, | |
| 1404 | .fn_naked_noreturn_no_args, | |
| 1405 | .fn_ccc_void_no_args, | |
| 1406 | .function, | |
| 1407 | .int_unsigned, | |
| 1408 | .int_signed, | |
| 1409 | .optional, | |
| 1410 | .optional_single_mut_pointer, | |
| 1411 | .optional_single_const_pointer, | |
| 1412 | .enum_literal, | |
| 1413 | .error_union, | |
| 1414 | .anyerror_void_error_union, | |
| 1415 | .error_set, | |
| 1416 | .error_set_single, | |
| 1417 | .empty_struct, | |
| 1418 | .empty_struct_literal, | |
| 1419 | .@"opaque", | |
| 1420 | .@"struct", | |
| 1421 | .var_args_param, | |
| 1422 | => unreachable, | |
| 1423 | ||
| 1424 | 1415 | .const_slice, |
| 1425 | 1416 | .mut_slice, |
| 1426 | 1417 | .const_slice_u8, |
| ... | ... | @@ -1442,159 +1433,26 @@ pub const Type = extern union { |
| 1442 | 1433 | => .One, |
| 1443 | 1434 | |
| 1444 | 1435 | .pointer => self.castTag(.pointer).?.data.size, |
| 1436 | ||
| 1437 | else => unreachable, | |
| 1445 | 1438 | }; |
| 1446 | 1439 | } |
| 1447 | 1440 | |
| 1448 | 1441 | pub fn isSlice(self: Type) bool { |
| 1449 | 1442 | return switch (self.tag()) { |
| 1450 | .u8, | |
| 1451 | .i8, | |
| 1452 | .u16, | |
| 1453 | .i16, | |
| 1454 | .u32, | |
| 1455 | .i32, | |
| 1456 | .u64, | |
| 1457 | .i64, | |
| 1458 | .u128, | |
| 1459 | .i128, | |
| 1460 | .usize, | |
| 1461 | .isize, | |
| 1462 | .c_short, | |
| 1463 | .c_ushort, | |
| 1464 | .c_int, | |
| 1465 | .c_uint, | |
| 1466 | .c_long, | |
| 1467 | .c_ulong, | |
| 1468 | .c_longlong, | |
| 1469 | .c_ulonglong, | |
| 1470 | .c_longdouble, | |
| 1471 | .f16, | |
| 1472 | .f32, | |
| 1473 | .f64, | |
| 1474 | .f128, | |
| 1475 | .c_void, | |
| 1476 | .bool, | |
| 1477 | .void, | |
| 1478 | .type, | |
| 1479 | .anyerror, | |
| 1480 | .comptime_int, | |
| 1481 | .comptime_float, | |
| 1482 | .noreturn, | |
| 1483 | .@"null", | |
| 1484 | .@"undefined", | |
| 1485 | .array, | |
| 1486 | .array_sentinel, | |
| 1487 | .array_u8, | |
| 1488 | .array_u8_sentinel_0, | |
| 1489 | .single_const_pointer, | |
| 1490 | .single_mut_pointer, | |
| 1491 | .many_const_pointer, | |
| 1492 | .many_mut_pointer, | |
| 1493 | .c_const_pointer, | |
| 1494 | .c_mut_pointer, | |
| 1495 | .single_const_pointer_to_comptime_int, | |
| 1496 | .fn_noreturn_no_args, | |
| 1497 | .fn_void_no_args, | |
| 1498 | .fn_naked_noreturn_no_args, | |
| 1499 | .fn_ccc_void_no_args, | |
| 1500 | .function, | |
| 1501 | .int_unsigned, | |
| 1502 | .int_signed, | |
| 1503 | .optional, | |
| 1504 | .optional_single_mut_pointer, | |
| 1505 | .optional_single_const_pointer, | |
| 1506 | .enum_literal, | |
| 1507 | .error_union, | |
| 1508 | .anyerror_void_error_union, | |
| 1509 | .error_set, | |
| 1510 | .error_set_single, | |
| 1511 | .empty_struct, | |
| 1512 | .empty_struct_literal, | |
| 1513 | .inferred_alloc_const, | |
| 1514 | .inferred_alloc_mut, | |
| 1515 | .@"struct", | |
| 1516 | .@"opaque", | |
| 1517 | .var_args_param, | |
| 1518 | => false, | |
| 1519 | ||
| 1520 | .const_slice, | |
| 1521 | .mut_slice, | |
| 1522 | .const_slice_u8, | |
| 1523 | => true, | |
| 1524 | ||
| 1525 | .pointer => self.castTag(.pointer).?.data.size == .Slice, | |
| 1526 | }; | |
| 1527 | } | |
| 1528 | ||
| 1529 | pub fn isConstPtr(self: Type) bool { | |
| 1530 | return switch (self.tag()) { | |
| 1531 | .u8, | |
| 1532 | .i8, | |
| 1533 | .u16, | |
| 1534 | .i16, | |
| 1535 | .u32, | |
| 1536 | .i32, | |
| 1537 | .u64, | |
| 1538 | .i64, | |
| 1539 | .u128, | |
| 1540 | .i128, | |
| 1541 | .usize, | |
| 1542 | .isize, | |
| 1543 | .c_short, | |
| 1544 | .c_ushort, | |
| 1545 | .c_int, | |
| 1546 | .c_uint, | |
| 1547 | .c_long, | |
| 1548 | .c_ulong, | |
| 1549 | .c_longlong, | |
| 1550 | .c_ulonglong, | |
| 1551 | .c_longdouble, | |
| 1552 | .f16, | |
| 1553 | .f32, | |
| 1554 | .f64, | |
| 1555 | .f128, | |
| 1556 | .c_void, | |
| 1557 | .bool, | |
| 1558 | .void, | |
| 1559 | .type, | |
| 1560 | .anyerror, | |
| 1561 | .comptime_int, | |
| 1562 | .comptime_float, | |
| 1563 | .noreturn, | |
| 1564 | .@"null", | |
| 1565 | .@"undefined", | |
| 1566 | .array, | |
| 1567 | .array_sentinel, | |
| 1568 | .array_u8, | |
| 1569 | .array_u8_sentinel_0, | |
| 1570 | .fn_noreturn_no_args, | |
| 1571 | .fn_void_no_args, | |
| 1572 | .fn_naked_noreturn_no_args, | |
| 1573 | .fn_ccc_void_no_args, | |
| 1574 | .function, | |
| 1575 | .int_unsigned, | |
| 1576 | .int_signed, | |
| 1577 | .single_mut_pointer, | |
| 1578 | .many_mut_pointer, | |
| 1579 | .c_mut_pointer, | |
| 1580 | .optional, | |
| 1581 | .optional_single_mut_pointer, | |
| 1582 | .optional_single_const_pointer, | |
| 1583 | .enum_literal, | |
| 1584 | .mut_slice, | |
| 1585 | .error_union, | |
| 1586 | .anyerror_void_error_union, | |
| 1587 | .error_set, | |
| 1588 | .error_set_single, | |
| 1589 | .empty_struct, | |
| 1590 | .empty_struct_literal, | |
| 1591 | .inferred_alloc_const, | |
| 1592 | .inferred_alloc_mut, | |
| 1593 | .@"struct", | |
| 1594 | .@"opaque", | |
| 1595 | .var_args_param, | |
| 1596 | => false, | |
| 1597 | ||
| 1443 | .const_slice, | |
| 1444 | .mut_slice, | |
| 1445 | .const_slice_u8, | |
| 1446 | => true, | |
| 1447 | ||
| 1448 | .pointer => self.castTag(.pointer).?.data.size == .Slice, | |
| 1449 | ||
| 1450 | else => false, | |
| 1451 | }; | |
| 1452 | } | |
| 1453 | ||
| 1454 | pub fn isConstPtr(self: Type) bool { | |
| 1455 | return switch (self.tag()) { | |
| 1598 | 1456 | .single_const_pointer, |
| 1599 | 1457 | .many_const_pointer, |
| 1600 | 1458 | .c_const_pointer, |
| ... | ... | @@ -1604,182 +1462,52 @@ pub const Type = extern union { |
| 1604 | 1462 | => true, |
| 1605 | 1463 | |
| 1606 | 1464 | .pointer => !self.castTag(.pointer).?.data.mutable, |
| 1465 | ||
| 1466 | else => false, | |
| 1607 | 1467 | }; |
| 1608 | 1468 | } |
| 1609 | 1469 | |
| 1610 | 1470 | pub fn isVolatilePtr(self: Type) bool { |
| 1611 | 1471 | return switch (self.tag()) { |
| 1612 | .u8, | |
| 1613 | .i8, | |
| 1614 | .u16, | |
| 1615 | .i16, | |
| 1616 | .u32, | |
| 1617 | .i32, | |
| 1618 | .u64, | |
| 1619 | .i64, | |
| 1620 | .u128, | |
| 1621 | .i128, | |
| 1622 | .usize, | |
| 1623 | .isize, | |
| 1624 | .c_short, | |
| 1625 | .c_ushort, | |
| 1626 | .c_int, | |
| 1627 | .c_uint, | |
| 1628 | .c_long, | |
| 1629 | .c_ulong, | |
| 1630 | .c_longlong, | |
| 1631 | .c_ulonglong, | |
| 1632 | .c_longdouble, | |
| 1633 | .f16, | |
| 1634 | .f32, | |
| 1635 | .f64, | |
| 1636 | .f128, | |
| 1637 | .c_void, | |
| 1638 | .bool, | |
| 1639 | .void, | |
| 1640 | .type, | |
| 1641 | .anyerror, | |
| 1642 | .comptime_int, | |
| 1643 | .comptime_float, | |
| 1644 | .noreturn, | |
| 1645 | .@"null", | |
| 1646 | .@"undefined", | |
| 1647 | .array, | |
| 1648 | .array_sentinel, | |
| 1649 | .array_u8, | |
| 1650 | .array_u8_sentinel_0, | |
| 1651 | .fn_noreturn_no_args, | |
| 1652 | .fn_void_no_args, | |
| 1653 | .fn_naked_noreturn_no_args, | |
| 1654 | .fn_ccc_void_no_args, | |
| 1655 | .function, | |
| 1656 | .int_unsigned, | |
| 1657 | .int_signed, | |
| 1658 | .single_mut_pointer, | |
| 1659 | .single_const_pointer, | |
| 1660 | .many_const_pointer, | |
| 1661 | .many_mut_pointer, | |
| 1662 | .c_const_pointer, | |
| 1663 | .c_mut_pointer, | |
| 1664 | .const_slice, | |
| 1665 | .mut_slice, | |
| 1666 | .single_const_pointer_to_comptime_int, | |
| 1667 | .const_slice_u8, | |
| 1668 | .optional, | |
| 1669 | .optional_single_mut_pointer, | |
| 1670 | .optional_single_const_pointer, | |
| 1671 | .enum_literal, | |
| 1672 | .error_union, | |
| 1673 | .anyerror_void_error_union, | |
| 1674 | .error_set, | |
| 1675 | .error_set_single, | |
| 1676 | .empty_struct, | |
| 1677 | .empty_struct_literal, | |
| 1678 | .inferred_alloc_const, | |
| 1679 | .inferred_alloc_mut, | |
| 1680 | .@"struct", | |
| 1681 | .@"opaque", | |
| 1682 | .var_args_param, | |
| 1683 | => false, | |
| 1684 | ||
| 1685 | 1472 | .pointer => { |
| 1686 | 1473 | const payload = self.castTag(.pointer).?.data; |
| 1687 | 1474 | return payload.@"volatile"; |
| 1688 | 1475 | }, |
| 1476 | else => false, | |
| 1689 | 1477 | }; |
| 1690 | 1478 | } |
| 1691 | 1479 | |
| 1692 | 1480 | pub fn isAllowzeroPtr(self: Type) bool { |
| 1693 | 1481 | return switch (self.tag()) { |
| 1694 | .u8, | |
| 1695 | .i8, | |
| 1696 | .u16, | |
| 1697 | .i16, | |
| 1698 | .u32, | |
| 1699 | .i32, | |
| 1700 | .u64, | |
| 1701 | .i64, | |
| 1702 | .u128, | |
| 1703 | .i128, | |
| 1704 | .usize, | |
| 1705 | .isize, | |
| 1706 | .c_short, | |
| 1707 | .c_ushort, | |
| 1708 | .c_int, | |
| 1709 | .c_uint, | |
| 1710 | .c_long, | |
| 1711 | .c_ulong, | |
| 1712 | .c_longlong, | |
| 1713 | .c_ulonglong, | |
| 1714 | .c_longdouble, | |
| 1715 | .f16, | |
| 1716 | .f32, | |
| 1717 | .f64, | |
| 1718 | .f128, | |
| 1719 | .c_void, | |
| 1720 | .bool, | |
| 1721 | .void, | |
| 1722 | .type, | |
| 1723 | .anyerror, | |
| 1724 | .comptime_int, | |
| 1725 | .comptime_float, | |
| 1726 | .noreturn, | |
| 1727 | .@"null", | |
| 1728 | .@"undefined", | |
| 1729 | .array, | |
| 1730 | .array_sentinel, | |
| 1731 | .array_u8, | |
| 1732 | .array_u8_sentinel_0, | |
| 1733 | .fn_noreturn_no_args, | |
| 1734 | .fn_void_no_args, | |
| 1735 | .fn_naked_noreturn_no_args, | |
| 1736 | .fn_ccc_void_no_args, | |
| 1737 | .function, | |
| 1738 | .int_unsigned, | |
| 1739 | .int_signed, | |
| 1740 | .single_mut_pointer, | |
| 1741 | .single_const_pointer, | |
| 1742 | .many_const_pointer, | |
| 1743 | .many_mut_pointer, | |
| 1744 | .c_const_pointer, | |
| 1745 | .c_mut_pointer, | |
| 1746 | .const_slice, | |
| 1747 | .mut_slice, | |
| 1748 | .single_const_pointer_to_comptime_int, | |
| 1749 | .const_slice_u8, | |
| 1750 | .optional, | |
| 1751 | .optional_single_mut_pointer, | |
| 1752 | .optional_single_const_pointer, | |
| 1753 | .enum_literal, | |
| 1754 | .error_union, | |
| 1755 | .anyerror_void_error_union, | |
| 1756 | .error_set, | |
| 1757 | .error_set_single, | |
| 1758 | .empty_struct, | |
| 1759 | .empty_struct_literal, | |
| 1760 | .inferred_alloc_const, | |
| 1761 | .inferred_alloc_mut, | |
| 1762 | .@"struct", | |
| 1763 | .@"opaque", | |
| 1764 | .var_args_param, | |
| 1765 | => false, | |
| 1766 | ||
| 1767 | 1482 | .pointer => { |
| 1768 | 1483 | const payload = self.castTag(.pointer).?.data; |
| 1769 | 1484 | return payload.@"allowzero"; |
| 1770 | 1485 | }, |
| 1486 | else => false, | |
| 1771 | 1487 | }; |
| 1772 | 1488 | } |
| 1773 | 1489 | |
| 1774 | /// Asserts that the type is an optional | |
| 1775 | pub fn isPtrLikeOptional(self: Type) bool { | |
| 1776 | switch (self.tag()) { | |
| 1777 | .optional_single_const_pointer, .optional_single_mut_pointer => return true, | |
| 1778 | .optional => { | |
| 1779 | var buf: Payload.ElemType = undefined; | |
| 1780 | const child_type = self.optionalChild(&buf); | |
| 1781 | // optionals of zero sized pointers behave like bools | |
| 1782 | if (!child_type.hasCodeGenBits()) return false; | |
| 1490 | pub fn isCPtr(self: Type) bool { | |
| 1491 | return switch (self.tag()) { | |
| 1492 | .c_const_pointer, | |
| 1493 | .c_mut_pointer, | |
| 1494 | => return true, | |
| 1495 | ||
| 1496 | .pointer => self.castTag(.pointer).?.data.size == .C, | |
| 1497 | ||
| 1498 | else => return false, | |
| 1499 | }; | |
| 1500 | } | |
| 1501 | ||
| 1502 | /// Asserts that the type is an optional | |
| 1503 | pub fn isPtrLikeOptional(self: Type) bool { | |
| 1504 | switch (self.tag()) { | |
| 1505 | .optional_single_const_pointer, .optional_single_mut_pointer => return true, | |
| 1506 | .optional => { | |
| 1507 | var buf: Payload.ElemType = undefined; | |
| 1508 | const child_type = self.optionalChild(&buf); | |
| 1509 | // optionals of zero sized pointers behave like bools | |
| 1510 | if (!child_type.hasCodeGenBits()) return false; | |
| 1783 | 1511 | |
| 1784 | 1512 | return child_type.zigTypeTag() == .Pointer and !child_type.isCPtr(); |
| 1785 | 1513 | }, |
| ... | ... | @@ -1833,64 +1561,6 @@ pub const Type = extern union { |
| 1833 | 1561 | /// Asserts the type is a pointer or array type. |
| 1834 | 1562 | pub fn elemType(self: Type) Type { |
| 1835 | 1563 | return switch (self.tag()) { |
| 1836 | .u8 => unreachable, | |
| 1837 | .i8 => unreachable, | |
| 1838 | .u16 => unreachable, | |
| 1839 | .i16 => unreachable, | |
| 1840 | .u32 => unreachable, | |
| 1841 | .i32 => unreachable, | |
| 1842 | .u64 => unreachable, | |
| 1843 | .i64 => unreachable, | |
| 1844 | .u128 => unreachable, | |
| 1845 | .i128 => unreachable, | |
| 1846 | .usize => unreachable, | |
| 1847 | .isize => unreachable, | |
| 1848 | .c_short => unreachable, | |
| 1849 | .c_ushort => unreachable, | |
| 1850 | .c_int => unreachable, | |
| 1851 | .c_uint => unreachable, | |
| 1852 | .c_long => unreachable, | |
| 1853 | .c_ulong => unreachable, | |
| 1854 | .c_longlong => unreachable, | |
| 1855 | .c_ulonglong => unreachable, | |
| 1856 | .c_longdouble => unreachable, | |
| 1857 | .f16 => unreachable, | |
| 1858 | .f32 => unreachable, | |
| 1859 | .f64 => unreachable, | |
| 1860 | .f128 => unreachable, | |
| 1861 | .c_void => unreachable, | |
| 1862 | .bool => unreachable, | |
| 1863 | .void => unreachable, | |
| 1864 | .type => unreachable, | |
| 1865 | .anyerror => unreachable, | |
| 1866 | .comptime_int => unreachable, | |
| 1867 | .comptime_float => unreachable, | |
| 1868 | .noreturn => unreachable, | |
| 1869 | .@"null" => unreachable, | |
| 1870 | .@"undefined" => unreachable, | |
| 1871 | .fn_noreturn_no_args => unreachable, | |
| 1872 | .fn_void_no_args => unreachable, | |
| 1873 | .fn_naked_noreturn_no_args => unreachable, | |
| 1874 | .fn_ccc_void_no_args => unreachable, | |
| 1875 | .function => unreachable, | |
| 1876 | .int_unsigned => unreachable, | |
| 1877 | .int_signed => unreachable, | |
| 1878 | .optional => unreachable, | |
| 1879 | .optional_single_const_pointer => unreachable, | |
| 1880 | .optional_single_mut_pointer => unreachable, | |
| 1881 | .enum_literal => unreachable, | |
| 1882 | .error_union => unreachable, | |
| 1883 | .anyerror_void_error_union => unreachable, | |
| 1884 | .error_set => unreachable, | |
| 1885 | .error_set_single => unreachable, | |
| 1886 | .@"struct" => unreachable, | |
| 1887 | .empty_struct => unreachable, | |
| 1888 | .empty_struct_literal => unreachable, | |
| 1889 | .inferred_alloc_const => unreachable, | |
| 1890 | .inferred_alloc_mut => unreachable, | |
| 1891 | .@"opaque" => unreachable, | |
| 1892 | .var_args_param => unreachable, | |
| 1893 | ||
| 1894 | 1564 | .array => self.castTag(.array).?.data.elem_type, |
| 1895 | 1565 | .array_sentinel => self.castTag(.array_sentinel).?.data.elem_type, |
| 1896 | 1566 | .single_const_pointer, |
| ... | ... | @@ -1902,9 +1572,12 @@ pub const Type = extern union { |
| 1902 | 1572 | .const_slice, |
| 1903 | 1573 | .mut_slice, |
| 1904 | 1574 | => self.castPointer().?.data, |
| 1575 | ||
| 1905 | 1576 | .array_u8, .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8), |
| 1906 | 1577 | .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), |
| 1907 | 1578 | .pointer => self.castTag(.pointer).?.data.pointee_type, |
| 1579 | ||
| 1580 | else => unreachable, | |
| 1908 | 1581 | }; |
| 1909 | 1582 | } |
| 1910 | 1583 | |
| ... | ... | @@ -1972,823 +1645,120 @@ pub const Type = extern union { |
| 1972 | 1645 | /// Asserts the type is an array or vector. |
| 1973 | 1646 | pub fn arrayLen(self: Type) u64 { |
| 1974 | 1647 | return switch (self.tag()) { |
| 1975 | .u8, | |
| 1976 | .i8, | |
| 1977 | .u16, | |
| 1978 | .i16, | |
| 1979 | .u32, | |
| 1980 | .i32, | |
| 1981 | .u64, | |
| 1982 | .i64, | |
| 1983 | .u128, | |
| 1984 | .i128, | |
| 1985 | .usize, | |
| 1986 | .isize, | |
| 1987 | .c_short, | |
| 1988 | .c_ushort, | |
| 1989 | .c_int, | |
| 1990 | .c_uint, | |
| 1991 | .c_long, | |
| 1992 | .c_ulong, | |
| 1993 | .c_longlong, | |
| 1994 | .c_ulonglong, | |
| 1995 | .c_longdouble, | |
| 1996 | .f16, | |
| 1997 | .f32, | |
| 1998 | .f64, | |
| 1999 | .f128, | |
| 2000 | .c_void, | |
| 2001 | .bool, | |
| 2002 | .void, | |
| 2003 | .type, | |
| 2004 | .anyerror, | |
| 2005 | .comptime_int, | |
| 2006 | .comptime_float, | |
| 2007 | .noreturn, | |
| 2008 | .@"null", | |
| 2009 | .@"undefined", | |
| 2010 | .fn_noreturn_no_args, | |
| 2011 | .fn_void_no_args, | |
| 2012 | .fn_naked_noreturn_no_args, | |
| 2013 | .fn_ccc_void_no_args, | |
| 2014 | .function, | |
| 2015 | .pointer, | |
| 2016 | .single_const_pointer, | |
| 2017 | .single_mut_pointer, | |
| 2018 | .many_const_pointer, | |
| 2019 | .many_mut_pointer, | |
| 2020 | .c_const_pointer, | |
| 2021 | .c_mut_pointer, | |
| 2022 | .const_slice, | |
| 2023 | .mut_slice, | |
| 2024 | .single_const_pointer_to_comptime_int, | |
| 2025 | .const_slice_u8, | |
| 2026 | .int_unsigned, | |
| 2027 | .int_signed, | |
| 2028 | .optional, | |
| 2029 | .optional_single_mut_pointer, | |
| 2030 | .optional_single_const_pointer, | |
| 2031 | .enum_literal, | |
| 2032 | .error_union, | |
| 2033 | .anyerror_void_error_union, | |
| 2034 | .error_set, | |
| 2035 | .error_set_single, | |
| 2036 | .@"struct", | |
| 2037 | .empty_struct, | |
| 2038 | .empty_struct_literal, | |
| 2039 | .inferred_alloc_const, | |
| 2040 | .inferred_alloc_mut, | |
| 2041 | .@"opaque", | |
| 2042 | .var_args_param, | |
| 2043 | => unreachable, | |
| 2044 | ||
| 2045 | 1648 | .array => self.castTag(.array).?.data.len, |
| 2046 | 1649 | .array_sentinel => self.castTag(.array_sentinel).?.data.len, |
| 2047 | 1650 | .array_u8 => self.castTag(.array_u8).?.data, |
| 2048 | 1651 | .array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data, |
| 2049 | }; | |
| 2050 | } | |
| 2051 | ||
| 2052 | /// Asserts the type is an array, pointer or vector. | |
| 2053 | pub fn sentinel(self: Type) ?Value { | |
| 2054 | return switch (self.tag()) { | |
| 2055 | .u8, | |
| 2056 | .i8, | |
| 2057 | .u16, | |
| 2058 | .i16, | |
| 2059 | .u32, | |
| 2060 | .i32, | |
| 2061 | .u64, | |
| 2062 | .i64, | |
| 2063 | .u128, | |
| 2064 | .i128, | |
| 2065 | .usize, | |
| 2066 | .isize, | |
| 2067 | .c_short, | |
| 2068 | .c_ushort, | |
| 2069 | .c_int, | |
| 2070 | .c_uint, | |
| 2071 | .c_long, | |
| 2072 | .c_ulong, | |
| 2073 | .c_longlong, | |
| 2074 | .c_ulonglong, | |
| 2075 | .c_longdouble, | |
| 2076 | .f16, | |
| 2077 | .f32, | |
| 2078 | .f64, | |
| 2079 | .f128, | |
| 2080 | .c_void, | |
| 2081 | .bool, | |
| 2082 | .void, | |
| 2083 | .type, | |
| 2084 | .anyerror, | |
| 2085 | .comptime_int, | |
| 2086 | .comptime_float, | |
| 2087 | .noreturn, | |
| 2088 | .@"null", | |
| 2089 | .@"undefined", | |
| 2090 | .fn_noreturn_no_args, | |
| 2091 | .fn_void_no_args, | |
| 2092 | .fn_naked_noreturn_no_args, | |
| 2093 | .fn_ccc_void_no_args, | |
| 2094 | .function, | |
| 2095 | .const_slice, | |
| 2096 | .mut_slice, | |
| 2097 | .const_slice_u8, | |
| 2098 | .int_unsigned, | |
| 2099 | .int_signed, | |
| 2100 | .optional, | |
| 2101 | .optional_single_mut_pointer, | |
| 2102 | .optional_single_const_pointer, | |
| 2103 | .enum_literal, | |
| 2104 | .error_union, | |
| 2105 | .anyerror_void_error_union, | |
| 2106 | .error_set, | |
| 2107 | .error_set_single, | |
| 2108 | .@"struct", | |
| 2109 | .empty_struct, | |
| 2110 | .empty_struct_literal, | |
| 2111 | .inferred_alloc_const, | |
| 2112 | .inferred_alloc_mut, | |
| 2113 | .@"opaque", | |
| 2114 | .var_args_param, | |
| 2115 | => unreachable, | |
| 2116 | 1652 | |
| 2117 | .single_const_pointer, | |
| 2118 | .single_mut_pointer, | |
| 2119 | .many_const_pointer, | |
| 2120 | .many_mut_pointer, | |
| 2121 | .c_const_pointer, | |
| 2122 | .c_mut_pointer, | |
| 2123 | .single_const_pointer_to_comptime_int, | |
| 2124 | .array, | |
| 2125 | .array_u8, | |
| 2126 | => return null, | |
| 2127 | ||
| 2128 | .pointer => return self.castTag(.pointer).?.data.sentinel, | |
| 2129 | .array_sentinel => return self.castTag(.array_sentinel).?.data.sentinel, | |
| 2130 | .array_u8_sentinel_0 => return Value.initTag(.zero), | |
| 1653 | else => unreachable, | |
| 2131 | 1654 | }; |
| 2132 | 1655 | } |
| 2133 | 1656 | |
| 2134 | /// Returns true if and only if the type is a fixed-width integer. | |
| 2135 | pub fn isInt(self: Type) bool { | |
| 2136 | return self.isSignedInt() or self.isUnsignedInt(); | |
| 2137 | } | |
| 2138 | ||
| 2139 | /// Returns true if and only if the type is a fixed-width, signed integer. | |
| 2140 | pub fn isSignedInt(self: Type) bool { | |
| 2141 | return switch (self.tag()) { | |
| 2142 | .f16, | |
| 2143 | .f32, | |
| 2144 | .f64, | |
| 2145 | .f128, | |
| 2146 | .c_longdouble, | |
| 2147 | .c_void, | |
| 2148 | .bool, | |
| 2149 | .void, | |
| 2150 | .type, | |
| 2151 | .anyerror, | |
| 2152 | .comptime_int, | |
| 2153 | .comptime_float, | |
| 2154 | .noreturn, | |
| 2155 | .@"null", | |
| 2156 | .@"undefined", | |
| 2157 | .fn_noreturn_no_args, | |
| 2158 | .fn_void_no_args, | |
| 2159 | .fn_naked_noreturn_no_args, | |
| 2160 | .fn_ccc_void_no_args, | |
| 2161 | .function, | |
| 2162 | .array, | |
| 2163 | .array_sentinel, | |
| 2164 | .array_u8, | |
| 2165 | .array_u8_sentinel_0, | |
| 2166 | .pointer, | |
| 2167 | .single_const_pointer, | |
| 2168 | .single_mut_pointer, | |
| 2169 | .many_const_pointer, | |
| 2170 | .many_mut_pointer, | |
| 2171 | .c_const_pointer, | |
| 2172 | .c_mut_pointer, | |
| 2173 | .const_slice, | |
| 2174 | .mut_slice, | |
| 2175 | .single_const_pointer_to_comptime_int, | |
| 2176 | .const_slice_u8, | |
| 2177 | .int_unsigned, | |
| 2178 | .u8, | |
| 2179 | .usize, | |
| 2180 | .c_ushort, | |
| 2181 | .c_uint, | |
| 2182 | .c_ulong, | |
| 2183 | .c_ulonglong, | |
| 2184 | .u16, | |
| 2185 | .u32, | |
| 2186 | .u64, | |
| 2187 | .optional, | |
| 2188 | .optional_single_mut_pointer, | |
| 2189 | .optional_single_const_pointer, | |
| 2190 | .enum_literal, | |
| 2191 | .error_union, | |
| 2192 | .anyerror_void_error_union, | |
| 2193 | .error_set, | |
| 2194 | .error_set_single, | |
| 2195 | .@"struct", | |
| 2196 | .empty_struct, | |
| 2197 | .empty_struct_literal, | |
| 2198 | .inferred_alloc_const, | |
| 2199 | .inferred_alloc_mut, | |
| 2200 | .@"opaque", | |
| 2201 | .var_args_param, | |
| 2202 | => false, | |
| 2203 | ||
| 2204 | .int_signed, | |
| 2205 | .i8, | |
| 2206 | .isize, | |
| 2207 | .c_short, | |
| 2208 | .c_int, | |
| 2209 | .c_long, | |
| 2210 | .c_longlong, | |
| 2211 | .i16, | |
| 2212 | .i32, | |
| 2213 | .i64, | |
| 2214 | .u128, | |
| 2215 | .i128, | |
| 2216 | => true, | |
| 2217 | }; | |
| 2218 | } | |
| 2219 | ||
| 2220 | /// Returns true if and only if the type is a fixed-width, unsigned integer. | |
| 2221 | pub fn isUnsignedInt(self: Type) bool { | |
| 2222 | return switch (self.tag()) { | |
| 2223 | .f16, | |
| 2224 | .f32, | |
| 2225 | .f64, | |
| 2226 | .f128, | |
| 2227 | .c_longdouble, | |
| 2228 | .c_void, | |
| 2229 | .bool, | |
| 2230 | .void, | |
| 2231 | .type, | |
| 2232 | .anyerror, | |
| 2233 | .comptime_int, | |
| 2234 | .comptime_float, | |
| 2235 | .noreturn, | |
| 2236 | .@"null", | |
| 2237 | .@"undefined", | |
| 2238 | .fn_noreturn_no_args, | |
| 2239 | .fn_void_no_args, | |
| 2240 | .fn_naked_noreturn_no_args, | |
| 2241 | .fn_ccc_void_no_args, | |
| 2242 | .function, | |
| 2243 | .array, | |
| 2244 | .array_sentinel, | |
| 2245 | .array_u8, | |
| 2246 | .array_u8_sentinel_0, | |
| 2247 | .pointer, | |
| 2248 | .single_const_pointer, | |
| 2249 | .single_mut_pointer, | |
| 2250 | .many_const_pointer, | |
| 2251 | .many_mut_pointer, | |
| 2252 | .c_const_pointer, | |
| 2253 | .c_mut_pointer, | |
| 2254 | .const_slice, | |
| 2255 | .mut_slice, | |
| 2256 | .single_const_pointer_to_comptime_int, | |
| 2257 | .const_slice_u8, | |
| 2258 | .int_signed, | |
| 2259 | .i8, | |
| 2260 | .isize, | |
| 2261 | .c_short, | |
| 2262 | .c_int, | |
| 2263 | .c_long, | |
| 2264 | .c_longlong, | |
| 2265 | .i16, | |
| 2266 | .i32, | |
| 2267 | .i64, | |
| 2268 | .u128, | |
| 2269 | .i128, | |
| 2270 | .optional, | |
| 2271 | .optional_single_mut_pointer, | |
| 2272 | .optional_single_const_pointer, | |
| 2273 | .enum_literal, | |
| 2274 | .error_union, | |
| 2275 | .anyerror_void_error_union, | |
| 2276 | .error_set, | |
| 2277 | .error_set_single, | |
| 2278 | .@"struct", | |
| 2279 | .empty_struct, | |
| 2280 | .empty_struct_literal, | |
| 2281 | .inferred_alloc_const, | |
| 2282 | .inferred_alloc_mut, | |
| 2283 | .@"opaque", | |
| 2284 | .var_args_param, | |
| 2285 | => false, | |
| 2286 | ||
| 2287 | .int_unsigned, | |
| 2288 | .u8, | |
| 2289 | .usize, | |
| 2290 | .c_ushort, | |
| 2291 | .c_uint, | |
| 2292 | .c_ulong, | |
| 2293 | .c_ulonglong, | |
| 2294 | .u16, | |
| 2295 | .u32, | |
| 2296 | .u64, | |
| 2297 | => true, | |
| 2298 | }; | |
| 2299 | } | |
| 2300 | ||
| 2301 | /// Asserts the type is an integer. | |
| 2302 | pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } { | |
| 2303 | return switch (self.tag()) { | |
| 2304 | .f16, | |
| 2305 | .f32, | |
| 2306 | .f64, | |
| 2307 | .f128, | |
| 2308 | .c_longdouble, | |
| 2309 | .c_void, | |
| 2310 | .bool, | |
| 2311 | .void, | |
| 2312 | .type, | |
| 2313 | .anyerror, | |
| 2314 | .comptime_int, | |
| 2315 | .comptime_float, | |
| 2316 | .noreturn, | |
| 2317 | .@"null", | |
| 2318 | .@"undefined", | |
| 2319 | .fn_noreturn_no_args, | |
| 2320 | .fn_void_no_args, | |
| 2321 | .fn_naked_noreturn_no_args, | |
| 2322 | .fn_ccc_void_no_args, | |
| 2323 | .function, | |
| 2324 | .array, | |
| 2325 | .array_sentinel, | |
| 2326 | .array_u8, | |
| 2327 | .array_u8_sentinel_0, | |
| 2328 | .pointer, | |
| 2329 | .single_const_pointer, | |
| 2330 | .single_mut_pointer, | |
| 2331 | .many_const_pointer, | |
| 2332 | .many_mut_pointer, | |
| 2333 | .c_const_pointer, | |
| 2334 | .c_mut_pointer, | |
| 2335 | .const_slice, | |
| 2336 | .mut_slice, | |
| 2337 | .single_const_pointer_to_comptime_int, | |
| 2338 | .const_slice_u8, | |
| 2339 | .optional, | |
| 2340 | .optional_single_mut_pointer, | |
| 2341 | .optional_single_const_pointer, | |
| 2342 | .enum_literal, | |
| 2343 | .error_union, | |
| 2344 | .anyerror_void_error_union, | |
| 2345 | .error_set, | |
| 2346 | .error_set_single, | |
| 2347 | .@"struct", | |
| 2348 | .empty_struct, | |
| 2349 | .empty_struct_literal, | |
| 2350 | .inferred_alloc_const, | |
| 2351 | .inferred_alloc_mut, | |
| 2352 | .@"opaque", | |
| 2353 | .var_args_param, | |
| 2354 | => unreachable, | |
| 2355 | ||
| 2356 | .int_unsigned => .{ | |
| 2357 | .signedness = .unsigned, | |
| 2358 | .bits = self.castTag(.int_unsigned).?.data, | |
| 2359 | }, | |
| 2360 | .int_signed => .{ | |
| 2361 | .signedness = .signed, | |
| 2362 | .bits = self.castTag(.int_signed).?.data, | |
| 2363 | }, | |
| 2364 | .u8 => .{ .signedness = .unsigned, .bits = 8 }, | |
| 2365 | .i8 => .{ .signedness = .signed, .bits = 8 }, | |
| 2366 | .u16 => .{ .signedness = .unsigned, .bits = 16 }, | |
| 2367 | .i16 => .{ .signedness = .signed, .bits = 16 }, | |
| 2368 | .u32 => .{ .signedness = .unsigned, .bits = 32 }, | |
| 2369 | .i32 => .{ .signedness = .signed, .bits = 32 }, | |
| 2370 | .u64 => .{ .signedness = .unsigned, .bits = 64 }, | |
| 2371 | .i64 => .{ .signedness = .signed, .bits = 64 }, | |
| 2372 | .u128 => .{ .signedness = .unsigned, .bits = 128 }, | |
| 2373 | .i128 => .{ .signedness = .signed, .bits = 128 }, | |
| 2374 | .usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, | |
| 2375 | .isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, | |
| 2376 | .c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, | |
| 2377 | .c_ushort => .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) }, | |
| 2378 | .c_int => .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) }, | |
| 2379 | .c_uint => .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) }, | |
| 2380 | .c_long => .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) }, | |
| 2381 | .c_ulong => .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) }, | |
| 2382 | .c_longlong => .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) }, | |
| 2383 | .c_ulonglong => .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) }, | |
| 2384 | }; | |
| 2385 | } | |
| 2386 | ||
| 2387 | pub fn isNamedInt(self: Type) bool { | |
| 2388 | return switch (self.tag()) { | |
| 2389 | .f16, | |
| 2390 | .f32, | |
| 2391 | .f64, | |
| 2392 | .f128, | |
| 2393 | .c_longdouble, | |
| 2394 | .c_void, | |
| 2395 | .bool, | |
| 2396 | .void, | |
| 2397 | .type, | |
| 2398 | .anyerror, | |
| 2399 | .comptime_int, | |
| 2400 | .comptime_float, | |
| 2401 | .noreturn, | |
| 2402 | .@"null", | |
| 2403 | .@"undefined", | |
| 2404 | .fn_noreturn_no_args, | |
| 2405 | .fn_void_no_args, | |
| 2406 | .fn_naked_noreturn_no_args, | |
| 2407 | .fn_ccc_void_no_args, | |
| 2408 | .function, | |
| 2409 | .array, | |
| 2410 | .array_sentinel, | |
| 2411 | .array_u8, | |
| 2412 | .array_u8_sentinel_0, | |
| 2413 | .pointer, | |
| 2414 | .single_const_pointer, | |
| 2415 | .single_mut_pointer, | |
| 2416 | .many_const_pointer, | |
| 2417 | .many_mut_pointer, | |
| 2418 | .c_const_pointer, | |
| 2419 | .c_mut_pointer, | |
| 2420 | .const_slice, | |
| 2421 | .mut_slice, | |
| 2422 | .single_const_pointer_to_comptime_int, | |
| 2423 | .const_slice_u8, | |
| 2424 | .int_unsigned, | |
| 2425 | .int_signed, | |
| 2426 | .u8, | |
| 2427 | .i8, | |
| 2428 | .u16, | |
| 2429 | .i16, | |
| 2430 | .u32, | |
| 2431 | .i32, | |
| 2432 | .u64, | |
| 2433 | .i64, | |
| 2434 | .u128, | |
| 2435 | .i128, | |
| 2436 | .optional, | |
| 2437 | .optional_single_mut_pointer, | |
| 2438 | .optional_single_const_pointer, | |
| 2439 | .enum_literal, | |
| 2440 | .error_union, | |
| 2441 | .anyerror_void_error_union, | |
| 2442 | .error_set, | |
| 2443 | .error_set_single, | |
| 2444 | .@"struct", | |
| 2445 | .empty_struct, | |
| 2446 | .empty_struct_literal, | |
| 2447 | .inferred_alloc_const, | |
| 2448 | .inferred_alloc_mut, | |
| 2449 | .@"opaque", | |
| 2450 | .var_args_param, | |
| 2451 | => false, | |
| 2452 | ||
| 2453 | .usize, | |
| 2454 | .isize, | |
| 2455 | .c_short, | |
| 2456 | .c_ushort, | |
| 2457 | .c_int, | |
| 2458 | .c_uint, | |
| 2459 | .c_long, | |
| 2460 | .c_ulong, | |
| 2461 | .c_longlong, | |
| 2462 | .c_ulonglong, | |
| 2463 | => true, | |
| 2464 | }; | |
| 2465 | } | |
| 2466 | ||
| 2467 | pub fn isFloat(self: Type) bool { | |
| 2468 | return switch (self.tag()) { | |
| 2469 | .f16, | |
| 2470 | .f32, | |
| 2471 | .f64, | |
| 2472 | .f128, | |
| 2473 | .c_longdouble, | |
| 2474 | => true, | |
| 2475 | ||
| 2476 | else => false, | |
| 2477 | }; | |
| 2478 | } | |
| 2479 | ||
| 2480 | /// Asserts the type is a fixed-size float. | |
| 2481 | pub fn floatBits(self: Type, target: Target) u16 { | |
| 2482 | return switch (self.tag()) { | |
| 2483 | .f16 => 16, | |
| 2484 | .f32 => 32, | |
| 2485 | .f64 => 64, | |
| 2486 | .f128 => 128, | |
| 2487 | .c_longdouble => CType.longdouble.sizeInBits(target), | |
| 2488 | ||
| 2489 | else => unreachable, | |
| 2490 | }; | |
| 2491 | } | |
| 2492 | ||
| 2493 | /// Asserts the type is a function. | |
| 2494 | pub fn fnParamLen(self: Type) usize { | |
| 2495 | return switch (self.tag()) { | |
| 2496 | .fn_noreturn_no_args => 0, | |
| 2497 | .fn_void_no_args => 0, | |
| 2498 | .fn_naked_noreturn_no_args => 0, | |
| 2499 | .fn_ccc_void_no_args => 0, | |
| 2500 | .function => self.castTag(.function).?.data.param_types.len, | |
| 2501 | ||
| 2502 | .f16, | |
| 2503 | .f32, | |
| 2504 | .f64, | |
| 2505 | .f128, | |
| 2506 | .c_longdouble, | |
| 2507 | .c_void, | |
| 2508 | .bool, | |
| 2509 | .void, | |
| 2510 | .type, | |
| 2511 | .anyerror, | |
| 2512 | .comptime_int, | |
| 2513 | .comptime_float, | |
| 2514 | .noreturn, | |
| 2515 | .@"null", | |
| 2516 | .@"undefined", | |
| 2517 | .array, | |
| 2518 | .array_sentinel, | |
| 2519 | .array_u8, | |
| 2520 | .array_u8_sentinel_0, | |
| 2521 | .pointer, | |
| 2522 | .single_const_pointer, | |
| 2523 | .single_mut_pointer, | |
| 2524 | .many_const_pointer, | |
| 2525 | .many_mut_pointer, | |
| 2526 | .c_const_pointer, | |
| 2527 | .c_mut_pointer, | |
| 2528 | .const_slice, | |
| 2529 | .mut_slice, | |
| 2530 | .single_const_pointer_to_comptime_int, | |
| 2531 | .const_slice_u8, | |
| 2532 | .u8, | |
| 2533 | .i8, | |
| 2534 | .u16, | |
| 2535 | .i16, | |
| 2536 | .u32, | |
| 2537 | .i32, | |
| 2538 | .u64, | |
| 2539 | .i64, | |
| 2540 | .u128, | |
| 2541 | .i128, | |
| 2542 | .usize, | |
| 2543 | .isize, | |
| 2544 | .c_short, | |
| 2545 | .c_ushort, | |
| 2546 | .c_int, | |
| 2547 | .c_uint, | |
| 2548 | .c_long, | |
| 2549 | .c_ulong, | |
| 2550 | .c_longlong, | |
| 2551 | .c_ulonglong, | |
| 2552 | .int_unsigned, | |
| 2553 | .int_signed, | |
| 2554 | .optional, | |
| 2555 | .optional_single_mut_pointer, | |
| 2556 | .optional_single_const_pointer, | |
| 2557 | .enum_literal, | |
| 2558 | .error_union, | |
| 2559 | .anyerror_void_error_union, | |
| 2560 | .error_set, | |
| 2561 | .error_set_single, | |
| 2562 | .@"struct", | |
| 2563 | .empty_struct, | |
| 2564 | .empty_struct_literal, | |
| 2565 | .inferred_alloc_const, | |
| 2566 | .inferred_alloc_mut, | |
| 2567 | .@"opaque", | |
| 2568 | .var_args_param, | |
| 2569 | => unreachable, | |
| 2570 | }; | |
| 2571 | } | |
| 2572 | ||
| 2573 | /// Asserts the type is a function. The length of the slice must be at least the length | |
| 2574 | /// given by `fnParamLen`. | |
| 2575 | pub fn fnParamTypes(self: Type, types: []Type) void { | |
| 2576 | switch (self.tag()) { | |
| 2577 | .fn_noreturn_no_args => return, | |
| 2578 | .fn_void_no_args => return, | |
| 2579 | .fn_naked_noreturn_no_args => return, | |
| 2580 | .fn_ccc_void_no_args => return, | |
| 2581 | .function => { | |
| 2582 | const payload = self.castTag(.function).?.data; | |
| 2583 | std.mem.copy(Type, types, payload.param_types); | |
| 2584 | }, | |
| 2585 | ||
| 2586 | .f16, | |
| 2587 | .f32, | |
| 2588 | .f64, | |
| 2589 | .f128, | |
| 2590 | .c_longdouble, | |
| 2591 | .c_void, | |
| 2592 | .bool, | |
| 2593 | .void, | |
| 2594 | .type, | |
| 2595 | .anyerror, | |
| 2596 | .comptime_int, | |
| 2597 | .comptime_float, | |
| 2598 | .noreturn, | |
| 2599 | .@"null", | |
| 2600 | .@"undefined", | |
| 2601 | .array, | |
| 2602 | .array_sentinel, | |
| 2603 | .array_u8, | |
| 2604 | .array_u8_sentinel_0, | |
| 2605 | .pointer, | |
| 2606 | .single_const_pointer, | |
| 2607 | .single_mut_pointer, | |
| 2608 | .many_const_pointer, | |
| 2609 | .many_mut_pointer, | |
| 2610 | .c_const_pointer, | |
| 2611 | .c_mut_pointer, | |
| 2612 | .const_slice, | |
| 2613 | .mut_slice, | |
| 2614 | .single_const_pointer_to_comptime_int, | |
| 2615 | .const_slice_u8, | |
| 2616 | .u8, | |
| 2617 | .i8, | |
| 2618 | .u16, | |
| 2619 | .i16, | |
| 2620 | .u32, | |
| 2621 | .i32, | |
| 2622 | .u64, | |
| 2623 | .i64, | |
| 2624 | .u128, | |
| 2625 | .i128, | |
| 2626 | .usize, | |
| 2627 | .isize, | |
| 2628 | .c_short, | |
| 2629 | .c_ushort, | |
| 2630 | .c_int, | |
| 2631 | .c_uint, | |
| 2632 | .c_long, | |
| 2633 | .c_ulong, | |
| 2634 | .c_longlong, | |
| 2635 | .c_ulonglong, | |
| 2636 | .int_unsigned, | |
| 2637 | .int_signed, | |
| 2638 | .optional, | |
| 2639 | .optional_single_mut_pointer, | |
| 2640 | .optional_single_const_pointer, | |
| 2641 | .enum_literal, | |
| 2642 | .error_union, | |
| 2643 | .anyerror_void_error_union, | |
| 2644 | .error_set, | |
| 2645 | .error_set_single, | |
| 2646 | .@"struct", | |
| 2647 | .empty_struct, | |
| 2648 | .empty_struct_literal, | |
| 2649 | .inferred_alloc_const, | |
| 2650 | .inferred_alloc_mut, | |
| 2651 | .@"opaque", | |
| 2652 | .var_args_param, | |
| 2653 | => unreachable, | |
| 2654 | } | |
| 2655 | } | |
| 2656 | ||
| 2657 | /// Asserts the type is a function. | |
| 2658 | pub fn fnParamType(self: Type, index: usize) Type { | |
| 2659 | switch (self.tag()) { | |
| 2660 | .function => { | |
| 2661 | const payload = self.castTag(.function).?.data; | |
| 2662 | return payload.param_types[index]; | |
| 2663 | }, | |
| 2664 | ||
| 2665 | .fn_noreturn_no_args, | |
| 2666 | .fn_void_no_args, | |
| 2667 | .fn_naked_noreturn_no_args, | |
| 2668 | .fn_ccc_void_no_args, | |
| 2669 | .f16, | |
| 2670 | .f32, | |
| 2671 | .f64, | |
| 2672 | .f128, | |
| 2673 | .c_longdouble, | |
| 2674 | .c_void, | |
| 2675 | .bool, | |
| 2676 | .void, | |
| 2677 | .type, | |
| 2678 | .anyerror, | |
| 2679 | .comptime_int, | |
| 2680 | .comptime_float, | |
| 2681 | .noreturn, | |
| 2682 | .@"null", | |
| 2683 | .@"undefined", | |
| 2684 | .array, | |
| 2685 | .array_sentinel, | |
| 2686 | .array_u8, | |
| 2687 | .array_u8_sentinel_0, | |
| 2688 | .pointer, | |
| 1657 | /// Asserts the type is an array, pointer or vector. | |
| 1658 | pub fn sentinel(self: Type) ?Value { | |
| 1659 | return switch (self.tag()) { | |
| 2689 | 1660 | .single_const_pointer, |
| 2690 | 1661 | .single_mut_pointer, |
| 2691 | 1662 | .many_const_pointer, |
| 2692 | 1663 | .many_mut_pointer, |
| 2693 | 1664 | .c_const_pointer, |
| 2694 | 1665 | .c_mut_pointer, |
| 2695 | .const_slice, | |
| 2696 | .mut_slice, | |
| 2697 | 1666 | .single_const_pointer_to_comptime_int, |
| 2698 | .const_slice_u8, | |
| 2699 | .u8, | |
| 1667 | .array, | |
| 1668 | .array_u8, | |
| 1669 | => return null, | |
| 1670 | ||
| 1671 | .pointer => return self.castTag(.pointer).?.data.sentinel, | |
| 1672 | .array_sentinel => return self.castTag(.array_sentinel).?.data.sentinel, | |
| 1673 | .array_u8_sentinel_0 => return Value.initTag(.zero), | |
| 1674 | ||
| 1675 | else => unreachable, | |
| 1676 | }; | |
| 1677 | } | |
| 1678 | ||
| 1679 | /// Returns true if and only if the type is a fixed-width integer. | |
| 1680 | pub fn isInt(self: Type) bool { | |
| 1681 | return self.isSignedInt() or self.isUnsignedInt(); | |
| 1682 | } | |
| 1683 | ||
| 1684 | /// Returns true if and only if the type is a fixed-width, signed integer. | |
| 1685 | pub fn isSignedInt(self: Type) bool { | |
| 1686 | return switch (self.tag()) { | |
| 1687 | .int_signed, | |
| 2700 | 1688 | .i8, |
| 2701 | .u16, | |
| 1689 | .isize, | |
| 1690 | .c_short, | |
| 1691 | .c_int, | |
| 1692 | .c_long, | |
| 1693 | .c_longlong, | |
| 2702 | 1694 | .i16, |
| 2703 | .u32, | |
| 2704 | 1695 | .i32, |
| 2705 | .u64, | |
| 2706 | 1696 | .i64, |
| 2707 | .u128, | |
| 2708 | 1697 | .i128, |
| 1698 | => true, | |
| 1699 | ||
| 1700 | else => false, | |
| 1701 | }; | |
| 1702 | } | |
| 1703 | ||
| 1704 | /// Returns true if and only if the type is a fixed-width, unsigned integer. | |
| 1705 | pub fn isUnsignedInt(self: Type) bool { | |
| 1706 | return switch (self.tag()) { | |
| 1707 | .int_unsigned, | |
| 1708 | .u8, | |
| 2709 | 1709 | .usize, |
| 2710 | .isize, | |
| 2711 | .c_short, | |
| 2712 | 1710 | .c_ushort, |
| 2713 | .c_int, | |
| 2714 | 1711 | .c_uint, |
| 2715 | .c_long, | |
| 2716 | 1712 | .c_ulong, |
| 2717 | .c_longlong, | |
| 2718 | 1713 | .c_ulonglong, |
| 2719 | .int_unsigned, | |
| 2720 | .int_signed, | |
| 2721 | .optional, | |
| 2722 | .optional_single_mut_pointer, | |
| 2723 | .optional_single_const_pointer, | |
| 2724 | .enum_literal, | |
| 2725 | .error_union, | |
| 2726 | .anyerror_void_error_union, | |
| 2727 | .error_set, | |
| 2728 | .error_set_single, | |
| 2729 | .@"struct", | |
| 2730 | .empty_struct, | |
| 2731 | .empty_struct_literal, | |
| 2732 | .inferred_alloc_const, | |
| 2733 | .inferred_alloc_mut, | |
| 2734 | .@"opaque", | |
| 2735 | .var_args_param, | |
| 2736 | => unreachable, | |
| 2737 | } | |
| 1714 | .u16, | |
| 1715 | .u32, | |
| 1716 | .u64, | |
| 1717 | .u128, | |
| 1718 | => true, | |
| 1719 | ||
| 1720 | else => false, | |
| 1721 | }; | |
| 2738 | 1722 | } |
| 2739 | 1723 | |
| 2740 | /// Asserts the type is a function. | |
| 2741 | pub fn fnReturnType(self: Type) Type { | |
| 1724 | /// Asserts the type is an integer. | |
| 1725 | pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } { | |
| 2742 | 1726 | return switch (self.tag()) { |
| 2743 | .fn_noreturn_no_args => Type.initTag(.noreturn), | |
| 2744 | .fn_naked_noreturn_no_args => Type.initTag(.noreturn), | |
| 2745 | ||
| 2746 | .fn_void_no_args, | |
| 2747 | .fn_ccc_void_no_args, | |
| 2748 | => Type.initTag(.void), | |
| 1727 | .int_unsigned => .{ | |
| 1728 | .signedness = .unsigned, | |
| 1729 | .bits = self.castTag(.int_unsigned).?.data, | |
| 1730 | }, | |
| 1731 | .int_signed => .{ | |
| 1732 | .signedness = .signed, | |
| 1733 | .bits = self.castTag(.int_signed).?.data, | |
| 1734 | }, | |
| 1735 | .u8 => .{ .signedness = .unsigned, .bits = 8 }, | |
| 1736 | .i8 => .{ .signedness = .signed, .bits = 8 }, | |
| 1737 | .u16 => .{ .signedness = .unsigned, .bits = 16 }, | |
| 1738 | .i16 => .{ .signedness = .signed, .bits = 16 }, | |
| 1739 | .u32 => .{ .signedness = .unsigned, .bits = 32 }, | |
| 1740 | .i32 => .{ .signedness = .signed, .bits = 32 }, | |
| 1741 | .u64 => .{ .signedness = .unsigned, .bits = 64 }, | |
| 1742 | .i64 => .{ .signedness = .signed, .bits = 64 }, | |
| 1743 | .u128 => .{ .signedness = .unsigned, .bits = 128 }, | |
| 1744 | .i128 => .{ .signedness = .signed, .bits = 128 }, | |
| 1745 | .usize => .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, | |
| 1746 | .isize => .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, | |
| 1747 | .c_short => .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, | |
| 1748 | .c_ushort => .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) }, | |
| 1749 | .c_int => .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) }, | |
| 1750 | .c_uint => .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) }, | |
| 1751 | .c_long => .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) }, | |
| 1752 | .c_ulong => .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) }, | |
| 1753 | .c_longlong => .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) }, | |
| 1754 | .c_ulonglong => .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) }, | |
| 2749 | 1755 | |
| 2750 | .function => self.castTag(.function).?.data.return_type, | |
| 1756 | else => unreachable, | |
| 1757 | }; | |
| 1758 | } | |
| 2751 | 1759 | |
| 2752 | .f16, | |
| 2753 | .f32, | |
| 2754 | .f64, | |
| 2755 | .f128, | |
| 2756 | .c_longdouble, | |
| 2757 | .c_void, | |
| 2758 | .bool, | |
| 2759 | .void, | |
| 2760 | .type, | |
| 2761 | .anyerror, | |
| 2762 | .comptime_int, | |
| 2763 | .comptime_float, | |
| 2764 | .noreturn, | |
| 2765 | .@"null", | |
| 2766 | .@"undefined", | |
| 2767 | .array, | |
| 2768 | .array_sentinel, | |
| 2769 | .array_u8, | |
| 2770 | .array_u8_sentinel_0, | |
| 2771 | .pointer, | |
| 2772 | .single_const_pointer, | |
| 2773 | .single_mut_pointer, | |
| 2774 | .many_const_pointer, | |
| 2775 | .many_mut_pointer, | |
| 2776 | .c_const_pointer, | |
| 2777 | .c_mut_pointer, | |
| 2778 | .const_slice, | |
| 2779 | .mut_slice, | |
| 2780 | .single_const_pointer_to_comptime_int, | |
| 2781 | .const_slice_u8, | |
| 2782 | .u8, | |
| 2783 | .i8, | |
| 2784 | .u16, | |
| 2785 | .i16, | |
| 2786 | .u32, | |
| 2787 | .i32, | |
| 2788 | .u64, | |
| 2789 | .i64, | |
| 2790 | .u128, | |
| 2791 | .i128, | |
| 1760 | pub fn isNamedInt(self: Type) bool { | |
| 1761 | return switch (self.tag()) { | |
| 2792 | 1762 | .usize, |
| 2793 | 1763 | .isize, |
| 2794 | 1764 | .c_short, |
| ... | ... | @@ -2799,24 +1769,93 @@ pub const Type = extern union { |
| 2799 | 1769 | .c_ulong, |
| 2800 | 1770 | .c_longlong, |
| 2801 | 1771 | .c_ulonglong, |
| 2802 | .int_unsigned, | |
| 2803 | .int_signed, | |
| 2804 | .optional, | |
| 2805 | .optional_single_mut_pointer, | |
| 2806 | .optional_single_const_pointer, | |
| 2807 | .enum_literal, | |
| 2808 | .error_union, | |
| 2809 | .anyerror_void_error_union, | |
| 2810 | .error_set, | |
| 2811 | .error_set_single, | |
| 2812 | .@"struct", | |
| 2813 | .empty_struct, | |
| 2814 | .empty_struct_literal, | |
| 2815 | .inferred_alloc_const, | |
| 2816 | .inferred_alloc_mut, | |
| 2817 | .@"opaque", | |
| 2818 | .var_args_param, | |
| 2819 | => unreachable, | |
| 1772 | => true, | |
| 1773 | ||
| 1774 | else => false, | |
| 1775 | }; | |
| 1776 | } | |
| 1777 | ||
| 1778 | pub fn isFloat(self: Type) bool { | |
| 1779 | return switch (self.tag()) { | |
| 1780 | .f16, | |
| 1781 | .f32, | |
| 1782 | .f64, | |
| 1783 | .f128, | |
| 1784 | .c_longdouble, | |
| 1785 | => true, | |
| 1786 | ||
| 1787 | else => false, | |
| 1788 | }; | |
| 1789 | } | |
| 1790 | ||
| 1791 | /// Asserts the type is a fixed-size float. | |
| 1792 | pub fn floatBits(self: Type, target: Target) u16 { | |
| 1793 | return switch (self.tag()) { | |
| 1794 | .f16 => 16, | |
| 1795 | .f32 => 32, | |
| 1796 | .f64 => 64, | |
| 1797 | .f128 => 128, | |
| 1798 | .c_longdouble => CType.longdouble.sizeInBits(target), | |
| 1799 | ||
| 1800 | else => unreachable, | |
| 1801 | }; | |
| 1802 | } | |
| 1803 | ||
| 1804 | /// Asserts the type is a function. | |
| 1805 | pub fn fnParamLen(self: Type) usize { | |
| 1806 | return switch (self.tag()) { | |
| 1807 | .fn_noreturn_no_args => 0, | |
| 1808 | .fn_void_no_args => 0, | |
| 1809 | .fn_naked_noreturn_no_args => 0, | |
| 1810 | .fn_ccc_void_no_args => 0, | |
| 1811 | .function => self.castTag(.function).?.data.param_types.len, | |
| 1812 | ||
| 1813 | else => unreachable, | |
| 1814 | }; | |
| 1815 | } | |
| 1816 | ||
| 1817 | /// Asserts the type is a function. The length of the slice must be at least the length | |
| 1818 | /// given by `fnParamLen`. | |
| 1819 | pub fn fnParamTypes(self: Type, types: []Type) void { | |
| 1820 | switch (self.tag()) { | |
| 1821 | .fn_noreturn_no_args => return, | |
| 1822 | .fn_void_no_args => return, | |
| 1823 | .fn_naked_noreturn_no_args => return, | |
| 1824 | .fn_ccc_void_no_args => return, | |
| 1825 | .function => { | |
| 1826 | const payload = self.castTag(.function).?.data; | |
| 1827 | std.mem.copy(Type, types, payload.param_types); | |
| 1828 | }, | |
| 1829 | ||
| 1830 | else => unreachable, | |
| 1831 | } | |
| 1832 | } | |
| 1833 | ||
| 1834 | /// Asserts the type is a function. | |
| 1835 | pub fn fnParamType(self: Type, index: usize) Type { | |
| 1836 | switch (self.tag()) { | |
| 1837 | .function => { | |
| 1838 | const payload = self.castTag(.function).?.data; | |
| 1839 | return payload.param_types[index]; | |
| 1840 | }, | |
| 1841 | ||
| 1842 | else => unreachable, | |
| 1843 | } | |
| 1844 | } | |
| 1845 | ||
| 1846 | /// Asserts the type is a function. | |
| 1847 | pub fn fnReturnType(self: Type) Type { | |
| 1848 | return switch (self.tag()) { | |
| 1849 | .fn_noreturn_no_args => Type.initTag(.noreturn), | |
| 1850 | .fn_naked_noreturn_no_args => Type.initTag(.noreturn), | |
| 1851 | ||
| 1852 | .fn_void_no_args, | |
| 1853 | .fn_ccc_void_no_args, | |
| 1854 | => Type.initTag(.void), | |
| 1855 | ||
| 1856 | .function => self.castTag(.function).?.data.return_type, | |
| 1857 | ||
| 1858 | else => unreachable, | |
| 2820 | 1859 | }; |
| 2821 | 1860 | } |
| 2822 | 1861 | |
| ... | ... | @@ -2829,74 +1868,7 @@ pub const Type = extern union { |
| 2829 | 1868 | .fn_ccc_void_no_args => .C, |
| 2830 | 1869 | .function => self.castTag(.function).?.data.cc, |
| 2831 | 1870 | |
| 2832 | .f16, | |
| 2833 | .f32, | |
| 2834 | .f64, | |
| 2835 | .f128, | |
| 2836 | .c_longdouble, | |
| 2837 | .c_void, | |
| 2838 | .bool, | |
| 2839 | .void, | |
| 2840 | .type, | |
| 2841 | .anyerror, | |
| 2842 | .comptime_int, | |
| 2843 | .comptime_float, | |
| 2844 | .noreturn, | |
| 2845 | .@"null", | |
| 2846 | .@"undefined", | |
| 2847 | .array, | |
| 2848 | .array_sentinel, | |
| 2849 | .array_u8, | |
| 2850 | .array_u8_sentinel_0, | |
| 2851 | .pointer, | |
| 2852 | .single_const_pointer, | |
| 2853 | .single_mut_pointer, | |
| 2854 | .many_const_pointer, | |
| 2855 | .many_mut_pointer, | |
| 2856 | .c_const_pointer, | |
| 2857 | .c_mut_pointer, | |
| 2858 | .const_slice, | |
| 2859 | .mut_slice, | |
| 2860 | .single_const_pointer_to_comptime_int, | |
| 2861 | .const_slice_u8, | |
| 2862 | .u8, | |
| 2863 | .i8, | |
| 2864 | .u16, | |
| 2865 | .i16, | |
| 2866 | .u32, | |
| 2867 | .i32, | |
| 2868 | .u64, | |
| 2869 | .i64, | |
| 2870 | .u128, | |
| 2871 | .i128, | |
| 2872 | .usize, | |
| 2873 | .isize, | |
| 2874 | .c_short, | |
| 2875 | .c_ushort, | |
| 2876 | .c_int, | |
| 2877 | .c_uint, | |
| 2878 | .c_long, | |
| 2879 | .c_ulong, | |
| 2880 | .c_longlong, | |
| 2881 | .c_ulonglong, | |
| 2882 | .int_unsigned, | |
| 2883 | .int_signed, | |
| 2884 | .optional, | |
| 2885 | .optional_single_mut_pointer, | |
| 2886 | .optional_single_const_pointer, | |
| 2887 | .enum_literal, | |
| 2888 | .error_union, | |
| 2889 | .anyerror_void_error_union, | |
| 2890 | .error_set, | |
| 2891 | .error_set_single, | |
| 2892 | .@"struct", | |
| 2893 | .empty_struct, | |
| 2894 | .empty_struct_literal, | |
| 2895 | .inferred_alloc_const, | |
| 2896 | .inferred_alloc_mut, | |
| 2897 | .@"opaque", | |
| 2898 | .var_args_param, | |
| 2899 | => unreachable, | |
| 1871 | else => unreachable, | |
| 2900 | 1872 | }; |
| 2901 | 1873 | } |
| 2902 | 1874 | |
| ... | ... | @@ -2909,74 +1881,7 @@ pub const Type = extern union { |
| 2909 | 1881 | .fn_ccc_void_no_args => false, |
| 2910 | 1882 | .function => self.castTag(.function).?.data.is_var_args, |
| 2911 | 1883 | |
| 2912 | .f16, | |
| 2913 | .f32, | |
| 2914 | .f64, | |
| 2915 | .f128, | |
| 2916 | .c_longdouble, | |
| 2917 | .c_void, | |
| 2918 | .bool, | |
| 2919 | .void, | |
| 2920 | .type, | |
| 2921 | .anyerror, | |
| 2922 | .comptime_int, | |
| 2923 | .comptime_float, | |
| 2924 | .noreturn, | |
| 2925 | .@"null", | |
| 2926 | .@"undefined", | |
| 2927 | .array, | |
| 2928 | .array_sentinel, | |
| 2929 | .array_u8, | |
| 2930 | .array_u8_sentinel_0, | |
| 2931 | .pointer, | |
| 2932 | .single_const_pointer, | |
| 2933 | .single_mut_pointer, | |
| 2934 | .many_const_pointer, | |
| 2935 | .many_mut_pointer, | |
| 2936 | .c_const_pointer, | |
| 2937 | .c_mut_pointer, | |
| 2938 | .const_slice, | |
| 2939 | .mut_slice, | |
| 2940 | .single_const_pointer_to_comptime_int, | |
| 2941 | .const_slice_u8, | |
| 2942 | .u8, | |
| 2943 | .i8, | |
| 2944 | .u16, | |
| 2945 | .i16, | |
| 2946 | .u32, | |
| 2947 | .i32, | |
| 2948 | .u64, | |
| 2949 | .i64, | |
| 2950 | .u128, | |
| 2951 | .i128, | |
| 2952 | .usize, | |
| 2953 | .isize, | |
| 2954 | .c_short, | |
| 2955 | .c_ushort, | |
| 2956 | .c_int, | |
| 2957 | .c_uint, | |
| 2958 | .c_long, | |
| 2959 | .c_ulong, | |
| 2960 | .c_longlong, | |
| 2961 | .c_ulonglong, | |
| 2962 | .int_unsigned, | |
| 2963 | .int_signed, | |
| 2964 | .optional, | |
| 2965 | .optional_single_mut_pointer, | |
| 2966 | .optional_single_const_pointer, | |
| 2967 | .enum_literal, | |
| 2968 | .error_union, | |
| 2969 | .anyerror_void_error_union, | |
| 2970 | .error_set, | |
| 2971 | .error_set_single, | |
| 2972 | .@"struct", | |
| 2973 | .empty_struct, | |
| 2974 | .empty_struct_literal, | |
| 2975 | .inferred_alloc_const, | |
| 2976 | .inferred_alloc_mut, | |
| 2977 | .@"opaque", | |
| 2978 | .var_args_param, | |
| 2979 | => unreachable, | |
| 1884 | else => unreachable, | |
| 2980 | 1885 | }; |
| 2981 | 1886 | } |
| 2982 | 1887 | |
| ... | ... | @@ -2984,84 +1889,41 @@ pub const Type = extern union { |
| 2984 | 1889 | return switch (self.tag()) { |
| 2985 | 1890 | .f16, |
| 2986 | 1891 | .f32, |
| 2987 | .f64, | |
| 2988 | .f128, | |
| 2989 | .c_longdouble, | |
| 2990 | .comptime_int, | |
| 2991 | .comptime_float, | |
| 2992 | .u8, | |
| 2993 | .i8, | |
| 2994 | .u16, | |
| 2995 | .i16, | |
| 2996 | .u32, | |
| 2997 | .i32, | |
| 2998 | .u64, | |
| 2999 | .i64, | |
| 3000 | .u128, | |
| 3001 | .i128, | |
| 3002 | .usize, | |
| 3003 | .isize, | |
| 3004 | .c_short, | |
| 3005 | .c_ushort, | |
| 3006 | .c_int, | |
| 3007 | .c_uint, | |
| 3008 | .c_long, | |
| 3009 | .c_ulong, | |
| 3010 | .c_longlong, | |
| 3011 | .c_ulonglong, | |
| 3012 | .int_unsigned, | |
| 3013 | .int_signed, | |
| 3014 | => true, | |
| 3015 | ||
| 3016 | .c_void, | |
| 3017 | .bool, | |
| 3018 | .void, | |
| 3019 | .type, | |
| 3020 | .anyerror, | |
| 3021 | .noreturn, | |
| 3022 | .@"null", | |
| 3023 | .@"undefined", | |
| 3024 | .fn_noreturn_no_args, | |
| 3025 | .fn_void_no_args, | |
| 3026 | .fn_naked_noreturn_no_args, | |
| 3027 | .fn_ccc_void_no_args, | |
| 3028 | .function, | |
| 3029 | .array, | |
| 3030 | .array_sentinel, | |
| 3031 | .array_u8, | |
| 3032 | .array_u8_sentinel_0, | |
| 3033 | .pointer, | |
| 3034 | .single_const_pointer, | |
| 3035 | .single_mut_pointer, | |
| 3036 | .many_const_pointer, | |
| 3037 | .many_mut_pointer, | |
| 3038 | .c_const_pointer, | |
| 3039 | .c_mut_pointer, | |
| 3040 | .const_slice, | |
| 3041 | .mut_slice, | |
| 3042 | .single_const_pointer_to_comptime_int, | |
| 3043 | .const_slice_u8, | |
| 3044 | .optional, | |
| 3045 | .optional_single_mut_pointer, | |
| 3046 | .optional_single_const_pointer, | |
| 3047 | .enum_literal, | |
| 3048 | .error_union, | |
| 3049 | .anyerror_void_error_union, | |
| 3050 | .error_set, | |
| 3051 | .error_set_single, | |
| 3052 | .@"struct", | |
| 3053 | .empty_struct, | |
| 3054 | .empty_struct_literal, | |
| 3055 | .inferred_alloc_const, | |
| 3056 | .inferred_alloc_mut, | |
| 3057 | .@"opaque", | |
| 3058 | .var_args_param, | |
| 3059 | => false, | |
| 1892 | .f64, | |
| 1893 | .f128, | |
| 1894 | .c_longdouble, | |
| 1895 | .comptime_int, | |
| 1896 | .comptime_float, | |
| 1897 | .u8, | |
| 1898 | .i8, | |
| 1899 | .u16, | |
| 1900 | .i16, | |
| 1901 | .u32, | |
| 1902 | .i32, | |
| 1903 | .u64, | |
| 1904 | .i64, | |
| 1905 | .u128, | |
| 1906 | .i128, | |
| 1907 | .usize, | |
| 1908 | .isize, | |
| 1909 | .c_short, | |
| 1910 | .c_ushort, | |
| 1911 | .c_int, | |
| 1912 | .c_uint, | |
| 1913 | .c_long, | |
| 1914 | .c_ulong, | |
| 1915 | .c_longlong, | |
| 1916 | .c_ulonglong, | |
| 1917 | .int_unsigned, | |
| 1918 | .int_signed, | |
| 1919 | => true, | |
| 1920 | ||
| 1921 | else => false, | |
| 3060 | 1922 | }; |
| 3061 | 1923 | } |
| 3062 | 1924 | |
| 3063 | pub fn onePossibleValue(self: Type) ?Value { | |
| 3064 | var ty = self; | |
| 1925 | pub fn onePossibleValue(starting_type: Type) ?Value { | |
| 1926 | var ty = starting_type; | |
| 3065 | 1927 | while (true) switch (ty.tag()) { |
| 3066 | 1928 | .f16, |
| 3067 | 1929 | .f32, |
| ... | ... | @@ -3118,9 +1980,32 @@ pub const Type = extern union { |
| 3118 | 1980 | => return null, |
| 3119 | 1981 | |
| 3120 | 1982 | .@"struct" => { |
| 3121 | log.warn("TODO implement Type.onePossibleValue for structs", .{}); | |
| 3122 | return null; | |
| 1983 | const s = ty.castTag(.@"struct").?.data; | |
| 1984 | for (s.fields.entries.items) |entry| { | |
| 1985 | const field_ty = entry.value.ty; | |
| 1986 | if (field_ty.onePossibleValue() == null) { | |
| 1987 | return null; | |
| 1988 | } | |
| 1989 | } | |
| 1990 | return Value.initTag(.empty_struct_value); | |
| 1991 | }, | |
| 1992 | .enum_full => { | |
| 1993 | const enum_full = ty.castTag(.enum_full).?.data; | |
| 1994 | if (enum_full.fields.count() == 1) { | |
| 1995 | return enum_full.values.entries.items[0].key; | |
| 1996 | } else { | |
| 1997 | return null; | |
| 1998 | } | |
| 1999 | }, | |
| 2000 | .enum_simple => { | |
| 2001 | const enum_simple = ty.castTag(.enum_simple).?.data; | |
| 2002 | if (enum_simple.fields.count() == 1) { | |
| 2003 | return Value.initTag(.zero); | |
| 2004 | } else { | |
| 2005 | return null; | |
| 2006 | } | |
| 3123 | 2007 | }, |
| 2008 | .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty, | |
| 3124 | 2009 | |
| 3125 | 2010 | .empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value), |
| 3126 | 2011 | .void => return Value.initTag(.void_value), |
| ... | ... | @@ -3160,87 +2045,6 @@ pub const Type = extern union { |
| 3160 | 2045 | }; |
| 3161 | 2046 | } |
| 3162 | 2047 | |
| 3163 | pub fn isCPtr(self: Type) bool { | |
| 3164 | return switch (self.tag()) { | |
| 3165 | .f16, | |
| 3166 | .f32, | |
| 3167 | .f64, | |
| 3168 | .f128, | |
| 3169 | .c_longdouble, | |
| 3170 | .comptime_int, | |
| 3171 | .comptime_float, | |
| 3172 | .u8, | |
| 3173 | .i8, | |
| 3174 | .u16, | |
| 3175 | .i16, | |
| 3176 | .u32, | |
| 3177 | .i32, | |
| 3178 | .u64, | |
| 3179 | .i64, | |
| 3180 | .u128, | |
| 3181 | .i128, | |
| 3182 | .usize, | |
| 3183 | .isize, | |
| 3184 | .c_short, | |
| 3185 | .c_ushort, | |
| 3186 | .c_int, | |
| 3187 | .c_uint, | |
| 3188 | .c_long, | |
| 3189 | .c_ulong, | |
| 3190 | .c_longlong, | |
| 3191 | .c_ulonglong, | |
| 3192 | .bool, | |
| 3193 | .type, | |
| 3194 | .anyerror, | |
| 3195 | .fn_noreturn_no_args, | |
| 3196 | .fn_void_no_args, | |
| 3197 | .fn_naked_noreturn_no_args, | |
| 3198 | .fn_ccc_void_no_args, | |
| 3199 | .function, | |
| 3200 | .single_const_pointer_to_comptime_int, | |
| 3201 | .const_slice_u8, | |
| 3202 | .c_void, | |
| 3203 | .void, | |
| 3204 | .noreturn, | |
| 3205 | .@"null", | |
| 3206 | .@"undefined", | |
| 3207 | .int_unsigned, | |
| 3208 | .int_signed, | |
| 3209 | .array, | |
| 3210 | .array_sentinel, | |
| 3211 | .array_u8, | |
| 3212 | .array_u8_sentinel_0, | |
| 3213 | .single_const_pointer, | |
| 3214 | .single_mut_pointer, | |
| 3215 | .many_const_pointer, | |
| 3216 | .many_mut_pointer, | |
| 3217 | .const_slice, | |
| 3218 | .mut_slice, | |
| 3219 | .optional, | |
| 3220 | .optional_single_mut_pointer, | |
| 3221 | .optional_single_const_pointer, | |
| 3222 | .enum_literal, | |
| 3223 | .error_union, | |
| 3224 | .anyerror_void_error_union, | |
| 3225 | .error_set, | |
| 3226 | .error_set_single, | |
| 3227 | .@"struct", | |
| 3228 | .empty_struct, | |
| 3229 | .empty_struct_literal, | |
| 3230 | .inferred_alloc_const, | |
| 3231 | .inferred_alloc_mut, | |
| 3232 | .@"opaque", | |
| 3233 | .var_args_param, | |
| 3234 | => return false, | |
| 3235 | ||
| 3236 | .c_const_pointer, | |
| 3237 | .c_mut_pointer, | |
| 3238 | => return true, | |
| 3239 | ||
| 3240 | .pointer => self.castTag(.pointer).?.data.size == .C, | |
| 3241 | }; | |
| 3242 | } | |
| 3243 | ||
| 3244 | 2048 | pub fn isIndexable(self: Type) bool { |
| 3245 | 2049 | const zig_tag = self.zigTypeTag(); |
| 3246 | 2050 | // TODO tuples are indexable |
| ... | ... | @@ -3248,83 +2052,15 @@ pub const Type = extern union { |
| 3248 | 2052 | (self.isSinglePointer() and self.elemType().zigTypeTag() == .Array); |
| 3249 | 2053 | } |
| 3250 | 2054 | |
| 3251 | /// Asserts that the type is a container. (note: ErrorSet is not a container). | |
| 3252 | pub fn getContainerScope(self: Type) *Module.Scope.Container { | |
| 2055 | /// Returns null if the type has no container. | |
| 2056 | pub fn getContainerScope(self: Type) ?*Module.Scope.Container { | |
| 3253 | 2057 | return switch (self.tag()) { |
| 3254 | .f16, | |
| 3255 | .f32, | |
| 3256 | .f64, | |
| 3257 | .f128, | |
| 3258 | .c_longdouble, | |
| 3259 | .comptime_int, | |
| 3260 | .comptime_float, | |
| 3261 | .u8, | |
| 3262 | .i8, | |
| 3263 | .u16, | |
| 3264 | .i16, | |
| 3265 | .u32, | |
| 3266 | .i32, | |
| 3267 | .u64, | |
| 3268 | .i64, | |
| 3269 | .u128, | |
| 3270 | .i128, | |
| 3271 | .usize, | |
| 3272 | .isize, | |
| 3273 | .c_short, | |
| 3274 | .c_ushort, | |
| 3275 | .c_int, | |
| 3276 | .c_uint, | |
| 3277 | .c_long, | |
| 3278 | .c_ulong, | |
| 3279 | .c_longlong, | |
| 3280 | .c_ulonglong, | |
| 3281 | .bool, | |
| 3282 | .type, | |
| 3283 | .anyerror, | |
| 3284 | .fn_noreturn_no_args, | |
| 3285 | .fn_void_no_args, | |
| 3286 | .fn_naked_noreturn_no_args, | |
| 3287 | .fn_ccc_void_no_args, | |
| 3288 | .function, | |
| 3289 | .single_const_pointer_to_comptime_int, | |
| 3290 | .const_slice_u8, | |
| 3291 | .c_void, | |
| 3292 | .void, | |
| 3293 | .noreturn, | |
| 3294 | .@"null", | |
| 3295 | .@"undefined", | |
| 3296 | .int_unsigned, | |
| 3297 | .int_signed, | |
| 3298 | .array, | |
| 3299 | .array_sentinel, | |
| 3300 | .array_u8, | |
| 3301 | .array_u8_sentinel_0, | |
| 3302 | .single_const_pointer, | |
| 3303 | .single_mut_pointer, | |
| 3304 | .many_const_pointer, | |
| 3305 | .many_mut_pointer, | |
| 3306 | .const_slice, | |
| 3307 | .mut_slice, | |
| 3308 | .optional, | |
| 3309 | .optional_single_mut_pointer, | |
| 3310 | .optional_single_const_pointer, | |
| 3311 | .enum_literal, | |
| 3312 | .error_union, | |
| 3313 | .anyerror_void_error_union, | |
| 3314 | .error_set, | |
| 3315 | .error_set_single, | |
| 3316 | .c_const_pointer, | |
| 3317 | .c_mut_pointer, | |
| 3318 | .pointer, | |
| 3319 | .inferred_alloc_const, | |
| 3320 | .inferred_alloc_mut, | |
| 3321 | .var_args_param, | |
| 3322 | .empty_struct_literal, | |
| 3323 | => unreachable, | |
| 3324 | ||
| 3325 | 2058 | .@"struct" => &self.castTag(.@"struct").?.data.container, |
| 2059 | .enum_full => &self.castTag(.enum_full).?.data.container, | |
| 3326 | 2060 | .empty_struct => self.castTag(.empty_struct).?.data, |
| 3327 | 2061 | .@"opaque" => &self.castTag(.@"opaque").?.data, |
| 2062 | ||
| 2063 | else => null, | |
| 3328 | 2064 | }; |
| 3329 | 2065 | } |
| 3330 | 2066 | |
| ... | ... | @@ -3383,8 +2119,144 @@ pub const Type = extern union { |
| 3383 | 2119 | } |
| 3384 | 2120 | } |
| 3385 | 2121 | |
| 3386 | pub fn isExhaustiveEnum(ty: Type) bool { | |
| 3387 | return false; // TODO | |
| 2122 | pub fn isNonexhaustiveEnum(ty: Type) bool { | |
| 2123 | return switch (ty.tag()) { | |
| 2124 | .enum_nonexhaustive => true, | |
| 2125 | else => false, | |
| 2126 | }; | |
| 2127 | } | |
| 2128 | ||
| 2129 | pub fn enumFieldCount(ty: Type) usize { | |
| 2130 | switch (ty.tag()) { | |
| 2131 | .enum_full, .enum_nonexhaustive => { | |
| 2132 | const enum_full = ty.cast(Payload.EnumFull).?.data; | |
| 2133 | return enum_full.fields.count(); | |
| 2134 | }, | |
| 2135 | .enum_simple => { | |
| 2136 | const enum_simple = ty.castTag(.enum_simple).?.data; | |
| 2137 | return enum_simple.fields.count(); | |
| 2138 | }, | |
| 2139 | else => unreachable, | |
| 2140 | } | |
| 2141 | } | |
| 2142 | ||
| 2143 | pub fn enumFieldName(ty: Type, field_index: usize) []const u8 { | |
| 2144 | switch (ty.tag()) { | |
| 2145 | .enum_full, .enum_nonexhaustive => { | |
| 2146 | const enum_full = ty.cast(Payload.EnumFull).?.data; | |
| 2147 | return enum_full.fields.entries.items[field_index].key; | |
| 2148 | }, | |
| 2149 | .enum_simple => { | |
| 2150 | const enum_simple = ty.castTag(.enum_simple).?.data; | |
| 2151 | return enum_simple.fields.entries.items[field_index].key; | |
| 2152 | }, | |
| 2153 | else => unreachable, | |
| 2154 | } | |
| 2155 | } | |
| 2156 | ||
| 2157 | pub fn enumFieldIndex(ty: Type, field_name: []const u8) ?usize { | |
| 2158 | switch (ty.tag()) { | |
| 2159 | .enum_full, .enum_nonexhaustive => { | |
| 2160 | const enum_full = ty.cast(Payload.EnumFull).?.data; | |
| 2161 | return enum_full.fields.getIndex(field_name); | |
| 2162 | }, | |
| 2163 | .enum_simple => { | |
| 2164 | const enum_simple = ty.castTag(.enum_simple).?.data; | |
| 2165 | return enum_simple.fields.getIndex(field_name); | |
| 2166 | }, | |
| 2167 | else => unreachable, | |
| 2168 | } | |
| 2169 | } | |
| 2170 | ||
| 2171 | /// Asserts `ty` is an enum. `enum_tag` can either be `enum_field_index` or | |
| 2172 | /// an integer which represents the enum value. Returns the field index in | |
| 2173 | /// declaration order, or `null` if `enum_tag` does not match any field. | |
| 2174 | pub fn enumTagFieldIndex(ty: Type, enum_tag: Value) ?usize { | |
| 2175 | if (enum_tag.castTag(.enum_field_index)) |payload| { | |
| 2176 | return @as(usize, payload.data); | |
| 2177 | } | |
| 2178 | const S = struct { | |
| 2179 | fn fieldWithRange(int_val: Value, end: usize) ?usize { | |
| 2180 | if (int_val.compareWithZero(.lt)) return null; | |
| 2181 | var end_payload: Value.Payload.U64 = .{ | |
| 2182 | .base = .{ .tag = .int_u64 }, | |
| 2183 | .data = end, | |
| 2184 | }; | |
| 2185 | const end_val = Value.initPayload(&end_payload.base); | |
| 2186 | if (int_val.compare(.gte, end_val)) return null; | |
| 2187 | return int_val.toUnsignedInt(); | |
| 2188 | } | |
| 2189 | }; | |
| 2190 | switch (ty.tag()) { | |
| 2191 | .enum_full, .enum_nonexhaustive => { | |
| 2192 | const enum_full = ty.cast(Payload.EnumFull).?.data; | |
| 2193 | if (enum_full.values.count() == 0) { | |
| 2194 | return S.fieldWithRange(enum_tag, enum_full.fields.count()); | |
| 2195 | } else { | |
| 2196 | return enum_full.values.getIndex(enum_tag); | |
| 2197 | } | |
| 2198 | }, | |
| 2199 | .enum_simple => { | |
| 2200 | const enum_simple = ty.castTag(.enum_simple).?.data; | |
| 2201 | return S.fieldWithRange(enum_tag, enum_simple.fields.count()); | |
| 2202 | }, | |
| 2203 | else => unreachable, | |
| 2204 | } | |
| 2205 | } | |
| 2206 | ||
| 2207 | pub fn declSrcLoc(ty: Type) Module.SrcLoc { | |
| 2208 | switch (ty.tag()) { | |
| 2209 | .enum_full, .enum_nonexhaustive => { | |
| 2210 | const enum_full = ty.cast(Payload.EnumFull).?.data; | |
| 2211 | return enum_full.srcLoc(); | |
| 2212 | }, | |
| 2213 | .enum_simple => { | |
| 2214 | const enum_simple = ty.castTag(.enum_simple).?.data; | |
| 2215 | return enum_simple.srcLoc(); | |
| 2216 | }, | |
| 2217 | .@"struct" => { | |
| 2218 | const struct_obj = ty.castTag(.@"struct").?.data; | |
| 2219 | return struct_obj.srcLoc(); | |
| 2220 | }, | |
| 2221 | .error_set => { | |
| 2222 | const error_set = ty.castTag(.error_set).?.data; | |
| 2223 | return error_set.srcLoc(); | |
| 2224 | }, | |
| 2225 | else => unreachable, | |
| 2226 | } | |
| 2227 | } | |
| 2228 | ||
| 2229 | /// Asserts the type is an enum. | |
| 2230 | pub fn enumHasInt(ty: Type, int: Value, target: Target) bool { | |
| 2231 | const S = struct { | |
| 2232 | fn intInRange(int_val: Value, end: usize) bool { | |
| 2233 | if (int_val.compareWithZero(.lt)) return false; | |
| 2234 | var end_payload: Value.Payload.U64 = .{ | |
| 2235 | .base = .{ .tag = .int_u64 }, | |
| 2236 | .data = end, | |
| 2237 | }; | |
| 2238 | const end_val = Value.initPayload(&end_payload.base); | |
| 2239 | if (int_val.compare(.gte, end_val)) return false; | |
| 2240 | return true; | |
| 2241 | } | |
| 2242 | }; | |
| 2243 | switch (ty.tag()) { | |
| 2244 | .enum_nonexhaustive => return int.intFitsInType(ty, target), | |
| 2245 | .enum_full => { | |
| 2246 | const enum_full = ty.castTag(.enum_full).?.data; | |
| 2247 | if (enum_full.values.count() == 0) { | |
| 2248 | return S.intInRange(int, enum_full.fields.count()); | |
| 2249 | } else { | |
| 2250 | return enum_full.values.contains(int); | |
| 2251 | } | |
| 2252 | }, | |
| 2253 | .enum_simple => { | |
| 2254 | const enum_simple = ty.castTag(.enum_simple).?.data; | |
| 2255 | return S.intInRange(int, enum_simple.fields.count()); | |
| 2256 | }, | |
| 2257 | ||
| 2258 | else => unreachable, | |
| 2259 | } | |
| 3388 | 2260 | } |
| 3389 | 2261 | |
| 3390 | 2262 | /// This enum does not directly correspond to `std.builtin.TypeId` because |
| ... | ... | @@ -3476,6 +2348,9 @@ pub const Type = extern union { |
| 3476 | 2348 | empty_struct, |
| 3477 | 2349 | @"opaque", |
| 3478 | 2350 | @"struct", |
| 2351 | enum_simple, | |
| 2352 | enum_full, | |
| 2353 | enum_nonexhaustive, | |
| 3479 | 2354 | |
| 3480 | 2355 | pub const last_no_payload_tag = Tag.inferred_alloc_const; |
| 3481 | 2356 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | ... | @@ -3562,6 +2437,8 @@ pub const Type = extern union { |
| 3562 | 2437 | .error_set_single => Payload.Name, |
| 3563 | 2438 | .@"opaque" => Payload.Opaque, |
| 3564 | 2439 | .@"struct" => Payload.Struct, |
| 2440 | .enum_full, .enum_nonexhaustive => Payload.EnumFull, | |
| 2441 | .enum_simple => Payload.EnumSimple, | |
| 3565 | 2442 | .empty_struct => Payload.ContainerScope, |
| 3566 | 2443 | }; |
| 3567 | 2444 | } |
| ... | ... | @@ -3699,6 +2576,16 @@ pub const Type = extern union { |
| 3699 | 2576 | base: Payload = .{ .tag = .@"struct" }, |
| 3700 | 2577 | data: *Module.Struct, |
| 3701 | 2578 | }; |
| 2579 | ||
| 2580 | pub const EnumFull = struct { | |
| 2581 | base: Payload, | |
| 2582 | data: *Module.EnumFull, | |
| 2583 | }; | |
| 2584 | ||
| 2585 | pub const EnumSimple = struct { | |
| 2586 | base: Payload = .{ .tag = .enum_simple }, | |
| 2587 | data: *Module.EnumSimple, | |
| 2588 | }; | |
| 3702 | 2589 | }; |
| 3703 | 2590 | }; |
| 3704 | 2591 |
src/value.zig+57-780| ... | ... | @@ -103,6 +103,8 @@ pub const Value = extern union { |
| 103 | 103 | float_64, |
| 104 | 104 | float_128, |
| 105 | 105 | enum_literal, |
| 106 | /// A specific enum tag, indicated by the field index (declaration order). | |
| 107 | enum_field_index, | |
| 106 | 108 | @"error", |
| 107 | 109 | error_union, |
| 108 | 110 | /// This is a special value that tracks a set of types that have been stored |
| ... | ... | @@ -186,6 +188,8 @@ pub const Value = extern union { |
| 186 | 188 | .enum_literal, |
| 187 | 189 | => Payload.Bytes, |
| 188 | 190 | |
| 191 | .enum_field_index => Payload.U32, | |
| 192 | ||
| 189 | 193 | .ty => Payload.Ty, |
| 190 | 194 | .int_type => Payload.IntType, |
| 191 | 195 | .int_u64 => Payload.U64, |
| ... | ... | @@ -394,6 +398,7 @@ pub const Value = extern union { |
| 394 | 398 | }; |
| 395 | 399 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 396 | 400 | }, |
| 401 | .enum_field_index => return self.copyPayloadShallow(allocator, Payload.U32), | |
| 397 | 402 | .@"error" => return self.copyPayloadShallow(allocator, Payload.Error), |
| 398 | 403 | .error_union => { |
| 399 | 404 | const payload = self.castTag(.error_union).?; |
| ... | ... | @@ -416,6 +421,8 @@ pub const Value = extern union { |
| 416 | 421 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 417 | 422 | } |
| 418 | 423 | |
| 424 | /// TODO this should become a debug dump() function. In order to print values in a meaningful way | |
| 425 | /// we also need access to the type. | |
| 419 | 426 | pub fn format( |
| 420 | 427 | self: Value, |
| 421 | 428 | comptime fmt: []const u8, |
| ... | ... | @@ -506,6 +513,7 @@ pub const Value = extern union { |
| 506 | 513 | }, |
| 507 | 514 | .empty_array => return out_stream.writeAll(".{}"), |
| 508 | 515 | .enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(self.castTag(.enum_literal).?.data)}), |
| 516 | .enum_field_index => return out_stream.print("(enum field {d})", .{self.castTag(.enum_field_index).?.data}), | |
| 509 | 517 | .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(self.castTag(.bytes).?.data)}), |
| 510 | 518 | .repeated => { |
| 511 | 519 | try out_stream.writeAll("(repeated) "); |
| ... | ... | @@ -626,6 +634,7 @@ pub const Value = extern union { |
| 626 | 634 | .float_64, |
| 627 | 635 | .float_128, |
| 628 | 636 | .enum_literal, |
| 637 | .enum_field_index, | |
| 629 | 638 | .@"error", |
| 630 | 639 | .error_union, |
| 631 | 640 | .empty_struct_value, |
| ... | ... | @@ -638,76 +647,6 @@ pub const Value = extern union { |
| 638 | 647 | /// Asserts the value is an integer. |
| 639 | 648 | pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst { |
| 640 | 649 | switch (self.tag()) { |
| 641 | .ty, | |
| 642 | .int_type, | |
| 643 | .u8_type, | |
| 644 | .i8_type, | |
| 645 | .u16_type, | |
| 646 | .i16_type, | |
| 647 | .u32_type, | |
| 648 | .i32_type, | |
| 649 | .u64_type, | |
| 650 | .i64_type, | |
| 651 | .u128_type, | |
| 652 | .i128_type, | |
| 653 | .usize_type, | |
| 654 | .isize_type, | |
| 655 | .c_short_type, | |
| 656 | .c_ushort_type, | |
| 657 | .c_int_type, | |
| 658 | .c_uint_type, | |
| 659 | .c_long_type, | |
| 660 | .c_ulong_type, | |
| 661 | .c_longlong_type, | |
| 662 | .c_ulonglong_type, | |
| 663 | .c_longdouble_type, | |
| 664 | .f16_type, | |
| 665 | .f32_type, | |
| 666 | .f64_type, | |
| 667 | .f128_type, | |
| 668 | .c_void_type, | |
| 669 | .bool_type, | |
| 670 | .void_type, | |
| 671 | .type_type, | |
| 672 | .anyerror_type, | |
| 673 | .comptime_int_type, | |
| 674 | .comptime_float_type, | |
| 675 | .noreturn_type, | |
| 676 | .null_type, | |
| 677 | .undefined_type, | |
| 678 | .fn_noreturn_no_args_type, | |
| 679 | .fn_void_no_args_type, | |
| 680 | .fn_naked_noreturn_no_args_type, | |
| 681 | .fn_ccc_void_no_args_type, | |
| 682 | .single_const_pointer_to_comptime_int_type, | |
| 683 | .const_slice_u8_type, | |
| 684 | .enum_literal_type, | |
| 685 | .null_value, | |
| 686 | .function, | |
| 687 | .extern_fn, | |
| 688 | .variable, | |
| 689 | .ref_val, | |
| 690 | .decl_ref, | |
| 691 | .elem_ptr, | |
| 692 | .bytes, | |
| 693 | .repeated, | |
| 694 | .float_16, | |
| 695 | .float_32, | |
| 696 | .float_64, | |
| 697 | .float_128, | |
| 698 | .void_value, | |
| 699 | .unreachable_value, | |
| 700 | .empty_array, | |
| 701 | .enum_literal, | |
| 702 | .error_union, | |
| 703 | .@"error", | |
| 704 | .empty_struct_value, | |
| 705 | .inferred_alloc, | |
| 706 | .abi_align_default, | |
| 707 | => unreachable, | |
| 708 | ||
| 709 | .undef => unreachable, | |
| 710 | ||
| 711 | 650 | .zero, |
| 712 | 651 | .bool_false, |
| 713 | 652 | => return BigIntMutable.init(&space.limbs, 0).toConst(), |
| ... | ... | @@ -720,82 +659,15 @@ pub const Value = extern union { |
| 720 | 659 | .int_i64 => return BigIntMutable.init(&space.limbs, self.castTag(.int_i64).?.data).toConst(), |
| 721 | 660 | .int_big_positive => return self.castTag(.int_big_positive).?.asBigInt(), |
| 722 | 661 | .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt(), |
| 662 | ||
| 663 | .undef => unreachable, | |
| 664 | else => unreachable, | |
| 723 | 665 | } |
| 724 | 666 | } |
| 725 | 667 | |
| 726 | 668 | /// Asserts the value is an integer and it fits in a u64 |
| 727 | 669 | pub fn toUnsignedInt(self: Value) u64 { |
| 728 | 670 | switch (self.tag()) { |
| 729 | .ty, | |
| 730 | .int_type, | |
| 731 | .u8_type, | |
| 732 | .i8_type, | |
| 733 | .u16_type, | |
| 734 | .i16_type, | |
| 735 | .u32_type, | |
| 736 | .i32_type, | |
| 737 | .u64_type, | |
| 738 | .i64_type, | |
| 739 | .u128_type, | |
| 740 | .i128_type, | |
| 741 | .usize_type, | |
| 742 | .isize_type, | |
| 743 | .c_short_type, | |
| 744 | .c_ushort_type, | |
| 745 | .c_int_type, | |
| 746 | .c_uint_type, | |
| 747 | .c_long_type, | |
| 748 | .c_ulong_type, | |
| 749 | .c_longlong_type, | |
| 750 | .c_ulonglong_type, | |
| 751 | .c_longdouble_type, | |
| 752 | .f16_type, | |
| 753 | .f32_type, | |
| 754 | .f64_type, | |
| 755 | .f128_type, | |
| 756 | .c_void_type, | |
| 757 | .bool_type, | |
| 758 | .void_type, | |
| 759 | .type_type, | |
| 760 | .anyerror_type, | |
| 761 | .comptime_int_type, | |
| 762 | .comptime_float_type, | |
| 763 | .noreturn_type, | |
| 764 | .null_type, | |
| 765 | .undefined_type, | |
| 766 | .fn_noreturn_no_args_type, | |
| 767 | .fn_void_no_args_type, | |
| 768 | .fn_naked_noreturn_no_args_type, | |
| 769 | .fn_ccc_void_no_args_type, | |
| 770 | .single_const_pointer_to_comptime_int_type, | |
| 771 | .const_slice_u8_type, | |
| 772 | .enum_literal_type, | |
| 773 | .null_value, | |
| 774 | .function, | |
| 775 | .extern_fn, | |
| 776 | .variable, | |
| 777 | .ref_val, | |
| 778 | .decl_ref, | |
| 779 | .elem_ptr, | |
| 780 | .bytes, | |
| 781 | .repeated, | |
| 782 | .float_16, | |
| 783 | .float_32, | |
| 784 | .float_64, | |
| 785 | .float_128, | |
| 786 | .void_value, | |
| 787 | .unreachable_value, | |
| 788 | .empty_array, | |
| 789 | .enum_literal, | |
| 790 | .@"error", | |
| 791 | .error_union, | |
| 792 | .empty_struct_value, | |
| 793 | .inferred_alloc, | |
| 794 | .abi_align_default, | |
| 795 | => unreachable, | |
| 796 | ||
| 797 | .undef => unreachable, | |
| 798 | ||
| 799 | 671 | .zero, |
| 800 | 672 | .bool_false, |
| 801 | 673 | => return 0, |
| ... | ... | @@ -808,82 +680,15 @@ pub const Value = extern union { |
| 808 | 680 | .int_i64 => return @intCast(u64, self.castTag(.int_i64).?.data), |
| 809 | 681 | .int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().to(u64) catch unreachable, |
| 810 | 682 | .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().to(u64) catch unreachable, |
| 683 | ||
| 684 | .undef => unreachable, | |
| 685 | else => unreachable, | |
| 811 | 686 | } |
| 812 | 687 | } |
| 813 | 688 | |
| 814 | 689 | /// Asserts the value is an integer and it fits in a i64 |
| 815 | 690 | pub fn toSignedInt(self: Value) i64 { |
| 816 | 691 | switch (self.tag()) { |
| 817 | .ty, | |
| 818 | .int_type, | |
| 819 | .u8_type, | |
| 820 | .i8_type, | |
| 821 | .u16_type, | |
| 822 | .i16_type, | |
| 823 | .u32_type, | |
| 824 | .i32_type, | |
| 825 | .u64_type, | |
| 826 | .i64_type, | |
| 827 | .u128_type, | |
| 828 | .i128_type, | |
| 829 | .usize_type, | |
| 830 | .isize_type, | |
| 831 | .c_short_type, | |
| 832 | .c_ushort_type, | |
| 833 | .c_int_type, | |
| 834 | .c_uint_type, | |
| 835 | .c_long_type, | |
| 836 | .c_ulong_type, | |
| 837 | .c_longlong_type, | |
| 838 | .c_ulonglong_type, | |
| 839 | .c_longdouble_type, | |
| 840 | .f16_type, | |
| 841 | .f32_type, | |
| 842 | .f64_type, | |
| 843 | .f128_type, | |
| 844 | .c_void_type, | |
| 845 | .bool_type, | |
| 846 | .void_type, | |
| 847 | .type_type, | |
| 848 | .anyerror_type, | |
| 849 | .comptime_int_type, | |
| 850 | .comptime_float_type, | |
| 851 | .noreturn_type, | |
| 852 | .null_type, | |
| 853 | .undefined_type, | |
| 854 | .fn_noreturn_no_args_type, | |
| 855 | .fn_void_no_args_type, | |
| 856 | .fn_naked_noreturn_no_args_type, | |
| 857 | .fn_ccc_void_no_args_type, | |
| 858 | .single_const_pointer_to_comptime_int_type, | |
| 859 | .const_slice_u8_type, | |
| 860 | .enum_literal_type, | |
| 861 | .null_value, | |
| 862 | .function, | |
| 863 | .extern_fn, | |
| 864 | .variable, | |
| 865 | .ref_val, | |
| 866 | .decl_ref, | |
| 867 | .elem_ptr, | |
| 868 | .bytes, | |
| 869 | .repeated, | |
| 870 | .float_16, | |
| 871 | .float_32, | |
| 872 | .float_64, | |
| 873 | .float_128, | |
| 874 | .void_value, | |
| 875 | .unreachable_value, | |
| 876 | .empty_array, | |
| 877 | .enum_literal, | |
| 878 | .@"error", | |
| 879 | .error_union, | |
| 880 | .empty_struct_value, | |
| 881 | .inferred_alloc, | |
| 882 | .abi_align_default, | |
| 883 | => unreachable, | |
| 884 | ||
| 885 | .undef => unreachable, | |
| 886 | ||
| 887 | 692 | .zero, |
| 888 | 693 | .bool_false, |
| 889 | 694 | => return 0, |
| ... | ... | @@ -896,6 +701,9 @@ pub const Value = extern union { |
| 896 | 701 | .int_i64 => return self.castTag(.int_i64).?.data, |
| 897 | 702 | .int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().to(i64) catch unreachable, |
| 898 | 703 | .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().to(i64) catch unreachable, |
| 704 | ||
| 705 | .undef => unreachable, | |
| 706 | else => unreachable, | |
| 899 | 707 | } |
| 900 | 708 | } |
| 901 | 709 | |
| ... | ... | @@ -929,75 +737,6 @@ pub const Value = extern union { |
| 929 | 737 | /// Returns the number of bits the value requires to represent stored in twos complement form. |
| 930 | 738 | pub fn intBitCountTwosComp(self: Value) usize { |
| 931 | 739 | switch (self.tag()) { |
| 932 | .ty, | |
| 933 | .int_type, | |
| 934 | .u8_type, | |
| 935 | .i8_type, | |
| 936 | .u16_type, | |
| 937 | .i16_type, | |
| 938 | .u32_type, | |
| 939 | .i32_type, | |
| 940 | .u64_type, | |
| 941 | .i64_type, | |
| 942 | .u128_type, | |
| 943 | .i128_type, | |
| 944 | .usize_type, | |
| 945 | .isize_type, | |
| 946 | .c_short_type, | |
| 947 | .c_ushort_type, | |
| 948 | .c_int_type, | |
| 949 | .c_uint_type, | |
| 950 | .c_long_type, | |
| 951 | .c_ulong_type, | |
| 952 | .c_longlong_type, | |
| 953 | .c_ulonglong_type, | |
| 954 | .c_longdouble_type, | |
| 955 | .f16_type, | |
| 956 | .f32_type, | |
| 957 | .f64_type, | |
| 958 | .f128_type, | |
| 959 | .c_void_type, | |
| 960 | .bool_type, | |
| 961 | .void_type, | |
| 962 | .type_type, | |
| 963 | .anyerror_type, | |
| 964 | .comptime_int_type, | |
| 965 | .comptime_float_type, | |
| 966 | .noreturn_type, | |
| 967 | .null_type, | |
| 968 | .undefined_type, | |
| 969 | .fn_noreturn_no_args_type, | |
| 970 | .fn_void_no_args_type, | |
| 971 | .fn_naked_noreturn_no_args_type, | |
| 972 | .fn_ccc_void_no_args_type, | |
| 973 | .single_const_pointer_to_comptime_int_type, | |
| 974 | .const_slice_u8_type, | |
| 975 | .enum_literal_type, | |
| 976 | .null_value, | |
| 977 | .function, | |
| 978 | .extern_fn, | |
| 979 | .variable, | |
| 980 | .ref_val, | |
| 981 | .decl_ref, | |
| 982 | .elem_ptr, | |
| 983 | .bytes, | |
| 984 | .undef, | |
| 985 | .repeated, | |
| 986 | .float_16, | |
| 987 | .float_32, | |
| 988 | .float_64, | |
| 989 | .float_128, | |
| 990 | .void_value, | |
| 991 | .unreachable_value, | |
| 992 | .empty_array, | |
| 993 | .enum_literal, | |
| 994 | .@"error", | |
| 995 | .error_union, | |
| 996 | .empty_struct_value, | |
| 997 | .inferred_alloc, | |
| 998 | .abi_align_default, | |
| 999 | => unreachable, | |
| 1000 | ||
| 1001 | 740 | .zero, |
| 1002 | 741 | .bool_false, |
| 1003 | 742 | => return 0, |
| ... | ... | @@ -1016,80 +755,14 @@ pub const Value = extern union { |
| 1016 | 755 | }, |
| 1017 | 756 | .int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().bitCountTwosComp(), |
| 1018 | 757 | .int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().bitCountTwosComp(), |
| 758 | ||
| 759 | else => unreachable, | |
| 1019 | 760 | } |
| 1020 | 761 | } |
| 1021 | 762 | |
| 1022 | 763 | /// Asserts the value is an integer, and the destination type is ComptimeInt or Int. |
| 1023 | 764 | pub fn intFitsInType(self: Value, ty: Type, target: Target) bool { |
| 1024 | 765 | switch (self.tag()) { |
| 1025 | .ty, | |
| 1026 | .int_type, | |
| 1027 | .u8_type, | |
| 1028 | .i8_type, | |
| 1029 | .u16_type, | |
| 1030 | .i16_type, | |
| 1031 | .u32_type, | |
| 1032 | .i32_type, | |
| 1033 | .u64_type, | |
| 1034 | .i64_type, | |
| 1035 | .u128_type, | |
| 1036 | .i128_type, | |
| 1037 | .usize_type, | |
| 1038 | .isize_type, | |
| 1039 | .c_short_type, | |
| 1040 | .c_ushort_type, | |
| 1041 | .c_int_type, | |
| 1042 | .c_uint_type, | |
| 1043 | .c_long_type, | |
| 1044 | .c_ulong_type, | |
| 1045 | .c_longlong_type, | |
| 1046 | .c_ulonglong_type, | |
| 1047 | .c_longdouble_type, | |
| 1048 | .f16_type, | |
| 1049 | .f32_type, | |
| 1050 | .f64_type, | |
| 1051 | .f128_type, | |
| 1052 | .c_void_type, | |
| 1053 | .bool_type, | |
| 1054 | .void_type, | |
| 1055 | .type_type, | |
| 1056 | .anyerror_type, | |
| 1057 | .comptime_int_type, | |
| 1058 | .comptime_float_type, | |
| 1059 | .noreturn_type, | |
| 1060 | .null_type, | |
| 1061 | .undefined_type, | |
| 1062 | .fn_noreturn_no_args_type, | |
| 1063 | .fn_void_no_args_type, | |
| 1064 | .fn_naked_noreturn_no_args_type, | |
| 1065 | .fn_ccc_void_no_args_type, | |
| 1066 | .single_const_pointer_to_comptime_int_type, | |
| 1067 | .const_slice_u8_type, | |
| 1068 | .enum_literal_type, | |
| 1069 | .null_value, | |
| 1070 | .function, | |
| 1071 | .extern_fn, | |
| 1072 | .variable, | |
| 1073 | .ref_val, | |
| 1074 | .decl_ref, | |
| 1075 | .elem_ptr, | |
| 1076 | .bytes, | |
| 1077 | .repeated, | |
| 1078 | .float_16, | |
| 1079 | .float_32, | |
| 1080 | .float_64, | |
| 1081 | .float_128, | |
| 1082 | .void_value, | |
| 1083 | .unreachable_value, | |
| 1084 | .empty_array, | |
| 1085 | .enum_literal, | |
| 1086 | .@"error", | |
| 1087 | .error_union, | |
| 1088 | .empty_struct_value, | |
| 1089 | .inferred_alloc, | |
| 1090 | .abi_align_default, | |
| 1091 | => unreachable, | |
| 1092 | ||
| 1093 | 766 | .zero, |
| 1094 | 767 | .undef, |
| 1095 | 768 | .bool_false, |
| ... | ... | @@ -1144,6 +817,8 @@ pub const Value = extern union { |
| 1144 | 817 | .ComptimeInt => return true, |
| 1145 | 818 | else => unreachable, |
| 1146 | 819 | }, |
| 820 | ||
| 821 | else => unreachable, | |
| 1147 | 822 | } |
| 1148 | 823 | } |
| 1149 | 824 | |
| ... | ... | @@ -1180,77 +855,6 @@ pub const Value = extern union { |
| 1180 | 855 | /// Asserts the value is a float |
| 1181 | 856 | pub fn floatHasFraction(self: Value) bool { |
| 1182 | 857 | return switch (self.tag()) { |
| 1183 | .ty, | |
| 1184 | .int_type, | |
| 1185 | .u8_type, | |
| 1186 | .i8_type, | |
| 1187 | .u16_type, | |
| 1188 | .i16_type, | |
| 1189 | .u32_type, | |
| 1190 | .i32_type, | |
| 1191 | .u64_type, | |
| 1192 | .i64_type, | |
| 1193 | .u128_type, | |
| 1194 | .i128_type, | |
| 1195 | .usize_type, | |
| 1196 | .isize_type, | |
| 1197 | .c_short_type, | |
| 1198 | .c_ushort_type, | |
| 1199 | .c_int_type, | |
| 1200 | .c_uint_type, | |
| 1201 | .c_long_type, | |
| 1202 | .c_ulong_type, | |
| 1203 | .c_longlong_type, | |
| 1204 | .c_ulonglong_type, | |
| 1205 | .c_longdouble_type, | |
| 1206 | .f16_type, | |
| 1207 | .f32_type, | |
| 1208 | .f64_type, | |
| 1209 | .f128_type, | |
| 1210 | .c_void_type, | |
| 1211 | .bool_type, | |
| 1212 | .void_type, | |
| 1213 | .type_type, | |
| 1214 | .anyerror_type, | |
| 1215 | .comptime_int_type, | |
| 1216 | .comptime_float_type, | |
| 1217 | .noreturn_type, | |
| 1218 | .null_type, | |
| 1219 | .undefined_type, | |
| 1220 | .fn_noreturn_no_args_type, | |
| 1221 | .fn_void_no_args_type, | |
| 1222 | .fn_naked_noreturn_no_args_type, | |
| 1223 | .fn_ccc_void_no_args_type, | |
| 1224 | .single_const_pointer_to_comptime_int_type, | |
| 1225 | .const_slice_u8_type, | |
| 1226 | .enum_literal_type, | |
| 1227 | .bool_true, | |
| 1228 | .bool_false, | |
| 1229 | .null_value, | |
| 1230 | .function, | |
| 1231 | .extern_fn, | |
| 1232 | .variable, | |
| 1233 | .ref_val, | |
| 1234 | .decl_ref, | |
| 1235 | .elem_ptr, | |
| 1236 | .bytes, | |
| 1237 | .repeated, | |
| 1238 | .undef, | |
| 1239 | .int_u64, | |
| 1240 | .int_i64, | |
| 1241 | .int_big_positive, | |
| 1242 | .int_big_negative, | |
| 1243 | .empty_array, | |
| 1244 | .void_value, | |
| 1245 | .unreachable_value, | |
| 1246 | .enum_literal, | |
| 1247 | .@"error", | |
| 1248 | .error_union, | |
| 1249 | .empty_struct_value, | |
| 1250 | .inferred_alloc, | |
| 1251 | .abi_align_default, | |
| 1252 | => unreachable, | |
| 1253 | ||
| 1254 | 858 | .zero, |
| 1255 | 859 | .one, |
| 1256 | 860 | => false, |
| ... | ... | @@ -1260,76 +864,13 @@ pub const Value = extern union { |
| 1260 | 864 | .float_64 => @rem(self.castTag(.float_64).?.data, 1) != 0, |
| 1261 | 865 | // .float_128 => @rem(self.castTag(.float_128).?.data, 1) != 0, |
| 1262 | 866 | .float_128 => @panic("TODO lld: error: undefined symbol: fmodl"), |
| 867 | ||
| 868 | else => unreachable, | |
| 1263 | 869 | }; |
| 1264 | 870 | } |
| 1265 | 871 | |
| 1266 | 872 | pub fn orderAgainstZero(lhs: Value) std.math.Order { |
| 1267 | 873 | return switch (lhs.tag()) { |
| 1268 | .ty, | |
| 1269 | .int_type, | |
| 1270 | .u8_type, | |
| 1271 | .i8_type, | |
| 1272 | .u16_type, | |
| 1273 | .i16_type, | |
| 1274 | .u32_type, | |
| 1275 | .i32_type, | |
| 1276 | .u64_type, | |
| 1277 | .i64_type, | |
| 1278 | .u128_type, | |
| 1279 | .i128_type, | |
| 1280 | .usize_type, | |
| 1281 | .isize_type, | |
| 1282 | .c_short_type, | |
| 1283 | .c_ushort_type, | |
| 1284 | .c_int_type, | |
| 1285 | .c_uint_type, | |
| 1286 | .c_long_type, | |
| 1287 | .c_ulong_type, | |
| 1288 | .c_longlong_type, | |
| 1289 | .c_ulonglong_type, | |
| 1290 | .c_longdouble_type, | |
| 1291 | .f16_type, | |
| 1292 | .f32_type, | |
| 1293 | .f64_type, | |
| 1294 | .f128_type, | |
| 1295 | .c_void_type, | |
| 1296 | .bool_type, | |
| 1297 | .void_type, | |
| 1298 | .type_type, | |
| 1299 | .anyerror_type, | |
| 1300 | .comptime_int_type, | |
| 1301 | .comptime_float_type, | |
| 1302 | .noreturn_type, | |
| 1303 | .null_type, | |
| 1304 | .undefined_type, | |
| 1305 | .fn_noreturn_no_args_type, | |
| 1306 | .fn_void_no_args_type, | |
| 1307 | .fn_naked_noreturn_no_args_type, | |
| 1308 | .fn_ccc_void_no_args_type, | |
| 1309 | .single_const_pointer_to_comptime_int_type, | |
| 1310 | .const_slice_u8_type, | |
| 1311 | .enum_literal_type, | |
| 1312 | .null_value, | |
| 1313 | .function, | |
| 1314 | .extern_fn, | |
| 1315 | .variable, | |
| 1316 | .ref_val, | |
| 1317 | .decl_ref, | |
| 1318 | .elem_ptr, | |
| 1319 | .bytes, | |
| 1320 | .repeated, | |
| 1321 | .undef, | |
| 1322 | .void_value, | |
| 1323 | .unreachable_value, | |
| 1324 | .empty_array, | |
| 1325 | .enum_literal, | |
| 1326 | .@"error", | |
| 1327 | .error_union, | |
| 1328 | .empty_struct_value, | |
| 1329 | .inferred_alloc, | |
| 1330 | .abi_align_default, | |
| 1331 | => unreachable, | |
| 1332 | ||
| 1333 | 874 | .zero, |
| 1334 | 875 | .bool_false, |
| 1335 | 876 | => .eq, |
| ... | ... | @@ -1347,6 +888,8 @@ pub const Value = extern union { |
| 1347 | 888 | .float_32 => std.math.order(lhs.castTag(.float_32).?.data, 0), |
| 1348 | 889 | .float_64 => std.math.order(lhs.castTag(.float_64).?.data, 0), |
| 1349 | 890 | .float_128 => std.math.order(lhs.castTag(.float_128).?.data, 0), |
| 891 | ||
| 892 | else => unreachable, | |
| 1350 | 893 | }; |
| 1351 | 894 | } |
| 1352 | 895 | |
| ... | ... | @@ -1396,10 +939,12 @@ pub const Value = extern union { |
| 1396 | 939 | } |
| 1397 | 940 | |
| 1398 | 941 | pub fn eql(a: Value, b: Value) bool { |
| 1399 | if (a.tag() == b.tag()) { | |
| 1400 | if (a.tag() == .void_value or a.tag() == .null_value) { | |
| 942 | const a_tag = a.tag(); | |
| 943 | const b_tag = b.tag(); | |
| 944 | if (a_tag == b_tag) { | |
| 945 | if (a_tag == .void_value or a_tag == .null_value) { | |
| 1401 | 946 | return true; |
| 1402 | } else if (a.tag() == .enum_literal) { | |
| 947 | } else if (a_tag == .enum_literal) { | |
| 1403 | 948 | const a_name = a.castTag(.enum_literal).?.data; |
| 1404 | 949 | const b_name = b.castTag(.enum_literal).?.data; |
| 1405 | 950 | return std.mem.eql(u8, a_name, b_name); |
| ... | ... | @@ -1416,6 +961,10 @@ pub const Value = extern union { |
| 1416 | 961 | return compare(a, .eq, b); |
| 1417 | 962 | } |
| 1418 | 963 | |
| 964 | pub fn hash_u32(self: Value) u32 { | |
| 965 | return @truncate(u32, self.hash()); | |
| 966 | } | |
| 967 | ||
| 1419 | 968 | pub fn hash(self: Value) u64 { |
| 1420 | 969 | var hasher = std.hash.Wyhash.init(0); |
| 1421 | 970 | |
| ... | ... | @@ -1493,11 +1042,18 @@ pub const Value = extern union { |
| 1493 | 1042 | .zero, .bool_false => std.hash.autoHash(&hasher, @as(u64, 0)), |
| 1494 | 1043 | .one, .bool_true => std.hash.autoHash(&hasher, @as(u64, 1)), |
| 1495 | 1044 | |
| 1496 | .float_16, .float_32, .float_64, .float_128 => {}, | |
| 1045 | .float_16, .float_32, .float_64, .float_128 => { | |
| 1046 | @panic("TODO implement Value.hash for floats"); | |
| 1047 | }, | |
| 1048 | ||
| 1497 | 1049 | .enum_literal => { |
| 1498 | 1050 | const payload = self.castTag(.enum_literal).?; |
| 1499 | 1051 | hasher.update(payload.data); |
| 1500 | 1052 | }, |
| 1053 | .enum_field_index => { | |
| 1054 | const payload = self.castTag(.enum_field_index).?; | |
| 1055 | std.hash.autoHash(&hasher, payload.data); | |
| 1056 | }, | |
| 1501 | 1057 | .bytes => { |
| 1502 | 1058 | const payload = self.castTag(.bytes).?; |
| 1503 | 1059 | hasher.update(payload.data); |
| ... | ... | @@ -1573,80 +1129,6 @@ pub const Value = extern union { |
| 1573 | 1129 | /// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis. |
| 1574 | 1130 | pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value { |
| 1575 | 1131 | return switch (self.tag()) { |
| 1576 | .ty, | |
| 1577 | .int_type, | |
| 1578 | .u8_type, | |
| 1579 | .i8_type, | |
| 1580 | .u16_type, | |
| 1581 | .i16_type, | |
| 1582 | .u32_type, | |
| 1583 | .i32_type, | |
| 1584 | .u64_type, | |
| 1585 | .i64_type, | |
| 1586 | .u128_type, | |
| 1587 | .i128_type, | |
| 1588 | .usize_type, | |
| 1589 | .isize_type, | |
| 1590 | .c_short_type, | |
| 1591 | .c_ushort_type, | |
| 1592 | .c_int_type, | |
| 1593 | .c_uint_type, | |
| 1594 | .c_long_type, | |
| 1595 | .c_ulong_type, | |
| 1596 | .c_longlong_type, | |
| 1597 | .c_ulonglong_type, | |
| 1598 | .c_longdouble_type, | |
| 1599 | .f16_type, | |
| 1600 | .f32_type, | |
| 1601 | .f64_type, | |
| 1602 | .f128_type, | |
| 1603 | .c_void_type, | |
| 1604 | .bool_type, | |
| 1605 | .void_type, | |
| 1606 | .type_type, | |
| 1607 | .anyerror_type, | |
| 1608 | .comptime_int_type, | |
| 1609 | .comptime_float_type, | |
| 1610 | .noreturn_type, | |
| 1611 | .null_type, | |
| 1612 | .undefined_type, | |
| 1613 | .fn_noreturn_no_args_type, | |
| 1614 | .fn_void_no_args_type, | |
| 1615 | .fn_naked_noreturn_no_args_type, | |
| 1616 | .fn_ccc_void_no_args_type, | |
| 1617 | .single_const_pointer_to_comptime_int_type, | |
| 1618 | .const_slice_u8_type, | |
| 1619 | .enum_literal_type, | |
| 1620 | .zero, | |
| 1621 | .one, | |
| 1622 | .bool_true, | |
| 1623 | .bool_false, | |
| 1624 | .null_value, | |
| 1625 | .function, | |
| 1626 | .extern_fn, | |
| 1627 | .variable, | |
| 1628 | .int_u64, | |
| 1629 | .int_i64, | |
| 1630 | .int_big_positive, | |
| 1631 | .int_big_negative, | |
| 1632 | .bytes, | |
| 1633 | .undef, | |
| 1634 | .repeated, | |
| 1635 | .float_16, | |
| 1636 | .float_32, | |
| 1637 | .float_64, | |
| 1638 | .float_128, | |
| 1639 | .void_value, | |
| 1640 | .unreachable_value, | |
| 1641 | .empty_array, | |
| 1642 | .enum_literal, | |
| 1643 | .@"error", | |
| 1644 | .error_union, | |
| 1645 | .empty_struct_value, | |
| 1646 | .inferred_alloc, | |
| 1647 | .abi_align_default, | |
| 1648 | => unreachable, | |
| 1649 | ||
| 1650 | 1132 | .ref_val => self.castTag(.ref_val).?.data, |
| 1651 | 1133 | .decl_ref => self.castTag(.decl_ref).?.data.value(), |
| 1652 | 1134 | .elem_ptr => { |
| ... | ... | @@ -1654,6 +1136,8 @@ pub const Value = extern union { |
| 1654 | 1136 | const array_val = try elem_ptr.array_ptr.pointerDeref(allocator); |
| 1655 | 1137 | return array_val.elemValue(allocator, elem_ptr.index); |
| 1656 | 1138 | }, |
| 1139 | ||
| 1140 | else => unreachable, | |
| 1657 | 1141 | }; |
| 1658 | 1142 | } |
| 1659 | 1143 | |
| ... | ... | @@ -1661,86 +1145,14 @@ pub const Value = extern union { |
| 1661 | 1145 | /// or an unknown-length pointer, and returns the element value at the index. |
| 1662 | 1146 | pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value { |
| 1663 | 1147 | switch (self.tag()) { |
| 1664 | .ty, | |
| 1665 | .int_type, | |
| 1666 | .u8_type, | |
| 1667 | .i8_type, | |
| 1668 | .u16_type, | |
| 1669 | .i16_type, | |
| 1670 | .u32_type, | |
| 1671 | .i32_type, | |
| 1672 | .u64_type, | |
| 1673 | .i64_type, | |
| 1674 | .u128_type, | |
| 1675 | .i128_type, | |
| 1676 | .usize_type, | |
| 1677 | .isize_type, | |
| 1678 | .c_short_type, | |
| 1679 | .c_ushort_type, | |
| 1680 | .c_int_type, | |
| 1681 | .c_uint_type, | |
| 1682 | .c_long_type, | |
| 1683 | .c_ulong_type, | |
| 1684 | .c_longlong_type, | |
| 1685 | .c_ulonglong_type, | |
| 1686 | .c_longdouble_type, | |
| 1687 | .f16_type, | |
| 1688 | .f32_type, | |
| 1689 | .f64_type, | |
| 1690 | .f128_type, | |
| 1691 | .c_void_type, | |
| 1692 | .bool_type, | |
| 1693 | .void_type, | |
| 1694 | .type_type, | |
| 1695 | .anyerror_type, | |
| 1696 | .comptime_int_type, | |
| 1697 | .comptime_float_type, | |
| 1698 | .noreturn_type, | |
| 1699 | .null_type, | |
| 1700 | .undefined_type, | |
| 1701 | .fn_noreturn_no_args_type, | |
| 1702 | .fn_void_no_args_type, | |
| 1703 | .fn_naked_noreturn_no_args_type, | |
| 1704 | .fn_ccc_void_no_args_type, | |
| 1705 | .single_const_pointer_to_comptime_int_type, | |
| 1706 | .const_slice_u8_type, | |
| 1707 | .enum_literal_type, | |
| 1708 | .zero, | |
| 1709 | .one, | |
| 1710 | .bool_true, | |
| 1711 | .bool_false, | |
| 1712 | .null_value, | |
| 1713 | .function, | |
| 1714 | .extern_fn, | |
| 1715 | .variable, | |
| 1716 | .int_u64, | |
| 1717 | .int_i64, | |
| 1718 | .int_big_positive, | |
| 1719 | .int_big_negative, | |
| 1720 | .undef, | |
| 1721 | .elem_ptr, | |
| 1722 | .ref_val, | |
| 1723 | .decl_ref, | |
| 1724 | .float_16, | |
| 1725 | .float_32, | |
| 1726 | .float_64, | |
| 1727 | .float_128, | |
| 1728 | .void_value, | |
| 1729 | .unreachable_value, | |
| 1730 | .enum_literal, | |
| 1731 | .@"error", | |
| 1732 | .error_union, | |
| 1733 | .empty_struct_value, | |
| 1734 | .inferred_alloc, | |
| 1735 | .abi_align_default, | |
| 1736 | => unreachable, | |
| 1737 | ||
| 1738 | 1148 | .empty_array => unreachable, // out of bounds array index |
| 1739 | 1149 | |
| 1740 | 1150 | .bytes => return Tag.int_u64.create(allocator, self.castTag(.bytes).?.data[index]), |
| 1741 | 1151 | |
| 1742 | 1152 | // No matter the index; all the elements are the same! |
| 1743 | 1153 | .repeated => return self.castTag(.repeated).?.data, |
| 1154 | ||
| 1155 | else => unreachable, | |
| 1744 | 1156 | } |
| 1745 | 1157 | } |
| 1746 | 1158 | |
| ... | ... | @@ -1766,161 +1178,18 @@ pub const Value = extern union { |
| 1766 | 1178 | /// Valid for all types. Asserts the value is not undefined and not unreachable. |
| 1767 | 1179 | pub fn isNull(self: Value) bool { |
| 1768 | 1180 | return switch (self.tag()) { |
| 1769 | .ty, | |
| 1770 | .int_type, | |
| 1771 | .u8_type, | |
| 1772 | .i8_type, | |
| 1773 | .u16_type, | |
| 1774 | .i16_type, | |
| 1775 | .u32_type, | |
| 1776 | .i32_type, | |
| 1777 | .u64_type, | |
| 1778 | .i64_type, | |
| 1779 | .u128_type, | |
| 1780 | .i128_type, | |
| 1781 | .usize_type, | |
| 1782 | .isize_type, | |
| 1783 | .c_short_type, | |
| 1784 | .c_ushort_type, | |
| 1785 | .c_int_type, | |
| 1786 | .c_uint_type, | |
| 1787 | .c_long_type, | |
| 1788 | .c_ulong_type, | |
| 1789 | .c_longlong_type, | |
| 1790 | .c_ulonglong_type, | |
| 1791 | .c_longdouble_type, | |
| 1792 | .f16_type, | |
| 1793 | .f32_type, | |
| 1794 | .f64_type, | |
| 1795 | .f128_type, | |
| 1796 | .c_void_type, | |
| 1797 | .bool_type, | |
| 1798 | .void_type, | |
| 1799 | .type_type, | |
| 1800 | .anyerror_type, | |
| 1801 | .comptime_int_type, | |
| 1802 | .comptime_float_type, | |
| 1803 | .noreturn_type, | |
| 1804 | .null_type, | |
| 1805 | .undefined_type, | |
| 1806 | .fn_noreturn_no_args_type, | |
| 1807 | .fn_void_no_args_type, | |
| 1808 | .fn_naked_noreturn_no_args_type, | |
| 1809 | .fn_ccc_void_no_args_type, | |
| 1810 | .single_const_pointer_to_comptime_int_type, | |
| 1811 | .const_slice_u8_type, | |
| 1812 | .enum_literal_type, | |
| 1813 | .zero, | |
| 1814 | .one, | |
| 1815 | .empty_array, | |
| 1816 | .bool_true, | |
| 1817 | .bool_false, | |
| 1818 | .function, | |
| 1819 | .extern_fn, | |
| 1820 | .variable, | |
| 1821 | .int_u64, | |
| 1822 | .int_i64, | |
| 1823 | .int_big_positive, | |
| 1824 | .int_big_negative, | |
| 1825 | .ref_val, | |
| 1826 | .decl_ref, | |
| 1827 | .elem_ptr, | |
| 1828 | .bytes, | |
| 1829 | .repeated, | |
| 1830 | .float_16, | |
| 1831 | .float_32, | |
| 1832 | .float_64, | |
| 1833 | .float_128, | |
| 1834 | .void_value, | |
| 1835 | .enum_literal, | |
| 1836 | .@"error", | |
| 1837 | .error_union, | |
| 1838 | .empty_struct_value, | |
| 1839 | .abi_align_default, | |
| 1840 | => false, | |
| 1841 | ||
| 1842 | 1181 | .undef => unreachable, |
| 1843 | 1182 | .unreachable_value => unreachable, |
| 1844 | 1183 | .inferred_alloc => unreachable, |
| 1845 | 1184 | .null_value => true, |
| 1185 | ||
| 1186 | else => false, | |
| 1846 | 1187 | }; |
| 1847 | 1188 | } |
| 1848 | 1189 | |
| 1849 | 1190 | /// Valid for all types. Asserts the value is not undefined and not unreachable. |
| 1850 | 1191 | pub fn getError(self: Value) ?[]const u8 { |
| 1851 | 1192 | return switch (self.tag()) { |
| 1852 | .ty, | |
| 1853 | .int_type, | |
| 1854 | .u8_type, | |
| 1855 | .i8_type, | |
| 1856 | .u16_type, | |
| 1857 | .i16_type, | |
| 1858 | .u32_type, | |
| 1859 | .i32_type, | |
| 1860 | .u64_type, | |
| 1861 | .i64_type, | |
| 1862 | .u128_type, | |
| 1863 | .i128_type, | |
| 1864 | .usize_type, | |
| 1865 | .isize_type, | |
| 1866 | .c_short_type, | |
| 1867 | .c_ushort_type, | |
| 1868 | .c_int_type, | |
| 1869 | .c_uint_type, | |
| 1870 | .c_long_type, | |
| 1871 | .c_ulong_type, | |
| 1872 | .c_longlong_type, | |
| 1873 | .c_ulonglong_type, | |
| 1874 | .c_longdouble_type, | |
| 1875 | .f16_type, | |
| 1876 | .f32_type, | |
| 1877 | .f64_type, | |
| 1878 | .f128_type, | |
| 1879 | .c_void_type, | |
| 1880 | .bool_type, | |
| 1881 | .void_type, | |
| 1882 | .type_type, | |
| 1883 | .anyerror_type, | |
| 1884 | .comptime_int_type, | |
| 1885 | .comptime_float_type, | |
| 1886 | .noreturn_type, | |
| 1887 | .null_type, | |
| 1888 | .undefined_type, | |
| 1889 | .fn_noreturn_no_args_type, | |
| 1890 | .fn_void_no_args_type, | |
| 1891 | .fn_naked_noreturn_no_args_type, | |
| 1892 | .fn_ccc_void_no_args_type, | |
| 1893 | .single_const_pointer_to_comptime_int_type, | |
| 1894 | .const_slice_u8_type, | |
| 1895 | .enum_literal_type, | |
| 1896 | .zero, | |
| 1897 | .one, | |
| 1898 | .null_value, | |
| 1899 | .empty_array, | |
| 1900 | .bool_true, | |
| 1901 | .bool_false, | |
| 1902 | .function, | |
| 1903 | .extern_fn, | |
| 1904 | .variable, | |
| 1905 | .int_u64, | |
| 1906 | .int_i64, | |
| 1907 | .int_big_positive, | |
| 1908 | .int_big_negative, | |
| 1909 | .ref_val, | |
| 1910 | .decl_ref, | |
| 1911 | .elem_ptr, | |
| 1912 | .bytes, | |
| 1913 | .repeated, | |
| 1914 | .float_16, | |
| 1915 | .float_32, | |
| 1916 | .float_64, | |
| 1917 | .float_128, | |
| 1918 | .void_value, | |
| 1919 | .enum_literal, | |
| 1920 | .empty_struct_value, | |
| 1921 | .abi_align_default, | |
| 1922 | => null, | |
| 1923 | ||
| 1924 | 1193 | .error_union => { |
| 1925 | 1194 | const data = self.castTag(.error_union).?.data; |
| 1926 | 1195 | return if (data.tag() == .@"error") |
| ... | ... | @@ -1932,6 +1201,8 @@ pub const Value = extern union { |
| 1932 | 1201 | .undef => unreachable, |
| 1933 | 1202 | .unreachable_value => unreachable, |
| 1934 | 1203 | .inferred_alloc => unreachable, |
| 1204 | ||
| 1205 | else => null, | |
| 1935 | 1206 | }; |
| 1936 | 1207 | } |
| 1937 | 1208 | /// Valid for all types. Asserts the value is not undefined. |
| ... | ... | @@ -2021,6 +1292,7 @@ pub const Value = extern union { |
| 2021 | 1292 | .float_128, |
| 2022 | 1293 | .void_value, |
| 2023 | 1294 | .enum_literal, |
| 1295 | .enum_field_index, | |
| 2024 | 1296 | .@"error", |
| 2025 | 1297 | .error_union, |
| 2026 | 1298 | .empty_struct_value, |
| ... | ... | @@ -2038,6 +1310,11 @@ pub const Value = extern union { |
| 2038 | 1310 | pub const Payload = struct { |
| 2039 | 1311 | tag: Tag, |
| 2040 | 1312 | |
| 1313 | pub const U32 = struct { | |
| 1314 | base: Payload, | |
| 1315 | data: u32, | |
| 1316 | }; | |
| 1317 | ||
| 2041 | 1318 | pub const U64 = struct { |
| 2042 | 1319 | base: Payload, |
| 2043 | 1320 | data: u64, |
src/zig_clang.cpp+34| ... | ... | @@ -2060,6 +2060,11 @@ bool ZigClangType_isRecordType(const ZigClangType *self) { |
| 2060 | 2060 | return casted->isRecordType(); |
| 2061 | 2061 | } |
| 2062 | 2062 | |
| 2063 | bool ZigClangType_isVectorType(const ZigClangType *self) { | |
| 2064 | auto casted = reinterpret_cast<const clang::Type *>(self); | |
| 2065 | return casted->isVectorType(); | |
| 2066 | } | |
| 2067 | ||
| 2063 | 2068 | bool ZigClangType_isIncompleteOrZeroLengthArrayType(const ZigClangQualType *self, |
| 2064 | 2069 | const struct ZigClangASTContext *ctx) |
| 2065 | 2070 | { |
| ... | ... | @@ -2752,6 +2757,16 @@ struct ZigClangQualType ZigClangBinaryOperator_getType(const struct ZigClangBina |
| 2752 | 2757 | return bitcast(casted->getType()); |
| 2753 | 2758 | } |
| 2754 | 2759 | |
| 2760 | const struct ZigClangExpr *ZigClangConvertVectorExpr_getSrcExpr(const struct ZigClangConvertVectorExpr *self) { | |
| 2761 | auto casted = reinterpret_cast<const clang::ConvertVectorExpr *>(self); | |
| 2762 | return reinterpret_cast<const struct ZigClangExpr *>(casted->getSrcExpr()); | |
| 2763 | } | |
| 2764 | ||
| 2765 | struct ZigClangQualType ZigClangConvertVectorExpr_getTypeSourceInfo_getType(const struct ZigClangConvertVectorExpr *self) { | |
| 2766 | auto casted = reinterpret_cast<const clang::ConvertVectorExpr *>(self); | |
| 2767 | return bitcast(casted->getTypeSourceInfo()->getType()); | |
| 2768 | } | |
| 2769 | ||
| 2755 | 2770 | struct ZigClangQualType ZigClangDecayedType_getDecayedType(const struct ZigClangDecayedType *self) { |
| 2756 | 2771 | auto casted = reinterpret_cast<const clang::DecayedType *>(self); |
| 2757 | 2772 | return bitcast(casted->getDecayedType()); |
| ... | ... | @@ -2857,6 +2872,16 @@ struct ZigClangQualType ZigClangValueDecl_getType(const struct ZigClangValueDecl |
| 2857 | 2872 | return bitcast(casted->getType()); |
| 2858 | 2873 | } |
| 2859 | 2874 | |
| 2875 | struct ZigClangQualType ZigClangVectorType_getElementType(const struct ZigClangVectorType *self) { | |
| 2876 | auto casted = reinterpret_cast<const clang::VectorType *>(self); | |
| 2877 | return bitcast(casted->getElementType()); | |
| 2878 | } | |
| 2879 | ||
| 2880 | unsigned ZigClangVectorType_getNumElements(const struct ZigClangVectorType *self) { | |
| 2881 | auto casted = reinterpret_cast<const clang::VectorType *>(self); | |
| 2882 | return casted->getNumElements(); | |
| 2883 | } | |
| 2884 | ||
| 2860 | 2885 | const struct ZigClangExpr *ZigClangWhileStmt_getCond(const struct ZigClangWhileStmt *self) { |
| 2861 | 2886 | auto casted = reinterpret_cast<const clang::WhileStmt *>(self); |
| 2862 | 2887 | return reinterpret_cast<const struct ZigClangExpr *>(casted->getCond()); |
| ... | ... | @@ -2936,6 +2961,15 @@ struct ZigClangSourceLocation ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc( |
| 2936 | 2961 | return bitcast(casted->getBeginLoc()); |
| 2937 | 2962 | } |
| 2938 | 2963 | |
| 2964 | unsigned ZigClangShuffleVectorExpr_getNumSubExprs(const ZigClangShuffleVectorExpr *self) { | |
| 2965 | auto casted = reinterpret_cast<const clang::ShuffleVectorExpr *>(self); | |
| 2966 | return casted->getNumSubExprs(); | |
| 2967 | } | |
| 2968 | ||
| 2969 | const struct ZigClangExpr *ZigClangShuffleVectorExpr_getExpr(const struct ZigClangShuffleVectorExpr *self, unsigned idx) { | |
| 2970 | auto casted = reinterpret_cast<const clang::ShuffleVectorExpr *>(self); | |
| 2971 | return reinterpret_cast<const struct ZigClangExpr *>(casted->getExpr(idx)); | |
| 2972 | } | |
| 2939 | 2973 | |
| 2940 | 2974 | enum ZigClangUnaryExprOrTypeTrait_Kind ZigClangUnaryExprOrTypeTraitExpr_getKind( |
| 2941 | 2975 | const struct ZigClangUnaryExprOrTypeTraitExpr *self) |
src/zig_clang.h+10| ... | ... | @@ -1071,6 +1071,7 @@ ZIG_EXTERN_C bool ZigClangType_isBooleanType(const struct ZigClangType *self); |
| 1071 | 1071 | ZIG_EXTERN_C bool ZigClangType_isVoidType(const struct ZigClangType *self); |
| 1072 | 1072 | ZIG_EXTERN_C bool ZigClangType_isArrayType(const struct ZigClangType *self); |
| 1073 | 1073 | ZIG_EXTERN_C bool ZigClangType_isRecordType(const struct ZigClangType *self); |
| 1074 | ZIG_EXTERN_C bool ZigClangType_isVectorType(const struct ZigClangType *self); | |
| 1074 | 1075 | ZIG_EXTERN_C bool ZigClangType_isIncompleteOrZeroLengthArrayType(const ZigClangQualType *self, const struct ZigClangASTContext *ctx); |
| 1075 | 1076 | ZIG_EXTERN_C bool ZigClangType_isConstantArrayType(const ZigClangType *self); |
| 1076 | 1077 | ZIG_EXTERN_C const char *ZigClangType_getTypeClassName(const struct ZigClangType *self); |
| ... | ... | @@ -1206,6 +1207,9 @@ ZIG_EXTERN_C const struct ZigClangExpr *ZigClangBinaryOperator_getLHS(const stru |
| 1206 | 1207 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangBinaryOperator_getRHS(const struct ZigClangBinaryOperator *); |
| 1207 | 1208 | ZIG_EXTERN_C struct ZigClangQualType ZigClangBinaryOperator_getType(const struct ZigClangBinaryOperator *); |
| 1208 | 1209 | |
| 1210 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangConvertVectorExpr_getSrcExpr(const struct ZigClangConvertVectorExpr *); | |
| 1211 | ZIG_EXTERN_C struct ZigClangQualType ZigClangConvertVectorExpr_getTypeSourceInfo_getType(const struct ZigClangConvertVectorExpr *); | |
| 1212 | ||
| 1209 | 1213 | ZIG_EXTERN_C struct ZigClangQualType ZigClangDecayedType_getDecayedType(const struct ZigClangDecayedType *); |
| 1210 | 1214 | |
| 1211 | 1215 | ZIG_EXTERN_C const struct ZigClangCompoundStmt *ZigClangStmtExpr_getSubStmt(const struct ZigClangStmtExpr *); |
| ... | ... | @@ -1235,6 +1239,9 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangUnaryOperator_getBeginLoc(con |
| 1235 | 1239 | |
| 1236 | 1240 | ZIG_EXTERN_C struct ZigClangQualType ZigClangValueDecl_getType(const struct ZigClangValueDecl *); |
| 1237 | 1241 | |
| 1242 | ZIG_EXTERN_C struct ZigClangQualType ZigClangVectorType_getElementType(const struct ZigClangVectorType *); | |
| 1243 | ZIG_EXTERN_C unsigned ZigClangVectorType_getNumElements(const struct ZigClangVectorType *); | |
| 1244 | ||
| 1238 | 1245 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangWhileStmt_getCond(const struct ZigClangWhileStmt *); |
| 1239 | 1246 | ZIG_EXTERN_C const struct ZigClangStmt *ZigClangWhileStmt_getBody(const struct ZigClangWhileStmt *); |
| 1240 | 1247 | |
| ... | ... | @@ -1259,6 +1266,9 @@ ZIG_EXTERN_C struct ZigClangQualType ZigClangUnaryExprOrTypeTraitExpr_getTypeOfA |
| 1259 | 1266 | ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(const struct ZigClangUnaryExprOrTypeTraitExpr *); |
| 1260 | 1267 | ZIG_EXTERN_C enum ZigClangUnaryExprOrTypeTrait_Kind ZigClangUnaryExprOrTypeTraitExpr_getKind(const struct ZigClangUnaryExprOrTypeTraitExpr *); |
| 1261 | 1268 | |
| 1269 | ZIG_EXTERN_C unsigned ZigClangShuffleVectorExpr_getNumSubExprs(const struct ZigClangShuffleVectorExpr *); | |
| 1270 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangShuffleVectorExpr_getExpr(const struct ZigClangShuffleVectorExpr *, unsigned); | |
| 1271 | ||
| 1262 | 1272 | ZIG_EXTERN_C const struct ZigClangStmt *ZigClangDoStmt_getBody(const struct ZigClangDoStmt *); |
| 1263 | 1273 | ZIG_EXTERN_C const struct ZigClangExpr *ZigClangDoStmt_getCond(const struct ZigClangDoStmt *); |
| 1264 | 1274 |
src/zir.zig+118-23| ... | ... | @@ -37,8 +37,6 @@ pub const Code = struct { |
| 37 | 37 | string_bytes: []u8, |
| 38 | 38 | /// The meaning of this data is determined by `Inst.Tag` value. |
| 39 | 39 | extra: []u32, |
| 40 | /// Used for decl_val and decl_ref instructions. | |
| 41 | decls: []*Module.Decl, | |
| 42 | 40 | |
| 43 | 41 | /// Returns the requested data, as well as the new index which is at the start of the |
| 44 | 42 | /// trailers for the object. |
| ... | ... | @@ -78,7 +76,6 @@ pub const Code = struct { |
| 78 | 76 | code.instructions.deinit(gpa); |
| 79 | 77 | gpa.free(code.string_bytes); |
| 80 | 78 | gpa.free(code.extra); |
| 81 | gpa.free(code.decls); | |
| 82 | 79 | code.* = undefined; |
| 83 | 80 | } |
| 84 | 81 | |
| ... | ... | @@ -133,7 +130,7 @@ pub const Inst = struct { |
| 133 | 130 | /// Same as `alloc` except mutable. |
| 134 | 131 | alloc_mut, |
| 135 | 132 | /// Same as `alloc` except the type is inferred. |
| 136 | /// The operand is unused. | |
| 133 | /// Uses the `node` union field. | |
| 137 | 134 | alloc_inferred, |
| 138 | 135 | /// Same as `alloc_inferred` except mutable. |
| 139 | 136 | alloc_inferred_mut, |
| ... | ... | @@ -267,9 +264,6 @@ pub const Inst = struct { |
| 267 | 264 | /// only the taken branch is analyzed. The then block and else block must |
| 268 | 265 | /// terminate with an "inline" variant of a noreturn instruction. |
| 269 | 266 | condbr_inline, |
| 270 | /// A comptime known value. | |
| 271 | /// Uses the `const` union field. | |
| 272 | @"const", | |
| 273 | 267 | /// A struct type definition. Contains references to ZIR instructions for |
| 274 | 268 | /// the field types, defaults, and alignments. |
| 275 | 269 | /// Uses the `pl_node` union field. Payload is `StructDecl`. |
| ... | ... | @@ -286,6 +280,8 @@ pub const Inst = struct { |
| 286 | 280 | /// the field value expressions and optional type tag expression. |
| 287 | 281 | /// Uses the `pl_node` union field. Payload is `EnumDecl`. |
| 288 | 282 | enum_decl, |
| 283 | /// Same as `enum_decl`, except the enum is non-exhaustive. | |
| 284 | enum_decl_nonexhaustive, | |
| 289 | 285 | /// An opaque type definition. Provides an AST node only. |
| 290 | 286 | /// Uses the `node` union field. |
| 291 | 287 | opaque_decl, |
| ... | ... | @@ -332,12 +328,16 @@ pub const Inst = struct { |
| 332 | 328 | error_union_type, |
| 333 | 329 | /// `error.Foo` syntax. Uses the `str_tok` field of the Data union. |
| 334 | 330 | error_value, |
| 331 | /// Implements the `@export` builtin function. | |
| 332 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 333 | @"export", | |
| 335 | 334 | /// Given a pointer to a struct or object that contains virtual fields, returns a pointer |
| 336 | 335 | /// to the named field. The field name is stored in string_bytes. Used by a.b syntax. |
| 337 | 336 | /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. |
| 338 | 337 | field_ptr, |
| 339 | 338 | /// Given a struct or object that contains virtual fields, returns the named field. |
| 340 | 339 | /// The field name is stored in string_bytes. Used by a.b syntax. |
| 340 | /// This instruction also accepts a pointer. | |
| 341 | 341 | /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field. |
| 342 | 342 | field_val, |
| 343 | 343 | /// Given a pointer to a struct or object that contains virtual fields, returns a pointer |
| ... | ... | @@ -363,11 +363,19 @@ pub const Inst = struct { |
| 363 | 363 | fn_type_cc, |
| 364 | 364 | /// Same as `fn_type_cc` but the function is variadic. |
| 365 | 365 | fn_type_cc_var_args, |
| 366 | /// Implements the `@hasDecl` builtin. | |
| 367 | /// Uses the `pl_node` union field. Payload is `Bin`. | |
| 368 | has_decl, | |
| 366 | 369 | /// `@import(operand)`. |
| 367 | 370 | /// Uses the `un_node` field. |
| 368 | 371 | import, |
| 369 | 372 | /// Integer literal that fits in a u64. Uses the int union value. |
| 370 | 373 | int, |
| 374 | /// A float literal that fits in a f32. Uses the float union value. | |
| 375 | float, | |
| 376 | /// A float literal that fits in a f128. Uses the `pl_node` union value. | |
| 377 | /// Payload is `Float128`. | |
| 378 | float128, | |
| 371 | 379 | /// Convert an integer value to another integer type, asserting that the destination type |
| 372 | 380 | /// can hold the same mathematical value. |
| 373 | 381 | /// Uses the `pl_node` field. AST is the `@intCast` syntax. |
| ... | ... | @@ -659,11 +667,28 @@ pub const Inst = struct { |
| 659 | 667 | /// Given a set of `field_ptr` instructions, assumes they are all part of a struct |
| 660 | 668 | /// initialization expression, and emits compile errors for duplicate fields |
| 661 | 669 | /// as well as missing fields, if applicable. |
| 670 | /// This instruction asserts that there is at least one field_ptr instruction, | |
| 671 | /// because it must use one of them to find out the struct type. | |
| 662 | 672 | /// Uses the `pl_node` field. Payload is `Block`. |
| 663 | 673 | validate_struct_init_ptr, |
| 664 | 674 | /// A struct literal with a specified type, with no fields. |
| 665 | 675 | /// Uses the `un_node` field. |
| 666 | 676 | struct_init_empty, |
| 677 | /// Given a struct, union, enum, or opaque and a field name, returns the field type. | |
| 678 | /// Uses the `pl_node` field. Payload is `FieldType`. | |
| 679 | field_type, | |
| 680 | /// Finalizes a typed struct initialization, performs validation, and returns the | |
| 681 | /// struct value. | |
| 682 | /// Uses the `pl_node` field. Payload is `StructInit`. | |
| 683 | struct_init, | |
| 684 | /// Converts an integer into an enum value. | |
| 685 | /// Uses `pl_node` with payload `Bin`. `lhs` is enum type, `rhs` is operand. | |
| 686 | int_to_enum, | |
| 687 | /// Converts an enum value into an integer. Resulting type will be the tag type | |
| 688 | /// of the enum. Uses `un_node`. | |
| 689 | enum_to_int, | |
| 690 | /// Implements the `@typeInfo` builtin. Uses `un_node`. | |
| 691 | type_info, | |
| 667 | 692 | |
| 668 | 693 | /// Returns whether the instruction is one of the control flow "noreturn" types. |
| 669 | 694 | /// Function calls do not count. |
| ... | ... | @@ -709,12 +734,12 @@ pub const Inst = struct { |
| 709 | 734 | .cmp_gt, |
| 710 | 735 | .cmp_neq, |
| 711 | 736 | .coerce_result_ptr, |
| 712 | .@"const", | |
| 713 | 737 | .struct_decl, |
| 714 | 738 | .struct_decl_packed, |
| 715 | 739 | .struct_decl_extern, |
| 716 | 740 | .union_decl, |
| 717 | 741 | .enum_decl, |
| 742 | .enum_decl_nonexhaustive, | |
| 718 | 743 | .opaque_decl, |
| 719 | 744 | .dbg_stmt_node, |
| 720 | 745 | .decl_ref, |
| ... | ... | @@ -727,6 +752,7 @@ pub const Inst = struct { |
| 727 | 752 | .elem_val_node, |
| 728 | 753 | .ensure_result_used, |
| 729 | 754 | .ensure_result_non_error, |
| 755 | .@"export", | |
| 730 | 756 | .floatcast, |
| 731 | 757 | .field_ptr, |
| 732 | 758 | .field_val, |
| ... | ... | @@ -736,7 +762,10 @@ pub const Inst = struct { |
| 736 | 762 | .fn_type_var_args, |
| 737 | 763 | .fn_type_cc, |
| 738 | 764 | .fn_type_cc_var_args, |
| 765 | .has_decl, | |
| 739 | 766 | .int, |
| 767 | .float, | |
| 768 | .float128, | |
| 740 | 769 | .intcast, |
| 741 | 770 | .int_type, |
| 742 | 771 | .is_non_null, |
| ... | ... | @@ -819,6 +848,11 @@ pub const Inst = struct { |
| 819 | 848 | .switch_block_ref_under_multi, |
| 820 | 849 | .validate_struct_init_ptr, |
| 821 | 850 | .struct_init_empty, |
| 851 | .struct_init, | |
| 852 | .field_type, | |
| 853 | .int_to_enum, | |
| 854 | .enum_to_int, | |
| 855 | .type_info, | |
| 822 | 856 | => false, |
| 823 | 857 | |
| 824 | 858 | .@"break", |
| ... | ... | @@ -1181,7 +1215,6 @@ pub const Inst = struct { |
| 1181 | 1215 | } |
| 1182 | 1216 | }, |
| 1183 | 1217 | bin: Bin, |
| 1184 | @"const": *TypedValue, | |
| 1185 | 1218 | /// For strings which may contain null bytes. |
| 1186 | 1219 | str: struct { |
| 1187 | 1220 | /// Offset into `string_bytes`. |
| ... | ... | @@ -1223,6 +1256,16 @@ pub const Inst = struct { |
| 1223 | 1256 | /// Offset from Decl AST node index. |
| 1224 | 1257 | node: i32, |
| 1225 | 1258 | int: u64, |
| 1259 | float: struct { | |
| 1260 | /// Offset from Decl AST node index. | |
| 1261 | /// `Tag` determines which kind of AST node this points to. | |
| 1262 | src_node: i32, | |
| 1263 | number: f32, | |
| 1264 | ||
| 1265 | pub fn src(self: @This()) LazySrcLoc { | |
| 1266 | return .{ .node_offset = self.src_node }; | |
| 1267 | } | |
| 1268 | }, | |
| 1226 | 1269 | array_type_sentinel: struct { |
| 1227 | 1270 | len: Ref, |
| 1228 | 1271 | /// index into extra, points to an `ArrayTypeSentinel` |
| ... | ... | @@ -1504,6 +1547,40 @@ pub const Inst = struct { |
| 1504 | 1547 | tag_type: Ref, |
| 1505 | 1548 | fields_len: u32, |
| 1506 | 1549 | }; |
| 1550 | ||
| 1551 | /// A f128 value, broken up into 4 u32 parts. | |
| 1552 | pub const Float128 = struct { | |
| 1553 | piece0: u32, | |
| 1554 | piece1: u32, | |
| 1555 | piece2: u32, | |
| 1556 | piece3: u32, | |
| 1557 | ||
| 1558 | pub fn get(self: Float128) f128 { | |
| 1559 | const int_bits = @as(u128, self.piece0) | | |
| 1560 | (@as(u128, self.piece1) << 32) | | |
| 1561 | (@as(u128, self.piece2) << 64) | | |
| 1562 | (@as(u128, self.piece3) << 96); | |
| 1563 | return @bitCast(f128, int_bits); | |
| 1564 | } | |
| 1565 | }; | |
| 1566 | ||
| 1567 | /// Trailing is an item per field. | |
| 1568 | pub const StructInit = struct { | |
| 1569 | fields_len: u32, | |
| 1570 | ||
| 1571 | pub const Item = struct { | |
| 1572 | /// The `field_type` ZIR instruction for this field init. | |
| 1573 | field_type: Index, | |
| 1574 | /// The field init expression to be used as the field value. | |
| 1575 | init: Ref, | |
| 1576 | }; | |
| 1577 | }; | |
| 1578 | ||
| 1579 | pub const FieldType = struct { | |
| 1580 | container_type: Ref, | |
| 1581 | /// Offset into `string_bytes`, null terminated. | |
| 1582 | name_start: u32, | |
| 1583 | }; | |
| 1507 | 1584 | }; |
| 1508 | 1585 | |
| 1509 | 1586 | pub const SpecialProng = enum { none, @"else", under }; |
| ... | ... | @@ -1533,12 +1610,11 @@ const Writer = struct { |
| 1533 | 1610 | .intcast, |
| 1534 | 1611 | .store, |
| 1535 | 1612 | .store_to_block_ptr, |
| 1613 | .store_to_inferred_ptr, | |
| 1536 | 1614 | => try self.writeBin(stream, inst), |
| 1537 | 1615 | |
| 1538 | 1616 | .alloc, |
| 1539 | 1617 | .alloc_mut, |
| 1540 | .alloc_inferred, | |
| 1541 | .alloc_inferred_mut, | |
| 1542 | 1618 | .indexable_ptr_len, |
| 1543 | 1619 | .bit_not, |
| 1544 | 1620 | .bool_not, |
| ... | ... | @@ -1578,6 +1654,8 @@ const Writer = struct { |
| 1578 | 1654 | .typeof, |
| 1579 | 1655 | .typeof_elem, |
| 1580 | 1656 | .struct_init_empty, |
| 1657 | .enum_to_int, | |
| 1658 | .type_info, | |
| 1581 | 1659 | => try self.writeUnNode(stream, inst), |
| 1582 | 1660 | |
| 1583 | 1661 | .ref, |
| ... | ... | @@ -1591,11 +1669,12 @@ const Writer = struct { |
| 1591 | 1669 | => try self.writeBoolBr(stream, inst), |
| 1592 | 1670 | |
| 1593 | 1671 | .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst), |
| 1594 | .@"const" => try self.writeConst(stream, inst), | |
| 1595 | 1672 | .param_type => try self.writeParamType(stream, inst), |
| 1596 | 1673 | .ptr_type_simple => try self.writePtrTypeSimple(stream, inst), |
| 1597 | 1674 | .ptr_type => try self.writePtrType(stream, inst), |
| 1598 | 1675 | .int => try self.writeInt(stream, inst), |
| 1676 | .float => try self.writeFloat(stream, inst), | |
| 1677 | .float128 => try self.writeFloat128(stream, inst), | |
| 1599 | 1678 | .str => try self.writeStr(stream, inst), |
| 1600 | 1679 | .elided => try stream.writeAll(")"), |
| 1601 | 1680 | .int_type => try self.writeIntType(stream, inst), |
| ... | ... | @@ -1616,6 +1695,9 @@ const Writer = struct { |
| 1616 | 1695 | .slice_sentinel, |
| 1617 | 1696 | .union_decl, |
| 1618 | 1697 | .enum_decl, |
| 1698 | .enum_decl_nonexhaustive, | |
| 1699 | .struct_init, | |
| 1700 | .field_type, | |
| 1619 | 1701 | => try self.writePlNode(stream, inst), |
| 1620 | 1702 | |
| 1621 | 1703 | .add, |
| ... | ... | @@ -1635,15 +1717,18 @@ const Writer = struct { |
| 1635 | 1717 | .cmp_gt, |
| 1636 | 1718 | .cmp_neq, |
| 1637 | 1719 | .div, |
| 1720 | .has_decl, | |
| 1638 | 1721 | .mod_rem, |
| 1639 | 1722 | .shl, |
| 1640 | 1723 | .shr, |
| 1641 | 1724 | .xor, |
| 1642 | 1725 | .store_node, |
| 1643 | 1726 | .error_union_type, |
| 1727 | .@"export", | |
| 1644 | 1728 | .merge_error_sets, |
| 1645 | 1729 | .bit_and, |
| 1646 | 1730 | .bit_or, |
| 1731 | .int_to_enum, | |
| 1647 | 1732 | => try self.writePlNodeBin(stream, inst), |
| 1648 | 1733 | |
| 1649 | 1734 | .call, |
| ... | ... | @@ -1701,6 +1786,8 @@ const Writer = struct { |
| 1701 | 1786 | .ret_type, |
| 1702 | 1787 | .repeat, |
| 1703 | 1788 | .repeat_inline, |
| 1789 | .alloc_inferred, | |
| 1790 | .alloc_inferred_mut, | |
| 1704 | 1791 | => try self.writeNode(stream, inst), |
| 1705 | 1792 | |
| 1706 | 1793 | .error_value, |
| ... | ... | @@ -1726,7 +1813,6 @@ const Writer = struct { |
| 1726 | 1813 | |
| 1727 | 1814 | .bitcast, |
| 1728 | 1815 | .bitcast_result_ptr, |
| 1729 | .store_to_inferred_ptr, | |
| 1730 | 1816 | => try stream.writeAll("TODO)"), |
| 1731 | 1817 | } |
| 1732 | 1818 | } |
| ... | ... | @@ -1770,15 +1856,6 @@ const Writer = struct { |
| 1770 | 1856 | try stream.writeAll("TODO)"); |
| 1771 | 1857 | } |
| 1772 | 1858 | |
| 1773 | fn writeConst( | |
| 1774 | self: *Writer, | |
| 1775 | stream: anytype, | |
| 1776 | inst: Inst.Index, | |
| 1777 | ) (@TypeOf(stream).Error || error{OutOfMemory})!void { | |
| 1778 | const inst_data = self.code.instructions.items(.data)[inst].@"const"; | |
| 1779 | try stream.writeAll("TODO)"); | |
| 1780 | } | |
| 1781 | ||
| 1782 | 1859 | fn writeParamType( |
| 1783 | 1860 | self: *Writer, |
| 1784 | 1861 | stream: anytype, |
| ... | ... | @@ -1816,6 +1893,23 @@ const Writer = struct { |
| 1816 | 1893 | try stream.print("{d})", .{inst_data}); |
| 1817 | 1894 | } |
| 1818 | 1895 | |
| 1896 | fn writeFloat(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 1897 | const inst_data = self.code.instructions.items(.data)[inst].float; | |
| 1898 | const src = inst_data.src(); | |
| 1899 | try stream.print("{d}) ", .{inst_data.number}); | |
| 1900 | try self.writeSrc(stream, src); | |
| 1901 | } | |
| 1902 | ||
| 1903 | fn writeFloat128(self: *Writer, stream: anytype, inst: Inst.Index) !void { | |
| 1904 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; | |
| 1905 | const extra = self.code.extraData(Inst.Float128, inst_data.payload_index).data; | |
| 1906 | const src = inst_data.src(); | |
| 1907 | const number = extra.get(); | |
| 1908 | // TODO improve std.format to be able to print f128 values | |
| 1909 | try stream.print("{d}) ", .{@floatCast(f64, number)}); | |
| 1910 | try self.writeSrc(stream, src); | |
| 1911 | } | |
| 1912 | ||
| 1819 | 1913 | fn writeStr( |
| 1820 | 1914 | self: *Writer, |
| 1821 | 1915 | stream: anytype, |
| ... | ... | @@ -2133,7 +2227,8 @@ const Writer = struct { |
| 2133 | 2227 | |
| 2134 | 2228 | fn writePlNodeDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void { |
| 2135 | 2229 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 2136 | const decl = self.code.decls[inst_data.payload_index]; | |
| 2230 | const owner_decl = self.scope.ownerDecl().?; | |
| 2231 | const decl = owner_decl.dependencies.entries.items[inst_data.payload_index].key; | |
| 2137 | 2232 | try stream.print("{s}) ", .{decl.name}); |
| 2138 | 2233 | try self.writeSrc(stream, inst_data.src()); |
| 2139 | 2234 | } |
test/run_translated_c.zig+102| ... | ... | @@ -1308,4 +1308,106 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 1308 | 1308 | \\ ufoo = (uval += 100000000); // compile error if @truncate() not inserted |
| 1309 | 1309 | \\} |
| 1310 | 1310 | , ""); |
| 1311 | ||
| 1312 | cases.add("basic vector expressions", | |
| 1313 | \\#include <stdlib.h> | |
| 1314 | \\#include <stdint.h> | |
| 1315 | \\typedef int16_t __v8hi __attribute__((__vector_size__(16))); | |
| 1316 | \\int main(int argc, char**argv) { | |
| 1317 | \\ __v8hi uninitialized; | |
| 1318 | \\ __v8hi empty_init = {}; | |
| 1319 | \\ __v8hi partial_init = {0, 1, 2, 3}; | |
| 1320 | \\ | |
| 1321 | \\ __v8hi a = {0, 1, 2, 3, 4, 5, 6, 7}; | |
| 1322 | \\ __v8hi b = (__v8hi) {100, 200, 300, 400, 500, 600, 700, 800}; | |
| 1323 | \\ | |
| 1324 | \\ __v8hi sum = a + b; | |
| 1325 | \\ for (int i = 0; i < 8; i++) { | |
| 1326 | \\ if (sum[i] != a[i] + b[i]) abort(); | |
| 1327 | \\ } | |
| 1328 | \\ return 0; | |
| 1329 | \\} | |
| 1330 | , ""); | |
| 1331 | ||
| 1332 | cases.add("__builtin_shufflevector", | |
| 1333 | \\#include <stdlib.h> | |
| 1334 | \\#include <stdint.h> | |
| 1335 | \\typedef int16_t __v4hi __attribute__((__vector_size__(8))); | |
| 1336 | \\typedef int16_t __v8hi __attribute__((__vector_size__(16))); | |
| 1337 | \\int main(int argc, char**argv) { | |
| 1338 | \\ __v8hi v8_a = {0, 1, 2, 3, 4, 5, 6, 7}; | |
| 1339 | \\ __v8hi v8_b = {100, 200, 300, 400, 500, 600, 700, 800}; | |
| 1340 | \\ __v8hi shuffled = __builtin_shufflevector(v8_a, v8_b, 0, 1, 2, 3, 8, 9, 10, 11); | |
| 1341 | \\ for (int i = 0; i < 8; i++) { | |
| 1342 | \\ if (i < 4) { | |
| 1343 | \\ if (shuffled[i] != v8_a[i]) abort(); | |
| 1344 | \\ } else { | |
| 1345 | \\ if (shuffled[i] != v8_b[i - 4]) abort(); | |
| 1346 | \\ } | |
| 1347 | \\ } | |
| 1348 | \\ shuffled = __builtin_shufflevector( | |
| 1349 | \\ (__v8hi) {-1, -1, -1, -1, -1, -1, -1, -1}, | |
| 1350 | \\ (__v8hi) {42, 42, 42, 42, 42, 42, 42, 42}, | |
| 1351 | \\ 0, 1, 2, 3, 8, 9, 10, 11 | |
| 1352 | \\ ); | |
| 1353 | \\ for (int i = 0; i < 8; i++) { | |
| 1354 | \\ if (i < 4) { | |
| 1355 | \\ if (shuffled[i] != -1) abort(); | |
| 1356 | \\ } else { | |
| 1357 | \\ if (shuffled[i] != 42) abort(); | |
| 1358 | \\ } | |
| 1359 | \\ } | |
| 1360 | \\ __v4hi shuffled_to_fewer_elements = __builtin_shufflevector(v8_a, v8_b, 0, 1, 8, 9); | |
| 1361 | \\ for (int i = 0; i < 4; i++) { | |
| 1362 | \\ if (i < 2) { | |
| 1363 | \\ if (shuffled_to_fewer_elements[i] != v8_a[i]) abort(); | |
| 1364 | \\ } else { | |
| 1365 | \\ if (shuffled_to_fewer_elements[i] != v8_b[i - 2]) abort(); | |
| 1366 | \\ } | |
| 1367 | \\ } | |
| 1368 | \\ __v4hi v4_a = {0, 1, 2, 3}; | |
| 1369 | \\ __v4hi v4_b = {100, 200, 300, 400}; | |
| 1370 | \\ __v8hi shuffled_to_more_elements = __builtin_shufflevector(v4_a, v4_b, 0, 1, 2, 3, 4, 5, 6, 7); | |
| 1371 | \\ for (int i = 0; i < 4; i++) { | |
| 1372 | \\ if (shuffled_to_more_elements[i] != v4_a[i]) abort(); | |
| 1373 | \\ if (shuffled_to_more_elements[i + 4] != v4_b[i]) abort(); | |
| 1374 | \\ } | |
| 1375 | \\ return 0; | |
| 1376 | \\} | |
| 1377 | , ""); | |
| 1378 | ||
| 1379 | cases.add("__builtin_convertvector", | |
| 1380 | \\#include <stdlib.h> | |
| 1381 | \\#include <stdint.h> | |
| 1382 | \\typedef int16_t __v8hi __attribute__((__vector_size__(16))); | |
| 1383 | \\typedef uint16_t __v8hu __attribute__((__vector_size__(16))); | |
| 1384 | \\int main(int argc, char**argv) { | |
| 1385 | \\ __v8hi signed_vector = { 1, 2, 3, 4, -1, -2, -3,-4}; | |
| 1386 | \\ __v8hu unsigned_vector = __builtin_convertvector(signed_vector, __v8hu); | |
| 1387 | \\ | |
| 1388 | \\ for (int i = 0; i < 8; i++) { | |
| 1389 | \\ if (unsigned_vector[i] != (uint16_t)signed_vector[i]) abort(); | |
| 1390 | \\ } | |
| 1391 | \\ return 0; | |
| 1392 | \\} | |
| 1393 | , ""); | |
| 1394 | ||
| 1395 | cases.add("vector casting", | |
| 1396 | \\#include <stdlib.h> | |
| 1397 | \\#include <stdint.h> | |
| 1398 | \\typedef int8_t __v8qi __attribute__((__vector_size__(8))); | |
| 1399 | \\typedef uint8_t __v8qu __attribute__((__vector_size__(8))); | |
| 1400 | \\int main(int argc, char**argv) { | |
| 1401 | \\ __v8qi signed_vector = { 1, 2, 3, 4, -1, -2, -3,-4}; | |
| 1402 | \\ | |
| 1403 | \\ uint64_t big_int = (uint64_t) signed_vector; | |
| 1404 | \\ if (big_int != 0x01020304FFFEFDFCULL && big_int != 0xFCFDFEFF04030201ULL) abort(); | |
| 1405 | \\ __v8qu unsigned_vector = (__v8qu) big_int; | |
| 1406 | \\ for (int i = 0; i < 8; i++) { | |
| 1407 | \\ if (unsigned_vector[i] != (uint8_t)signed_vector[i] && unsigned_vector[i] != (uint8_t)signed_vector[7 - i]) abort(); | |
| 1408 | \\ } | |
| 1409 | \\ return 0; | |
| 1410 | \\} | |
| 1411 | , ""); | |
| 1412 | ||
| 1311 | 1413 | } |
test/stack_traces.zig+173-409| ... | ... | @@ -3,16 +3,56 @@ const os = std.os; |
| 3 | 3 | const tests = @import("tests.zig"); |
| 4 | 4 | |
| 5 | 5 | pub fn addCases(cases: *tests.StackTracesContext) void { |
| 6 | const source_return = | |
| 7 | \\const std = @import("std"); | |
| 8 | \\ | |
| 6 | cases.addCase(.{ | |
| 7 | .name = "return", | |
| 8 | .source = | |
| 9 | 9 | \\pub fn main() !void { |
| 10 | 10 | \\ return error.TheSkyIsFalling; |
| 11 | 11 | \\} |
| 12 | ; | |
| 13 | const source_try_return = | |
| 14 | \\const std = @import("std"); | |
| 15 | \\ | |
| 12 | , | |
| 13 | .Debug = .{ | |
| 14 | .expect = | |
| 15 | \\error: TheSkyIsFalling | |
| 16 | \\source.zig:2:5: [address] in main (test) | |
| 17 | \\ return error.TheSkyIsFalling; | |
| 18 | \\ ^ | |
| 19 | \\ | |
| 20 | , | |
| 21 | }, | |
| 22 | .ReleaseSafe = .{ | |
| 23 | .exclude = struct { | |
| 24 | pub fn exclude() bool { | |
| 25 | return if (std.builtin.object_format == .elf) true else false; | |
| 26 | } | |
| 27 | }, | |
| 28 | .exclude_os = .{ | |
| 29 | .windows, // segfault | |
| 30 | }, | |
| 31 | .expect = | |
| 32 | \\error: TheSkyIsFalling | |
| 33 | \\source.zig:2:5: [address] in [function] | |
| 34 | \\ return error.TheSkyIsFalling; | |
| 35 | \\ ^ | |
| 36 | \\ | |
| 37 | , | |
| 38 | }, | |
| 39 | .ReleaseFast = .{ | |
| 40 | .expect = | |
| 41 | \\error: TheSkyIsFalling | |
| 42 | \\ | |
| 43 | , | |
| 44 | }, | |
| 45 | .ReleaseSmall = .{ | |
| 46 | .expect = | |
| 47 | \\error: TheSkyIsFalling | |
| 48 | \\ | |
| 49 | , | |
| 50 | }, | |
| 51 | }); | |
| 52 | ||
| 53 | cases.addCase(.{ | |
| 54 | .name = "try return", | |
| 55 | .source = | |
| 16 | 56 | \\fn foo() !void { |
| 17 | 57 | \\ return error.TheSkyIsFalling; |
| 18 | 58 | \\} |
| ... | ... | @@ -20,10 +60,56 @@ pub fn addCases(cases: *tests.StackTracesContext) void { |
| 20 | 60 | \\pub fn main() !void { |
| 21 | 61 | \\ try foo(); |
| 22 | 62 | \\} |
| 23 | ; | |
| 24 | const source_try_try_return_return = | |
| 25 | \\const std = @import("std"); | |
| 26 | \\ | |
| 63 | , | |
| 64 | .Debug = .{ | |
| 65 | .expect = | |
| 66 | \\error: TheSkyIsFalling | |
| 67 | \\source.zig:2:5: [address] in foo (test) | |
| 68 | \\ return error.TheSkyIsFalling; | |
| 69 | \\ ^ | |
| 70 | \\source.zig:6:5: [address] in main (test) | |
| 71 | \\ try foo(); | |
| 72 | \\ ^ | |
| 73 | \\ | |
| 74 | , | |
| 75 | }, | |
| 76 | .ReleaseSafe = .{ | |
| 77 | .exclude = struct { | |
| 78 | pub fn exclude() bool { | |
| 79 | return if (std.builtin.object_format == .elf) true else false; | |
| 80 | } | |
| 81 | }, | |
| 82 | .exclude_os = .{ | |
| 83 | .windows, // segfault | |
| 84 | }, | |
| 85 | .expect = | |
| 86 | \\error: TheSkyIsFalling | |
| 87 | \\source.zig:2:5: [address] in [function] | |
| 88 | \\ return error.TheSkyIsFalling; | |
| 89 | \\ ^ | |
| 90 | \\source.zig:6:5: [address] in [function] | |
| 91 | \\ try foo(); | |
| 92 | \\ ^ | |
| 93 | \\ | |
| 94 | , | |
| 95 | }, | |
| 96 | .ReleaseFast = .{ | |
| 97 | .expect = | |
| 98 | \\error: TheSkyIsFalling | |
| 99 | \\ | |
| 100 | , | |
| 101 | }, | |
| 102 | .ReleaseSmall = .{ | |
| 103 | .expect = | |
| 104 | \\error: TheSkyIsFalling | |
| 105 | \\ | |
| 106 | , | |
| 107 | }, | |
| 108 | }); | |
| 109 | ||
| 110 | cases.addCase(.{ | |
| 111 | .name = "try try return return", | |
| 112 | .source = | |
| 27 | 113 | \\fn foo() !void { |
| 28 | 114 | \\ try bar(); |
| 29 | 115 | \\} |
| ... | ... | @@ -39,9 +125,71 @@ pub fn addCases(cases: *tests.StackTracesContext) void { |
| 39 | 125 | \\pub fn main() !void { |
| 40 | 126 | \\ try foo(); |
| 41 | 127 | \\} |
| 42 | ; | |
| 128 | , | |
| 129 | .Debug = .{ | |
| 130 | .expect = | |
| 131 | \\error: TheSkyIsFalling | |
| 132 | \\source.zig:10:5: [address] in make_error (test) | |
| 133 | \\ return error.TheSkyIsFalling; | |
| 134 | \\ ^ | |
| 135 | \\source.zig:6:5: [address] in bar (test) | |
| 136 | \\ return make_error(); | |
| 137 | \\ ^ | |
| 138 | \\source.zig:2:5: [address] in foo (test) | |
| 139 | \\ try bar(); | |
| 140 | \\ ^ | |
| 141 | \\source.zig:14:5: [address] in main (test) | |
| 142 | \\ try foo(); | |
| 143 | \\ ^ | |
| 144 | \\ | |
| 145 | , | |
| 146 | }, | |
| 147 | .ReleaseSafe = .{ | |
| 148 | .exclude = struct { | |
| 149 | pub fn exclude() bool { | |
| 150 | return if (std.builtin.object_format == .elf) true else false; | |
| 151 | } | |
| 152 | }, | |
| 153 | .exclude_os = .{ | |
| 154 | .windows, // segfault | |
| 155 | }, | |
| 156 | .expect = | |
| 157 | \\error: TheSkyIsFalling | |
| 158 | \\source.zig:10:5: [address] in [function] | |
| 159 | \\ return error.TheSkyIsFalling; | |
| 160 | \\ ^ | |
| 161 | \\source.zig:6:5: [address] in [function] | |
| 162 | \\ return make_error(); | |
| 163 | \\ ^ | |
| 164 | \\source.zig:2:5: [address] in [function] | |
| 165 | \\ try bar(); | |
| 166 | \\ ^ | |
| 167 | \\source.zig:14:5: [address] in [function] | |
| 168 | \\ try foo(); | |
| 169 | \\ ^ | |
| 170 | \\ | |
| 171 | , | |
| 172 | }, | |
| 173 | .ReleaseFast = .{ | |
| 174 | .expect = | |
| 175 | \\error: TheSkyIsFalling | |
| 176 | \\ | |
| 177 | , | |
| 178 | }, | |
| 179 | .ReleaseSmall = .{ | |
| 180 | .expect = | |
| 181 | \\error: TheSkyIsFalling | |
| 182 | \\ | |
| 183 | , | |
| 184 | }, | |
| 185 | }); | |
| 43 | 186 | |
| 44 | const source_dumpCurrentStackTrace = | |
| 187 | cases.addCase(.{ | |
| 188 | .exclude_os = .{ | |
| 189 | .windows, | |
| 190 | }, | |
| 191 | .name = "dumpCurrentStackTrace", | |
| 192 | .source = | |
| 45 | 193 | \\const std = @import("std"); |
| 46 | 194 | \\ |
| 47 | 195 | \\fn bar() void { |
| ... | ... | @@ -54,401 +202,17 @@ pub fn addCases(cases: *tests.StackTracesContext) void { |
| 54 | 202 | \\ foo(); |
| 55 | 203 | \\ return 1; |
| 56 | 204 | \\} |
| 57 | ; | |
| 58 | ||
| 59 | switch (std.Target.current.os.tag) { | |
| 60 | .freebsd => { | |
| 61 | cases.addCase( | |
| 62 | "return", | |
| 63 | source_return, | |
| 64 | [_][]const u8{ | |
| 65 | // debug | |
| 66 | \\error: TheSkyIsFalling | |
| 67 | \\source.zig:4:5: [address] in main (test) | |
| 68 | \\ return error.TheSkyIsFalling; | |
| 69 | \\ ^ | |
| 70 | \\ | |
| 71 | , | |
| 72 | // release-safe | |
| 73 | // https://github.com/ziglang/zig/issues/8421 | |
| 74 | \\ | |
| 75 | , | |
| 76 | // release-fast | |
| 77 | \\error: TheSkyIsFalling | |
| 78 | \\ | |
| 79 | , | |
| 80 | // release-small | |
| 81 | \\error: TheSkyIsFalling | |
| 82 | \\ | |
| 83 | }, | |
| 84 | ); | |
| 85 | cases.addCase( | |
| 86 | "try return", | |
| 87 | source_try_return, | |
| 88 | [_][]const u8{ | |
| 89 | // debug | |
| 90 | \\error: TheSkyIsFalling | |
| 91 | \\source.zig:4:5: [address] in foo (test) | |
| 92 | \\ return error.TheSkyIsFalling; | |
| 93 | \\ ^ | |
| 94 | \\source.zig:8:5: [address] in main (test) | |
| 95 | \\ try foo(); | |
| 96 | \\ ^ | |
| 97 | \\ | |
| 98 | , | |
| 99 | // release-safe | |
| 100 | // https://github.com/ziglang/zig/issues/8421 | |
| 101 | \\ | |
| 102 | , | |
| 103 | // release-fast | |
| 104 | \\error: TheSkyIsFalling | |
| 105 | \\ | |
| 106 | , | |
| 107 | // release-small | |
| 108 | \\error: TheSkyIsFalling | |
| 109 | \\ | |
| 110 | }, | |
| 111 | ); | |
| 112 | cases.addCase( | |
| 113 | "try try return return", | |
| 114 | source_try_try_return_return, | |
| 115 | [_][]const u8{ | |
| 116 | // debug | |
| 117 | \\error: TheSkyIsFalling | |
| 118 | \\source.zig:12:5: [address] in make_error (test) | |
| 119 | \\ return error.TheSkyIsFalling; | |
| 120 | \\ ^ | |
| 121 | \\source.zig:8:5: [address] in bar (test) | |
| 122 | \\ return make_error(); | |
| 123 | \\ ^ | |
| 124 | \\source.zig:4:5: [address] in foo (test) | |
| 125 | \\ try bar(); | |
| 126 | \\ ^ | |
| 127 | \\source.zig:16:5: [address] in main (test) | |
| 128 | \\ try foo(); | |
| 129 | \\ ^ | |
| 130 | \\ | |
| 131 | , | |
| 132 | // release-safe | |
| 133 | // https://github.com/ziglang/zig/issues/8421 | |
| 134 | \\ | |
| 135 | , | |
| 136 | // release-fast | |
| 137 | \\error: TheSkyIsFalling | |
| 138 | \\ | |
| 139 | , | |
| 140 | // release-small | |
| 141 | \\error: TheSkyIsFalling | |
| 142 | \\ | |
| 143 | }, | |
| 144 | ); | |
| 145 | }, | |
| 146 | .linux => { | |
| 147 | cases.addCase( | |
| 148 | "return", | |
| 149 | source_return, | |
| 150 | [_][]const u8{ | |
| 151 | // debug | |
| 152 | \\error: TheSkyIsFalling | |
| 153 | \\source.zig:4:5: [address] in main (test) | |
| 154 | \\ return error.TheSkyIsFalling; | |
| 155 | \\ ^ | |
| 156 | \\ | |
| 157 | , | |
| 158 | // release-safe | |
| 159 | // https://github.com/ziglang/zig/issues/8421 | |
| 160 | \\ | |
| 161 | , | |
| 162 | // release-fast | |
| 163 | \\error: TheSkyIsFalling | |
| 164 | \\ | |
| 165 | , | |
| 166 | // release-small | |
| 167 | \\error: TheSkyIsFalling | |
| 168 | \\ | |
| 169 | }, | |
| 170 | ); | |
| 171 | cases.addCase( | |
| 172 | "try return", | |
| 173 | source_try_return, | |
| 174 | [_][]const u8{ | |
| 175 | // debug | |
| 176 | \\error: TheSkyIsFalling | |
| 177 | \\source.zig:4:5: [address] in foo (test) | |
| 178 | \\ return error.TheSkyIsFalling; | |
| 179 | \\ ^ | |
| 180 | \\source.zig:8:5: [address] in main (test) | |
| 181 | \\ try foo(); | |
| 182 | \\ ^ | |
| 183 | \\ | |
| 184 | , | |
| 185 | // release-safe | |
| 186 | // https://github.com/ziglang/zig/issues/8421 | |
| 187 | \\ | |
| 188 | , | |
| 189 | // release-fast | |
| 190 | \\error: TheSkyIsFalling | |
| 191 | \\ | |
| 192 | , | |
| 193 | // release-small | |
| 194 | \\error: TheSkyIsFalling | |
| 195 | \\ | |
| 196 | }, | |
| 197 | ); | |
| 198 | cases.addCase( | |
| 199 | "try try return return", | |
| 200 | source_try_try_return_return, | |
| 201 | [_][]const u8{ | |
| 202 | // debug | |
| 203 | \\error: TheSkyIsFalling | |
| 204 | \\source.zig:12:5: [address] in make_error (test) | |
| 205 | \\ return error.TheSkyIsFalling; | |
| 206 | \\ ^ | |
| 207 | \\source.zig:8:5: [address] in bar (test) | |
| 208 | \\ return make_error(); | |
| 209 | \\ ^ | |
| 210 | \\source.zig:4:5: [address] in foo (test) | |
| 211 | \\ try bar(); | |
| 212 | \\ ^ | |
| 213 | \\source.zig:16:5: [address] in main (test) | |
| 214 | \\ try foo(); | |
| 215 | \\ ^ | |
| 216 | \\ | |
| 217 | , | |
| 218 | // release-safe | |
| 219 | // https://github.com/ziglang/zig/issues/8421 | |
| 220 | \\ | |
| 221 | , | |
| 222 | // release-fast | |
| 223 | \\error: TheSkyIsFalling | |
| 224 | \\ | |
| 225 | , | |
| 226 | // release-small | |
| 227 | \\error: TheSkyIsFalling | |
| 228 | \\ | |
| 229 | }, | |
| 230 | ); | |
| 231 | cases.addCase( | |
| 232 | "dumpCurrentStackTrace", | |
| 233 | source_dumpCurrentStackTrace, | |
| 234 | [_][]const u8{ | |
| 235 | // debug | |
| 236 | \\source.zig:7:8: [address] in foo (test) | |
| 237 | \\ bar(); | |
| 238 | \\ ^ | |
| 239 | \\source.zig:10:8: [address] in main (test) | |
| 240 | \\ foo(); | |
| 241 | \\ ^ | |
| 242 | \\start.zig:342:29: [address] in std.start.posixCallMainAndExit (test) | |
| 243 | \\ return root.main(); | |
| 244 | \\ ^ | |
| 245 | \\start.zig:163:5: [address] in std.start._start (test) | |
| 246 | \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{}); | |
| 247 | \\ ^ | |
| 248 | \\ | |
| 249 | , | |
| 250 | // release-safe | |
| 251 | // https://github.com/ziglang/zig/issues/8421 | |
| 252 | \\ | |
| 253 | , | |
| 254 | // release-fast | |
| 255 | \\ | |
| 256 | , | |
| 257 | // release-small | |
| 258 | \\ | |
| 259 | }, | |
| 260 | ); | |
| 261 | }, | |
| 262 | .macos => { | |
| 263 | cases.addCase( | |
| 264 | "return", | |
| 265 | source_return, | |
| 266 | [_][]const u8{ | |
| 267 | // debug | |
| 268 | \\error: TheSkyIsFalling | |
| 269 | \\source.zig:4:5: [address] in main (test) | |
| 270 | \\ return error.TheSkyIsFalling; | |
| 271 | \\ ^ | |
| 272 | \\ | |
| 273 | , | |
| 274 | // release-safe | |
| 275 | \\error: TheSkyIsFalling | |
| 276 | \\source.zig:4:5: [address] in std.start.main (test) | |
| 277 | \\ return error.TheSkyIsFalling; | |
| 278 | \\ ^ | |
| 279 | \\ | |
| 280 | , | |
| 281 | // release-fast | |
| 282 | \\error: TheSkyIsFalling | |
| 283 | \\ | |
| 284 | , | |
| 285 | // release-small | |
| 286 | \\error: TheSkyIsFalling | |
| 287 | \\ | |
| 288 | }, | |
| 289 | ); | |
| 290 | cases.addCase( | |
| 291 | "try return", | |
| 292 | source_try_return, | |
| 293 | [_][]const u8{ | |
| 294 | // debug | |
| 295 | \\error: TheSkyIsFalling | |
| 296 | \\source.zig:4:5: [address] in foo (test) | |
| 297 | \\ return error.TheSkyIsFalling; | |
| 298 | \\ ^ | |
| 299 | \\source.zig:8:5: [address] in main (test) | |
| 300 | \\ try foo(); | |
| 301 | \\ ^ | |
| 302 | \\ | |
| 303 | , | |
| 304 | // release-safe | |
| 305 | \\error: TheSkyIsFalling | |
| 306 | \\source.zig:4:5: [address] in std.start.main (test) | |
| 307 | \\ return error.TheSkyIsFalling; | |
| 308 | \\ ^ | |
| 309 | \\source.zig:8:5: [address] in std.start.main (test) | |
| 310 | \\ try foo(); | |
| 311 | \\ ^ | |
| 312 | \\ | |
| 313 | , | |
| 314 | // release-fast | |
| 315 | \\error: TheSkyIsFalling | |
| 316 | \\ | |
| 317 | , | |
| 318 | // release-small | |
| 319 | \\error: TheSkyIsFalling | |
| 320 | \\ | |
| 321 | }, | |
| 322 | ); | |
| 323 | cases.addCase( | |
| 324 | "try try return return", | |
| 325 | source_try_try_return_return, | |
| 326 | [_][]const u8{ | |
| 327 | // debug | |
| 328 | \\error: TheSkyIsFalling | |
| 329 | \\source.zig:12:5: [address] in make_error (test) | |
| 330 | \\ return error.TheSkyIsFalling; | |
| 331 | \\ ^ | |
| 332 | \\source.zig:8:5: [address] in bar (test) | |
| 333 | \\ return make_error(); | |
| 334 | \\ ^ | |
| 335 | \\source.zig:4:5: [address] in foo (test) | |
| 336 | \\ try bar(); | |
| 337 | \\ ^ | |
| 338 | \\source.zig:16:5: [address] in main (test) | |
| 339 | \\ try foo(); | |
| 340 | \\ ^ | |
| 341 | \\ | |
| 342 | , | |
| 343 | // release-safe | |
| 344 | \\error: TheSkyIsFalling | |
| 345 | \\source.zig:12:5: [address] in std.start.main (test) | |
| 346 | \\ return error.TheSkyIsFalling; | |
| 347 | \\ ^ | |
| 348 | \\source.zig:8:5: [address] in std.start.main (test) | |
| 349 | \\ return make_error(); | |
| 350 | \\ ^ | |
| 351 | \\source.zig:4:5: [address] in std.start.main (test) | |
| 352 | \\ try bar(); | |
| 353 | \\ ^ | |
| 354 | \\source.zig:16:5: [address] in std.start.main (test) | |
| 355 | \\ try foo(); | |
| 356 | \\ ^ | |
| 357 | \\ | |
| 358 | , | |
| 359 | // release-fast | |
| 360 | \\error: TheSkyIsFalling | |
| 361 | \\ | |
| 362 | , | |
| 363 | // release-small | |
| 364 | \\error: TheSkyIsFalling | |
| 365 | \\ | |
| 366 | }, | |
| 367 | ); | |
| 368 | }, | |
| 369 | .windows => { | |
| 370 | cases.addCase( | |
| 371 | "return", | |
| 372 | source_return, | |
| 373 | [_][]const u8{ | |
| 374 | // debug | |
| 375 | \\error: TheSkyIsFalling | |
| 376 | \\source.zig:4:5: [address] in main (test.obj) | |
| 377 | \\ return error.TheSkyIsFalling; | |
| 378 | \\ ^ | |
| 379 | \\ | |
| 380 | , | |
| 381 | // release-safe | |
| 382 | // --disabled-- results in segmenetation fault | |
| 383 | "", | |
| 384 | // release-fast | |
| 385 | \\error: TheSkyIsFalling | |
| 386 | \\ | |
| 387 | , | |
| 388 | // release-small | |
| 389 | \\error: TheSkyIsFalling | |
| 390 | \\ | |
| 391 | }, | |
| 392 | ); | |
| 393 | cases.addCase( | |
| 394 | "try return", | |
| 395 | source_try_return, | |
| 396 | [_][]const u8{ | |
| 397 | // debug | |
| 398 | \\error: TheSkyIsFalling | |
| 399 | \\source.zig:4:5: [address] in foo (test.obj) | |
| 400 | \\ return error.TheSkyIsFalling; | |
| 401 | \\ ^ | |
| 402 | \\source.zig:8:5: [address] in main (test.obj) | |
| 403 | \\ try foo(); | |
| 404 | \\ ^ | |
| 405 | \\ | |
| 406 | , | |
| 407 | // release-safe | |
| 408 | // --disabled-- results in segmenetation fault | |
| 409 | "", | |
| 410 | // release-fast | |
| 411 | \\error: TheSkyIsFalling | |
| 412 | \\ | |
| 413 | , | |
| 414 | // release-small | |
| 415 | \\error: TheSkyIsFalling | |
| 416 | \\ | |
| 417 | }, | |
| 418 | ); | |
| 419 | cases.addCase( | |
| 420 | "try try return return", | |
| 421 | source_try_try_return_return, | |
| 422 | [_][]const u8{ | |
| 423 | // debug | |
| 424 | \\error: TheSkyIsFalling | |
| 425 | \\source.zig:12:5: [address] in make_error (test.obj) | |
| 426 | \\ return error.TheSkyIsFalling; | |
| 427 | \\ ^ | |
| 428 | \\source.zig:8:5: [address] in bar (test.obj) | |
| 429 | \\ return make_error(); | |
| 430 | \\ ^ | |
| 431 | \\source.zig:4:5: [address] in foo (test.obj) | |
| 432 | \\ try bar(); | |
| 433 | \\ ^ | |
| 434 | \\source.zig:16:5: [address] in main (test.obj) | |
| 435 | \\ try foo(); | |
| 436 | \\ ^ | |
| 437 | \\ | |
| 438 | , | |
| 439 | // release-safe | |
| 440 | // --disabled-- results in segmenetation fault | |
| 441 | "", | |
| 442 | // release-fast | |
| 443 | \\error: TheSkyIsFalling | |
| 444 | \\ | |
| 445 | , | |
| 446 | // release-small | |
| 447 | \\error: TheSkyIsFalling | |
| 448 | \\ | |
| 449 | }, | |
| 450 | ); | |
| 205 | , | |
| 206 | .Debug = .{ | |
| 207 | .expect = | |
| 208 | \\source.zig:7:8: [address] in foo (test) | |
| 209 | \\ bar(); | |
| 210 | \\ ^ | |
| 211 | \\source.zig:10:8: [address] in main (test) | |
| 212 | \\ foo(); | |
| 213 | \\ ^ | |
| 214 | \\ | |
| 215 | , | |
| 451 | 216 | }, |
| 452 | else => {}, | |
| 453 | } | |
| 217 | }); | |
| 454 | 218 | } |
test/stage2/cbe.zig+315-2| ... | ... | @@ -280,6 +280,15 @@ pub fn addCases(ctx: *TestContext) !void { |
| 280 | 280 | \\} |
| 281 | 281 | , ""); |
| 282 | 282 | |
| 283 | // If expression with breakpoint that does not get hit | |
| 284 | case.addCompareOutput( | |
| 285 | \\export fn main() c_int { | |
| 286 | \\ var x: i32 = 1; | |
| 287 | \\ if (x != 1) @breakpoint(); | |
| 288 | \\ return 0; | |
| 289 | \\} | |
| 290 | , ""); | |
| 291 | ||
| 283 | 292 | // Switch expression |
| 284 | 293 | case.addCompareOutput( |
| 285 | 294 | \\export fn main() c_int { |
| ... | ... | @@ -481,6 +490,310 @@ pub fn addCases(ctx: *TestContext) !void { |
| 481 | 490 | \\} |
| 482 | 491 | , ""); |
| 483 | 492 | } |
| 493 | ||
| 494 | { | |
| 495 | var case = ctx.exeFromCompiledC("structs", .{}); | |
| 496 | case.addError( | |
| 497 | \\const Point = struct { x: i32, y: i32 }; | |
| 498 | \\export fn main() c_int { | |
| 499 | \\ var p: Point = .{ | |
| 500 | \\ .y = 24, | |
| 501 | \\ .x = 12, | |
| 502 | \\ .y = 24, | |
| 503 | \\ }; | |
| 504 | \\ return p.y - p.x - p.x; | |
| 505 | \\} | |
| 506 | , &.{ | |
| 507 | ":6:10: error: duplicate field", | |
| 508 | ":4:10: note: other field here", | |
| 509 | }); | |
| 510 | case.addError( | |
| 511 | \\const Point = struct { x: i32, y: i32 }; | |
| 512 | \\export fn main() c_int { | |
| 513 | \\ var p: Point = .{ | |
| 514 | \\ .y = 24, | |
| 515 | \\ }; | |
| 516 | \\ return p.y - p.x - p.x; | |
| 517 | \\} | |
| 518 | , &.{ | |
| 519 | ":3:21: error: mising struct field: x", | |
| 520 | ":1:15: note: struct 'Point' declared here", | |
| 521 | }); | |
| 522 | case.addError( | |
| 523 | \\const Point = struct { x: i32, y: i32 }; | |
| 524 | \\export fn main() c_int { | |
| 525 | \\ var p: Point = .{ | |
| 526 | \\ .x = 12, | |
| 527 | \\ .y = 24, | |
| 528 | \\ .z = 48, | |
| 529 | \\ }; | |
| 530 | \\ return p.y - p.x - p.x; | |
| 531 | \\} | |
| 532 | , &.{ | |
| 533 | ":6:10: error: no field named 'z' in struct 'Point'", | |
| 534 | ":1:15: note: struct declared here", | |
| 535 | }); | |
| 536 | case.addCompareOutput( | |
| 537 | \\const Point = struct { x: i32, y: i32 }; | |
| 538 | \\export fn main() c_int { | |
| 539 | \\ var p: Point = .{ | |
| 540 | \\ .x = 12, | |
| 541 | \\ .y = 24, | |
| 542 | \\ }; | |
| 543 | \\ return p.y - p.x - p.x; | |
| 544 | \\} | |
| 545 | , ""); | |
| 546 | } | |
| 547 | ||
| 548 | { | |
| 549 | var case = ctx.exeFromCompiledC("enums", .{}); | |
| 550 | ||
| 551 | case.addError( | |
| 552 | \\const E1 = packed enum { a, b, c }; | |
| 553 | \\const E2 = extern enum { a, b, c }; | |
| 554 | \\export fn foo() void { | |
| 555 | \\ const x = E1.a; | |
| 556 | \\} | |
| 557 | \\export fn bar() void { | |
| 558 | \\ const x = E2.a; | |
| 559 | \\} | |
| 560 | , &.{ | |
| 561 | ":1:12: error: enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", | |
| 562 | ":2:12: error: enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", | |
| 563 | }); | |
| 564 | ||
| 565 | // comptime and types are caught in AstGen. | |
| 566 | case.addError( | |
| 567 | \\const E1 = enum { | |
| 568 | \\ a, | |
| 569 | \\ comptime b, | |
| 570 | \\ c, | |
| 571 | \\}; | |
| 572 | \\const E2 = enum { | |
| 573 | \\ a, | |
| 574 | \\ b: i32, | |
| 575 | \\ c, | |
| 576 | \\}; | |
| 577 | \\export fn foo() void { | |
| 578 | \\ const x = E1.a; | |
| 579 | \\} | |
| 580 | \\export fn bar() void { | |
| 581 | \\ const x = E2.a; | |
| 582 | \\} | |
| 583 | , &.{ | |
| 584 | ":3:5: error: enum fields cannot be marked comptime", | |
| 585 | ":8:8: error: enum fields do not have types", | |
| 586 | }); | |
| 587 | ||
| 588 | // @enumToInt, @intToEnum, enum literal coercion, field access syntax, comparison, switch | |
| 589 | case.addCompareOutput( | |
| 590 | \\const Number = enum { One, Two, Three }; | |
| 591 | \\ | |
| 592 | \\export fn main() c_int { | |
| 593 | \\ var number1 = Number.One; | |
| 594 | \\ var number2: Number = .Two; | |
| 595 | \\ const number3 = @intToEnum(Number, 2); | |
| 596 | \\ if (number1 == number2) return 1; | |
| 597 | \\ if (number2 == number3) return 1; | |
| 598 | \\ if (@enumToInt(number1) != 0) return 1; | |
| 599 | \\ if (@enumToInt(number2) != 1) return 1; | |
| 600 | \\ if (@enumToInt(number3) != 2) return 1; | |
| 601 | \\ var x: Number = .Two; | |
| 602 | \\ if (number2 != x) return 1; | |
| 603 | \\ switch (x) { | |
| 604 | \\ .One => return 1, | |
| 605 | \\ .Two => return 0, | |
| 606 | \\ number3 => return 2, | |
| 607 | \\ } | |
| 608 | \\} | |
| 609 | , ""); | |
| 610 | ||
| 611 | // Specifying alignment is a parse error. | |
| 612 | // This also tests going from a successful build to a parse error. | |
| 613 | case.addError( | |
| 614 | \\const E1 = enum { | |
| 615 | \\ a, | |
| 616 | \\ b align(4), | |
| 617 | \\ c, | |
| 618 | \\}; | |
| 619 | \\export fn foo() void { | |
| 620 | \\ const x = E1.a; | |
| 621 | \\} | |
| 622 | , &.{ | |
| 623 | ":3:7: error: expected ',', found 'align'", | |
| 624 | }); | |
| 625 | ||
| 626 | // Redundant non-exhaustive enum mark. | |
| 627 | // This also tests going from a parse error to an AstGen error. | |
| 628 | case.addError( | |
| 629 | \\const E1 = enum { | |
| 630 | \\ a, | |
| 631 | \\ _, | |
| 632 | \\ b, | |
| 633 | \\ c, | |
| 634 | \\ _, | |
| 635 | \\}; | |
| 636 | \\export fn foo() void { | |
| 637 | \\ const x = E1.a; | |
| 638 | \\} | |
| 639 | , &.{ | |
| 640 | ":6:5: error: redundant non-exhaustive enum mark", | |
| 641 | ":3:5: note: other mark here", | |
| 642 | }); | |
| 643 | ||
| 644 | case.addError( | |
| 645 | \\const E1 = enum { | |
| 646 | \\ a, | |
| 647 | \\ b, | |
| 648 | \\ c, | |
| 649 | \\ _ = 10, | |
| 650 | \\}; | |
| 651 | \\export fn foo() void { | |
| 652 | \\ const x = E1.a; | |
| 653 | \\} | |
| 654 | , &.{ | |
| 655 | ":5:9: error: '_' is used to mark an enum as non-exhaustive and cannot be assigned a value", | |
| 656 | }); | |
| 657 | ||
| 658 | case.addError( | |
| 659 | \\const E1 = enum {}; | |
| 660 | \\export fn foo() void { | |
| 661 | \\ const x = E1.a; | |
| 662 | \\} | |
| 663 | , &.{ | |
| 664 | ":1:12: error: enum declarations must have at least one tag", | |
| 665 | }); | |
| 666 | ||
| 667 | case.addError( | |
| 668 | \\const E1 = enum { a, b, _ }; | |
| 669 | \\export fn foo() void { | |
| 670 | \\ const x = E1.a; | |
| 671 | \\} | |
| 672 | , &.{ | |
| 673 | ":1:12: error: non-exhaustive enum missing integer tag type", | |
| 674 | ":1:25: note: marked non-exhaustive here", | |
| 675 | }); | |
| 676 | ||
| 677 | case.addError( | |
| 678 | \\const E1 = enum { a, b, c, b, d }; | |
| 679 | \\export fn foo() void { | |
| 680 | \\ const x = E1.a; | |
| 681 | \\} | |
| 682 | , &.{ | |
| 683 | ":1:28: error: duplicate enum tag", | |
| 684 | ":1:22: note: other tag here", | |
| 685 | }); | |
| 686 | ||
| 687 | case.addError( | |
| 688 | \\export fn foo() void { | |
| 689 | \\ const a = true; | |
| 690 | \\ const b = @enumToInt(a); | |
| 691 | \\} | |
| 692 | , &.{ | |
| 693 | ":3:26: error: expected enum or tagged union, found bool", | |
| 694 | }); | |
| 695 | ||
| 696 | case.addError( | |
| 697 | \\export fn foo() void { | |
| 698 | \\ const a = 1; | |
| 699 | \\ const b = @intToEnum(bool, a); | |
| 700 | \\} | |
| 701 | , &.{ | |
| 702 | ":3:26: error: expected enum, found bool", | |
| 703 | }); | |
| 704 | ||
| 705 | case.addError( | |
| 706 | \\const E = enum { a, b, c }; | |
| 707 | \\export fn foo() void { | |
| 708 | \\ const b = @intToEnum(E, 3); | |
| 709 | \\} | |
| 710 | , &.{ | |
| 711 | ":3:15: error: enum 'E' has no tag with value 3", | |
| 712 | ":1:11: note: enum declared here", | |
| 713 | }); | |
| 714 | ||
| 715 | case.addError( | |
| 716 | \\const E = enum { a, b, c }; | |
| 717 | \\export fn foo() void { | |
| 718 | \\ var x: E = .a; | |
| 719 | \\ switch (x) { | |
| 720 | \\ .a => {}, | |
| 721 | \\ .c => {}, | |
| 722 | \\ } | |
| 723 | \\} | |
| 724 | , &.{ | |
| 725 | ":4:5: error: switch must handle all possibilities", | |
| 726 | ":4:5: note: unhandled enumeration value: 'b'", | |
| 727 | ":1:11: note: enum 'E' declared here", | |
| 728 | }); | |
| 729 | ||
| 730 | case.addError( | |
| 731 | \\const E = enum { a, b, c }; | |
| 732 | \\export fn foo() void { | |
| 733 | \\ var x: E = .a; | |
| 734 | \\ switch (x) { | |
| 735 | \\ .a => {}, | |
| 736 | \\ .b => {}, | |
| 737 | \\ .b => {}, | |
| 738 | \\ .c => {}, | |
| 739 | \\ } | |
| 740 | \\} | |
| 741 | , &.{ | |
| 742 | ":7:10: error: duplicate switch value", | |
| 743 | ":6:10: note: previous value here", | |
| 744 | }); | |
| 745 | ||
| 746 | case.addError( | |
| 747 | \\const E = enum { a, b, c }; | |
| 748 | \\export fn foo() void { | |
| 749 | \\ var x: E = .a; | |
| 750 | \\ switch (x) { | |
| 751 | \\ .a => {}, | |
| 752 | \\ .b => {}, | |
| 753 | \\ .c => {}, | |
| 754 | \\ else => {}, | |
| 755 | \\ } | |
| 756 | \\} | |
| 757 | , &.{ | |
| 758 | ":8:14: error: unreachable else prong; all cases already handled", | |
| 759 | }); | |
| 760 | ||
| 761 | case.addError( | |
| 762 | \\const E = enum { a, b, c }; | |
| 763 | \\export fn foo() void { | |
| 764 | \\ var x: E = .a; | |
| 765 | \\ switch (x) { | |
| 766 | \\ .a => {}, | |
| 767 | \\ .b => {}, | |
| 768 | \\ _ => {}, | |
| 769 | \\ } | |
| 770 | \\} | |
| 771 | , &.{ | |
| 772 | ":4:5: error: '_' prong only allowed when switching on non-exhaustive enums", | |
| 773 | ":7:11: note: '_' prong here", | |
| 774 | }); | |
| 775 | ||
| 776 | case.addError( | |
| 777 | \\const E = enum { a, b, c }; | |
| 778 | \\export fn foo() void { | |
| 779 | \\ var x = E.d; | |
| 780 | \\} | |
| 781 | , &.{ | |
| 782 | ":3:14: error: enum 'E' has no member named 'd'", | |
| 783 | ":1:11: note: enum declared here", | |
| 784 | }); | |
| 785 | ||
| 786 | case.addError( | |
| 787 | \\const E = enum { a, b, c }; | |
| 788 | \\export fn foo() void { | |
| 789 | \\ var x: E = .d; | |
| 790 | \\} | |
| 791 | , &.{ | |
| 792 | ":3:17: error: enum 'E' has no field named 'd'", | |
| 793 | ":1:11: note: enum declared here", | |
| 794 | }); | |
| 795 | } | |
| 796 | ||
| 484 | 797 | ctx.c("empty start function", linux_x64, |
| 485 | 798 | \\export fn _start() noreturn { |
| 486 | 799 | \\ unreachable; |
| ... | ... | @@ -489,8 +802,8 @@ pub fn addCases(ctx: *TestContext) !void { |
| 489 | 802 | \\ZIG_EXTERN_C zig_noreturn void _start(void); |
| 490 | 803 | \\ |
| 491 | 804 | \\zig_noreturn void _start(void) { |
| 492 | \\ zig_breakpoint(); | |
| 493 | \\ zig_unreachable(); | |
| 805 | \\ zig_breakpoint(); | |
| 806 | \\ zig_unreachable(); | |
| 494 | 807 | \\} |
| 495 | 808 | \\ |
| 496 | 809 | ); |
test/stage2/test.zig+78-2| ... | ... | @@ -941,6 +941,32 @@ pub fn addCases(ctx: *TestContext) !void { |
| 941 | 941 | "", |
| 942 | 942 | ); |
| 943 | 943 | |
| 944 | // Array access to a global array. | |
| 945 | case.addCompareOutput( | |
| 946 | \\const hello = "hello".*; | |
| 947 | \\export fn _start() noreturn { | |
| 948 | \\ assert(hello[1] == 'e'); | |
| 949 | \\ | |
| 950 | \\ exit(); | |
| 951 | \\} | |
| 952 | \\ | |
| 953 | \\pub fn assert(ok: bool) void { | |
| 954 | \\ if (!ok) unreachable; // assertion failure | |
| 955 | \\} | |
| 956 | \\ | |
| 957 | \\fn exit() noreturn { | |
| 958 | \\ asm volatile ("syscall" | |
| 959 | \\ : | |
| 960 | \\ : [number] "{rax}" (231), | |
| 961 | \\ [arg1] "{rdi}" (0) | |
| 962 | \\ : "rcx", "r11", "memory" | |
| 963 | \\ ); | |
| 964 | \\ unreachable; | |
| 965 | \\} | |
| 966 | , | |
| 967 | "", | |
| 968 | ); | |
| 969 | ||
| 944 | 970 | // 64bit set stack |
| 945 | 971 | case.addCompareOutput( |
| 946 | 972 | \\export fn _start() noreturn { |
| ... | ... | @@ -1022,7 +1048,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1022 | 1048 | "Hello, World!\n", |
| 1023 | 1049 | ); |
| 1024 | 1050 | try case.files.append(.{ |
| 1025 | .src = | |
| 1051 | .src = | |
| 1026 | 1052 | \\pub fn print() void { |
| 1027 | 1053 | \\ asm volatile ("syscall" |
| 1028 | 1054 | \\ : |
| ... | ... | @@ -1038,11 +1064,61 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1038 | 1064 | .path = "print.zig", |
| 1039 | 1065 | }); |
| 1040 | 1066 | } |
| 1067 | { | |
| 1068 | var case = ctx.exe("import private", linux_x64); | |
| 1069 | case.addError( | |
| 1070 | \\export fn _start() noreturn { | |
| 1071 | \\ @import("print.zig").print(); | |
| 1072 | \\ exit(); | |
| 1073 | \\} | |
| 1074 | \\ | |
| 1075 | \\fn exit() noreturn { | |
| 1076 | \\ asm volatile ("syscall" | |
| 1077 | \\ : | |
| 1078 | \\ : [number] "{rax}" (231), | |
| 1079 | \\ [arg1] "{rdi}" (@as(usize, 0)) | |
| 1080 | \\ : "rcx", "r11", "memory" | |
| 1081 | \\ ); | |
| 1082 | \\ unreachable; | |
| 1083 | \\} | |
| 1084 | , | |
| 1085 | &.{":2:25: error: 'print' is private"}, | |
| 1086 | ); | |
| 1087 | try case.files.append(.{ | |
| 1088 | .src = | |
| 1089 | \\fn print() void { | |
| 1090 | \\ asm volatile ("syscall" | |
| 1091 | \\ : | |
| 1092 | \\ : [number] "{rax}" (@as(usize, 1)), | |
| 1093 | \\ [arg1] "{rdi}" (@as(usize, 1)), | |
| 1094 | \\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")), | |
| 1095 | \\ [arg3] "{rdx}" (@as(usize, 14)) | |
| 1096 | \\ : "rcx", "r11", "memory" | |
| 1097 | \\ ); | |
| 1098 | \\ return; | |
| 1099 | \\} | |
| 1100 | , | |
| 1101 | .path = "print.zig", | |
| 1102 | }); | |
| 1103 | } | |
| 1041 | 1104 | |
| 1042 | 1105 | ctx.compileError("function redefinition", linux_x64, |
| 1106 | \\// dummy comment | |
| 1043 | 1107 | \\fn entry() void {} |
| 1044 | 1108 | \\fn entry() void {} |
| 1045 | , &[_][]const u8{":2:4: error: redefinition of 'entry'"}); | |
| 1109 | , &[_][]const u8{ | |
| 1110 | ":3:4: error: redefinition of 'entry'", | |
| 1111 | ":2:1: note: previous definition here", | |
| 1112 | }); | |
| 1113 | ||
| 1114 | ctx.compileError("global variable redefinition", linux_x64, | |
| 1115 | \\// dummy comment | |
| 1116 | \\var foo = false; | |
| 1117 | \\var foo = true; | |
| 1118 | , &[_][]const u8{ | |
| 1119 | ":3:5: error: redefinition of 'foo'", | |
| 1120 | ":2:1: note: previous definition here", | |
| 1121 | }); | |
| 1046 | 1122 | |
| 1047 | 1123 | ctx.compileError("compileError", linux_x64, |
| 1048 | 1124 | \\export fn _start() noreturn { |
test/stage2/wasm.zig+132| ... | ... | @@ -121,6 +121,138 @@ pub fn addCases(ctx: *TestContext) !void { |
| 121 | 121 | \\ return x + y; |
| 122 | 122 | \\} |
| 123 | 123 | , "35\n"); |
| 124 | ||
| 125 | case.addCompareOutput( | |
| 126 | \\export fn _start() u32 { | |
| 127 | \\ var i: u32 = 20; | |
| 128 | \\ i -= 5; | |
| 129 | \\ return i; | |
| 130 | \\} | |
| 131 | , "15\n"); | |
| 132 | ||
| 133 | case.addCompareOutput( | |
| 134 | \\export fn _start() u32 { | |
| 135 | \\ var i: u32 = 5; | |
| 136 | \\ i -= 3; | |
| 137 | \\ var result: u32 = foo(i, 10); | |
| 138 | \\ return result; | |
| 139 | \\} | |
| 140 | \\fn foo(x: u32, y: u32) u32 { | |
| 141 | \\ return y - x; | |
| 142 | \\} | |
| 143 | , "8\n"); | |
| 144 | ||
| 145 | case.addCompareOutput( | |
| 146 | \\export fn _start() u32 { | |
| 147 | \\ var i: u32 = 5; | |
| 148 | \\ i *= 7; | |
| 149 | \\ var result: u32 = foo(i, 10); | |
| 150 | \\ return result; | |
| 151 | \\} | |
| 152 | \\fn foo(x: u32, y: u32) u32 { | |
| 153 | \\ return x * y; | |
| 154 | \\} | |
| 155 | , "350\n"); | |
| 156 | ||
| 157 | case.addCompareOutput( | |
| 158 | \\export fn _start() u32 { | |
| 159 | \\ var i: u32 = 352; | |
| 160 | \\ i /= 7; // i = 50 | |
| 161 | \\ var result: u32 = foo(i, 7); | |
| 162 | \\ return result; | |
| 163 | \\} | |
| 164 | \\fn foo(x: u32, y: u32) u32 { | |
| 165 | \\ return x / y; | |
| 166 | \\} | |
| 167 | , "7\n"); | |
| 168 | ||
| 169 | case.addCompareOutput( | |
| 170 | \\export fn _start() u32 { | |
| 171 | \\ var i: u32 = 5; | |
| 172 | \\ i &= 6; | |
| 173 | \\ return i; | |
| 174 | \\} | |
| 175 | , "4\n"); | |
| 176 | ||
| 177 | case.addCompareOutput( | |
| 178 | \\export fn _start() u32 { | |
| 179 | \\ var i: u32 = 5; | |
| 180 | \\ i |= 6; | |
| 181 | \\ return i; | |
| 182 | \\} | |
| 183 | , "7\n"); | |
| 184 | ||
| 185 | case.addCompareOutput( | |
| 186 | \\export fn _start() u32 { | |
| 187 | \\ var i: u32 = 5; | |
| 188 | \\ i ^= 6; | |
| 189 | \\ return i; | |
| 190 | \\} | |
| 191 | , "3\n"); | |
| 192 | ||
| 193 | case.addCompareOutput( | |
| 194 | \\export fn _start() bool { | |
| 195 | \\ var b: bool = false; | |
| 196 | \\ b = b or false; | |
| 197 | \\ return b; | |
| 198 | \\} | |
| 199 | , "0\n"); | |
| 200 | ||
| 201 | case.addCompareOutput( | |
| 202 | \\export fn _start() bool { | |
| 203 | \\ var b: bool = true; | |
| 204 | \\ b = b or false; | |
| 205 | \\ return b; | |
| 206 | \\} | |
| 207 | , "1\n"); | |
| 208 | ||
| 209 | case.addCompareOutput( | |
| 210 | \\export fn _start() bool { | |
| 211 | \\ var b: bool = false; | |
| 212 | \\ b = b or true; | |
| 213 | \\ return b; | |
| 214 | \\} | |
| 215 | , "1\n"); | |
| 216 | ||
| 217 | case.addCompareOutput( | |
| 218 | \\export fn _start() bool { | |
| 219 | \\ var b: bool = true; | |
| 220 | \\ b = b or true; | |
| 221 | \\ return b; | |
| 222 | \\} | |
| 223 | , "1\n"); | |
| 224 | ||
| 225 | case.addCompareOutput( | |
| 226 | \\export fn _start() bool { | |
| 227 | \\ var b: bool = false; | |
| 228 | \\ b = b and false; | |
| 229 | \\ return b; | |
| 230 | \\} | |
| 231 | , "0\n"); | |
| 232 | ||
| 233 | case.addCompareOutput( | |
| 234 | \\export fn _start() bool { | |
| 235 | \\ var b: bool = true; | |
| 236 | \\ b = b and false; | |
| 237 | \\ return b; | |
| 238 | \\} | |
| 239 | , "0\n"); | |
| 240 | ||
| 241 | case.addCompareOutput( | |
| 242 | \\export fn _start() bool { | |
| 243 | \\ var b: bool = false; | |
| 244 | \\ b = b and true; | |
| 245 | \\ return b; | |
| 246 | \\} | |
| 247 | , "0\n"); | |
| 248 | ||
| 249 | case.addCompareOutput( | |
| 250 | \\export fn _start() bool { | |
| 251 | \\ var b: bool = true; | |
| 252 | \\ b = b and true; | |
| 253 | \\ return b; | |
| 254 | \\} | |
| 255 | , "1\n"); | |
| 124 | 256 | } |
| 125 | 257 | |
| 126 | 258 | { |
test/tests.zig+98-33| ... | ... | @@ -566,42 +566,87 @@ pub const StackTracesContext = struct { |
| 566 | 566 | |
| 567 | 567 | const Expect = [@typeInfo(Mode).Enum.fields.len][]const u8; |
| 568 | 568 | |
| 569 | pub fn addCase( | |
| 569 | pub fn addCase(self: *StackTracesContext, config: anytype) void { | |
| 570 | if (@hasField(@TypeOf(config), "exclude")) { | |
| 571 | if (config.exclude.exclude()) return; | |
| 572 | } | |
| 573 | if (@hasField(@TypeOf(config), "exclude_arch")) { | |
| 574 | const exclude_arch: []const builtin.Cpu.Arch = &config.exclude_arch; | |
| 575 | for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; | |
| 576 | } | |
| 577 | if (@hasField(@TypeOf(config), "exclude_os")) { | |
| 578 | const exclude_os: []const builtin.Os.Tag = &config.exclude_os; | |
| 579 | for (exclude_os) |os| if (os == builtin.os.tag) return; | |
| 580 | } | |
| 581 | for (self.modes) |mode| { | |
| 582 | switch (mode) { | |
| 583 | .Debug => { | |
| 584 | if (@hasField(@TypeOf(config), "Debug")) { | |
| 585 | self.addExpect(config.name, config.source, mode, config.Debug); | |
| 586 | } | |
| 587 | }, | |
| 588 | .ReleaseSafe => { | |
| 589 | if (@hasField(@TypeOf(config), "ReleaseSafe")) { | |
| 590 | self.addExpect(config.name, config.source, mode, config.ReleaseSafe); | |
| 591 | } | |
| 592 | }, | |
| 593 | .ReleaseFast => { | |
| 594 | if (@hasField(@TypeOf(config), "ReleaseFast")) { | |
| 595 | self.addExpect(config.name, config.source, mode, config.ReleaseFast); | |
| 596 | } | |
| 597 | }, | |
| 598 | .ReleaseSmall => { | |
| 599 | if (@hasField(@TypeOf(config), "ReleaseSmall")) { | |
| 600 | self.addExpect(config.name, config.source, mode, config.ReleaseSmall); | |
| 601 | } | |
| 602 | }, | |
| 603 | } | |
| 604 | } | |
| 605 | } | |
| 606 | ||
| 607 | fn addExpect( | |
| 570 | 608 | self: *StackTracesContext, |
| 571 | 609 | name: []const u8, |
| 572 | 610 | source: []const u8, |
| 573 | expect: Expect, | |
| 611 | mode: Mode, | |
| 612 | mode_config: anytype, | |
| 574 | 613 | ) void { |
| 575 | const b = self.b; | |
| 576 | ||
| 577 | for (self.modes) |mode| { | |
| 578 | const expect_for_mode = expect[@enumToInt(mode)]; | |
| 579 | if (expect_for_mode.len == 0) continue; | |
| 580 | ||
| 581 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ | |
| 582 | "stack-trace", | |
| 583 | name, | |
| 584 | @tagName(mode), | |
| 585 | }) catch unreachable; | |
| 586 | if (self.test_filter) |filter| { | |
| 587 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; | |
| 588 | } | |
| 589 | ||
| 590 | const src_basename = "source.zig"; | |
| 591 | const write_src = b.addWriteFile(src_basename, source); | |
| 592 | const exe = b.addExecutableFromWriteFileStep("test", write_src, src_basename); | |
| 593 | exe.setBuildMode(mode); | |
| 594 | ||
| 595 | const run_and_compare = RunAndCompareStep.create( | |
| 596 | self, | |
| 597 | exe, | |
| 598 | annotated_case_name, | |
| 599 | mode, | |
| 600 | expect_for_mode, | |
| 601 | ); | |
| 614 | if (@hasField(@TypeOf(mode_config), "exclude")) { | |
| 615 | if (mode_config.exclude.exclude()) return; | |
| 616 | } | |
| 617 | if (@hasField(@TypeOf(mode_config), "exclude_arch")) { | |
| 618 | const exclude_arch: []const builtin.Cpu.Arch = &mode_config.exclude_arch; | |
| 619 | for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; | |
| 620 | } | |
| 621 | if (@hasField(@TypeOf(mode_config), "exclude_os")) { | |
| 622 | const exclude_os: []const builtin.Os.Tag = &mode_config.exclude_os; | |
| 623 | for (exclude_os) |os| if (os == builtin.os.tag) return; | |
| 624 | } | |
| 602 | 625 | |
| 603 | self.step.dependOn(&run_and_compare.step); | |
| 626 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ | |
| 627 | "stack-trace", | |
| 628 | name, | |
| 629 | @tagName(mode), | |
| 630 | }) catch unreachable; | |
| 631 | if (self.test_filter) |filter| { | |
| 632 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | |
| 604 | 633 | } |
| 634 | ||
| 635 | const b = self.b; | |
| 636 | const src_basename = "source.zig"; | |
| 637 | const write_src = b.addWriteFile(src_basename, source); | |
| 638 | const exe = b.addExecutableFromWriteFileStep("test", write_src, src_basename); | |
| 639 | exe.setBuildMode(mode); | |
| 640 | ||
| 641 | const run_and_compare = RunAndCompareStep.create( | |
| 642 | self, | |
| 643 | exe, | |
| 644 | annotated_case_name, | |
| 645 | mode, | |
| 646 | mode_config.expect, | |
| 647 | ); | |
| 648 | ||
| 649 | self.step.dependOn(&run_and_compare.step); | |
| 605 | 650 | } |
| 606 | 651 | |
| 607 | 652 | const RunAndCompareStep = struct { |
| ... | ... | @@ -703,6 +748,7 @@ pub const StackTracesContext = struct { |
| 703 | 748 | // process result |
| 704 | 749 | // - keep only basename of source file path |
| 705 | 750 | // - replace address with symbolic string |
| 751 | // - replace function name with symbolic string when mode != .Debug | |
| 706 | 752 | // - skip empty lines |
| 707 | 753 | const got: []const u8 = got_result: { |
| 708 | 754 | var buf = ArrayList(u8).init(b.allocator); |
| ... | ... | @@ -711,26 +757,45 @@ pub const StackTracesContext = struct { |
| 711 | 757 | var it = mem.split(stderr, "\n"); |
| 712 | 758 | process_lines: while (it.next()) |line| { |
| 713 | 759 | if (line.len == 0) continue; |
| 714 | const delims = [_][]const u8{ ":", ":", ":", " in " }; | |
| 715 | var marks = [_]usize{0} ** 4; | |
| 716 | 760 | // offset search past `[drive]:` on windows |
| 717 | 761 | var pos: usize = if (std.Target.current.os.tag == .windows) 2 else 0; |
| 762 | // locate delims/anchor | |
| 763 | const delims = [_][]const u8{ ":", ":", ":", " in ", "(", ")" }; | |
| 764 | var marks = [_]usize{0} ** delims.len; | |
| 718 | 765 | for (delims) |delim, i| { |
| 719 | 766 | marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse { |
| 767 | // unexpected pattern: emit raw line and cont | |
| 720 | 768 | try buf.appendSlice(line); |
| 721 | 769 | try buf.appendSlice("\n"); |
| 722 | 770 | continue :process_lines; |
| 723 | 771 | }; |
| 724 | 772 | pos = marks[i] + delim.len; |
| 725 | 773 | } |
| 774 | // locate source basename | |
| 726 | 775 | pos = mem.lastIndexOfScalar(u8, line[0..marks[0]], fs.path.sep) orelse { |
| 776 | // unexpected pattern: emit raw line and cont | |
| 727 | 777 | try buf.appendSlice(line); |
| 728 | 778 | try buf.appendSlice("\n"); |
| 729 | 779 | continue :process_lines; |
| 730 | 780 | }; |
| 781 | // end processing if source basename changes | |
| 782 | if (!mem.eql(u8, "source.zig", line[pos + 1 .. marks[0]])) break; | |
| 783 | // emit substituted line | |
| 731 | 784 | try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]); |
| 732 | 785 | try buf.appendSlice(" [address]"); |
| 733 | try buf.appendSlice(line[marks[3]..]); | |
| 786 | if (self.mode == .Debug) { | |
| 787 | if (mem.lastIndexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| { | |
| 788 | // On certain platforms (windows) or possibly depending on how we choose to link main | |
| 789 | // the object file extension may be present so we simply strip any extension. | |
| 790 | try buf.appendSlice(line[marks[3] .. marks[4] + idot]); | |
| 791 | try buf.appendSlice(line[marks[5]..]); | |
| 792 | } else { | |
| 793 | try buf.appendSlice(line[marks[3]..]); | |
| 794 | } | |
| 795 | } else { | |
| 796 | try buf.appendSlice(line[marks[3] .. marks[3] + delims[3].len]); | |
| 797 | try buf.appendSlice("[function]"); | |
| 798 | } | |
| 734 | 799 | try buf.appendSlice("\n"); |
| 735 | 800 | } |
| 736 | 801 | break :got_result buf.toOwnedSlice(); |
test/translate_c.zig+8| ... | ... | @@ -2529,6 +2529,14 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 2529 | 2529 | \\} |
| 2530 | 2530 | }); |
| 2531 | 2531 | |
| 2532 | cases.add("macro call with no args", | |
| 2533 | \\#define CALL(arg) bar() | |
| 2534 | , &[_][]const u8{ | |
| 2535 | \\pub fn CALL(arg: anytype) callconv(.Inline) @TypeOf(bar()) { | |
| 2536 | \\ return bar(); | |
| 2537 | \\} | |
| 2538 | }); | |
| 2539 | ||
| 2532 | 2540 | cases.add("logical and, logical or", |
| 2533 | 2541 | \\int max(int a, int b) { |
| 2534 | 2542 | \\ if (a < b || a == b) |